はじめに
この記事は完全に時代遅れです。
filestoreを使用した古いrook/cephはアップグレードせず、HDDを加えてbluestoreを有効にした最新版を新規に導入することをお勧めします。
kubernetes環境にCeph環境を構築しようとして、Rookを試しています。
基本的な導入手順は、下記のドキュメントにありますが、バージョンが新しくなって、ほんの少し変更が入っているのでメモを残しておきます。
Rookのv1.x以降では、各Kubernetesノードに専用のHDD/SSDが搭載されている事を前提としています。テスト用に引き続きdirectories: [path: /var/lib/rook]を指定することはできますが、cluster.yamlではuseAllDevices: trueがデフォルトの設定となっていて、未使用のブロックデバイス(e.g. /dev/sdb)の存在が期待されています。新しいバージョンのRookを利用する際には注意が必要です。可能であればFlex Volumeの使用は避けることをお勧めします。
参考資料
環境
- Kubernetes v1.12.1 (Xeon e3-1220v2, 24GB Memory, Ubuntu 16.04.5) x4台
- Rook tag:v0.8.3 (git clone https://github.com/rook/rook)
ノードはBaremetal(Ubuntu16.04.5)上にkubesprayで構築しています。
実行前のNamespaceの状況は次のとおりです。
$ kubectl get ns
NAME STATUS AGE
default Active 181d
ingress-nginx Active 11d
istio-system Active 13h
kube-public Active 181d
kube-system Active 181d
metallb-system Active 179d
【メモ】転送速度について
近い内に全体を最新のv1.6.xに更新する予定です。
Rook/Ceph v1.6の古いバージョンには致命的な欠陥があります。
必ず最新のv1.7やv1.6.8以降を利用するようにしてください。
その前に2つのCeph filesystem間でデータを転送したので環境は以下のとおりです。
- Kubernetes: v1.16.9 (TX1310m3 x4, Xeon E3-1225v6, 4TB HDD x2 RAID1)
- Kubernetes: v1.19.9 (TX1320m4 x5, Xeon E-2234, 4TB HDD + 500GB SSD)
- Local Network (K8sクラスター内部、両方とも): 10Gbps X520-DA1 + DAC + CRS309-1G-8S+
- Backend Network (K8sクラスター間接続): 1Gbps RJ45ケーブル + Switch
- Rook/Ceph v1.0.6 → v1.5.5
ネットワークは単一の192.168.1.0/24の内部で構成しています。
1ファイル 4KB未満のファイルを中心に構成されているFilesystem上にある約3GB(実転送データは約2.5GB)のデータ(ファイル数 2255364)を、片方のクラスターからもう片方(pod-to-pod)にrsyncで転送した結果は以下のようになりました。
sent 2,491,171,331 bytes received 39,713,035 bytes 136,763.90 bytes/sec
total size is 2,945,556,696 speedup is 1.16
準備
kubectlコマンドが実行できるノードで、rookのリポジトリをgit cloneで取得します。
$ git clone https://github.com/rook/rook
$ cd rook
$ git checkout refs/tags/v0.8.3 -b v0.8.3
Deployment
基本的には公式のドキュメントに従います。
公式ドキュメントは普通にアクセスすると、masterブランチが表示されるので、左下のメニューでv0.8を選択します。
先ほどの手順に続いて、rookディレクトリから、次のコマンドを実行することで、必要なサービスがデプロイされます。
minikubeを利用している場合は、cluster.yamlのdataDirHostPath: /var/lib/rook
を修正する必要があります。
$ cd cluster/examples/kubernetes/ceph
$ kubectl create -f operator.yaml
$ kubectl create -f cluster.yaml
[2018/11/20追記] ここでFLEXVOLUME_DIR_PATHを変更せずにoperator.yamlを実行すると動かない可能性があります。
修正可能ですが、2度手間を避けたい場合には、後半のFLEXVOLUME_DIR_PATHについてのセクションを参照してください。
ここまで進めると、namespaceは次のように変化しました。
$ kubectl get ns
NAME STATUS AGE
...
rook-ceph Active 1m
rook-ceph-system Active 1m
StorageClassでの利用
公式ドキュメントではBlock Storageの項目にあるStorageClassの操作を試してみます。
前の操作に引き続き、 cluster/examples/kubernetes/ceph がカレントディレクトリである事を想定しています。
公式ドキュメントにあるように、storageclass.yaml ファイルを適用し、StorageClassの状態を確認します。
$ kubectl apply -f storageclass.yaml
$ kubectl -n rook-ceph get storageclass
NAME PROVISIONER AGE
rook-ceph-block ceph.rook.io/block 1m
関連する定義を確認するため、-o yaml
をつけて実行しました。$ kubectl -n rook-ceph get storageclass -o yaml
- apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
...
name: rook-ceph-block
namespace: ""
...
namespaceが空なので実際には、どのnamespaceを指定してもstorageclassの定義を参照できることになります。
$ kubectl -n default get storageclass
NAME PROVISIONER AGE
rook-ceph-block ceph.rook.io/block 1h
$ kubectl -n kube-system get storageclass
NAME PROVISIONER AGE
rook-ceph-block ceph.rook.io/block 1h
そのため、システム全体から利用することができます。
StatefulSetを利用した事例について
ROOKの公式ドキュメントに含まれているwordpressのサンプルは、オリジナルがKubernetes公式ドキュメントに掲載されている(Example: Deploying WordPress and MySQL with Persistent Volumes)だと思われます。
これ自体はあまりおもしろくないので、以前、書いた Kubernetes公式のExample: CassandraをPersistentVolumeで構成する を、オリジナルに近い形に戻すことにしました。
このCassandraの例では volumeClaimTemplates を使用していて、StorageClassの定義が含まれています。
Service定義は公式ドキュメントのとおりで問題ありませんが、namespaceとして、cassandraを作成しています。
$ kubectl create ns cassandra
$ alias kubectl='kubectl -n cassandra'
$ kubectl create -f https://k8s.io/examples/application/cassandra/cassandra-service.yaml
StatefulSetは公式ドキュメントにあるcassandra-statefulset.yamlと比較すると次のようになります。
--- cassandra-statefulset.yaml.orig 2018-11-05 17:17:44.057436173 +0900
+++ cassandra-statefulset.yaml 2018-11-05 17:22:58.843780317 +0900
@@ -53,7 +53,7 @@
- name: HEAP_NEWSIZE
value: 100M
- name: CASSANDRA_SEEDS
- value: "cassandra-0.cassandra.default.svc.cluster.local"
+ value: "cassandra-0.cassandra.cassandra.svc.cluster.local"
- name: CASSANDRA_CLUSTER_NAME
value: "K8Demo"
- name: CASSANDRA_DC
@@ -86,15 +86,7 @@
name: cassandra-data
spec:
accessModes: [ "ReadWriteOnce" ]
- storageClassName: fast
+ storageClassName: rook-ceph-block
resources:
requests:
storage: 1Gi
----
-kind: StorageClass
-apiVersion: storage.k8s.io/v1
-metadata:
- name: fast
-provisioner: k8s.io/minikube-hostpath
-parameters:
- type: pd-ssd
namespaceを指定したので、"default"から"cassandra"に変更しています。
この前のところで、aliasでkubectlに"-n cassandra"を追加しているので、コマンドラインではnamespaceを意識することはありません。
また、StorageClassの部分をバッサリ削除して、storageClassNameの指定を先ほど確認したStorageClassの metadata.name の、rook-ceph-block に変更しています。
$ kubectl apply -f cassandra-statefulset.yaml
ここまで実行しても、Podが起動しなくて困ってしまいました。
$ kubectl describe pod cassandra-0
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 24m default-scheduler Successfully assigned cassandra/cassandra-0 to node6
Warning FailedMount 2m (x10 over 22m) kubelet, node6 Unable to mount volumes for pod "cassandra-0_cassandra(0ad97640-e0d4-11e8-8510-000db93312a4)": timeout expired waiting for volumes to attach or mount for pod "cassandra"/"cassandra-0". list of unattached volumes=[cassandra-data default-token-hlb95]
ここで、サンプルのwordpress.yamlなども実行してみましたが、PVもPVCも問題なく作成されているようにみえるものの、Podからマウントできないようにみえます。
Podが稼動しているnode6の/var/log/syslogを確認してみます。
Nov 5 18:57:28 node6 kubelet[1264]: E1105 18:57:28.898651 1264 desired_state_of_world_populator.go:311] Failed to add volume "cassandra-data" (specName: "pvc-15227c15-e0df-11e8-8510-000db93312a4") for pod "1527d22f-e0df-11e8-8510-000db93312a4" to desiredStateOfWorld. err=failed to get Plugin from volumeSpec for volume "pvc-15227c15-e0df-11e8-8510-000db93312a4" err=no volume plugin matched
FLEXVOLUME_DIR_PATH 関連のトラブル
似ている事例としては、次の事例が該当すると思われました。
ここからリンクされている公式ドキュメントによれば、理由として考えられるものは次の2点です。
- FlexVolumeの設定
- 指定したFlexVolumeがkubeletに指定されている
最初のFlexVolumeの設定については、デフォルトの /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ にディレクトリが作成されている事を確認しました。
$ ls -l /usr/libexec/kubernetes/kubelet-plugins/volume/exec/
total 16
drwxr-xr-x 2 root root 4096 Nov 6 00:10 ceph.rook.io~rook
drwxr-xr-x 2 root root 4096 Nov 6 00:10 ceph.rook.io~rook-ceph-system
drwxr-xr-x 2 root root 4096 Nov 6 00:10 rook.io~rook
drwxr-xr-x 2 root root 4096 Nov 6 00:10 rook.io~rook-ceph-system
この状態であれば、デフォルト設定でうまく動くかと思ったのですが、次のkubeletの設定について調べてみると、次のように /var/lib/kubelet/volume-plugins と、違う場所がポイントされています。
$ ps aux|grep volume-plugin-dir
root 1179 11.9 1.7 1848808 142048 ? Ssl Nov05 41:51 /usr/local/bin/kubelet ... --volume-plugin-dir=/var/lib/kubelet/volume-plugins
このため、cluster/examples/kubernetes/ceph の operator.yaml を編集して、FLEXVOLUME_DIR_PATHを設定しています。
$ cd cluster/examples/kubernetes/ceph
ここで、operator.yamlを編集してから、$ git diff operator.yaml
を実行した結果は次のようになりました。
--- a/cluster/examples/kubernetes/ceph/operator.yaml
+++ b/cluster/examples/kubernetes/ceph/operator.yaml
@@ -308,8 +308,8 @@ spec:
# - name: AGENT_TOLERATION_KEY
# value: "<KeyOfTheTaintToTolerate>"
# Set the path where the Rook agent can find the flex volumes
- # - name: FLEXVOLUME_DIR_PATH
- # value: "<PathToFlexVolumes>"
+ - name: FLEXVOLUME_DIR_PATH
+ value: "/var/lib/kubelet/volume-plugins/"
# Rook Discover toleration. Will tolerate all taints with all keys.
# Choose between NoSchedule, PreferNoSchedule and NoExecute:
# - name: DISCOVER_TOLERATION
この operator.yaml を再び適用して、しばらくするとPodがリスタートされていきます。
$ kubectl apply -f operator.yaml
稼動の確認
ここまでの作業で無事にrook-cephが稼動するようになりました。
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/cassandra-0 1/1 Running 0 6m
pod/cassandra-1 1/1 Running 0 4m
pod/cassandra-2 1/1 Running 0 1m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/cassandra ClusterIP None <none> 9042/TCP 7h
NAME DESIRED CURRENT AGE
statefulset.apps/cassandra 3 3 6m
外部へのサービスの公開
ここまできて改めてtype: LoadBalancerなServiceを追加し、外部からcassandraにアクセスできるようにしました。
$ cat 03.cassandra-service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: cassandra
name: cassandracl
spec:
ports:
- port: 9042
selector:
app: cassandra
type: LoadBalancer
$ kubectl apply -f 03.cassandra-service.yaml
service/cassandracl created
ここまでで、外部からアクセス可能なCassandraクラスタが稼動しました。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cassandra ClusterIP None <none> 9042/TCP 24m
cassandracl LoadBalancer 10.233.32.79 192.168.100.156 9042:30653/TCP 23m
突然動かなくなる
この後に電源停止を想定して、ノード全体をリスタートしたところ、うまく動かなくなりました。
状態を確認すると、次のようになっています。
$ kubectl describe pod/cassandra-0
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 5m default-scheduler Successfully assigned cassandra/cassandra-0 to node3
Warning FailedMount 1m (x2 over 3m) kubelet, node3 Unable to mount volumes for pod "cassandra-0_cassandra(243ea4ec-e188-11e8-8a8f-000db93312a4)": timeout expired waiting for volumes to attach or mount for pod "cassandra"/"cassandra-0". list of unmounted volumes=[cassandra-data]. list of unattached volumes=[cassandra-data default-token-kl6r4]
Podのログをみると起動しているのに、cassandra-0のIPがlookupできていません。
当初はkube-dnsを疑っていたのですが、StatefulSetを利用する時には、ClusterIP: NoneなService(svc)定義がないと、cassandra-0のIPが引けなくなる事に気がつきました。
そのためsvcは"cassandra"と"cassandracl"の2つを定義しています。
この結果、kube-dns(10.233.0.3)にクラスター名を問合せると、配下のPODのAレコード(IP)がRoundRobinで返ってきます。
$ nslookup cassandra.cassandra.svc.cluster.local 10.233.0.3
Server: 10.233.0.3
Address: 10.233.0.3#53
Name: cassandra.cassandra.svc.cluster.local
Address: 10.233.100.160
Name: cassandra.cassandra.svc.cluster.local
Address: 10.233.71.56
Name: cassandra.cassandra.svc.cluster.local
Address: 10.233.74.92
当初、ClusterIP: Noneなsvc定義を削除してから、type: LoadBalancerなsvcを再定義していたので、再起動のタイミングで問題が判明したのだと思われます。
次のPVCが作成できない問題
次にStorageClassを利用してrook-cephから作成することができなくなりました。
$ kubectl -n cassandra describe pvc myclaim
Name: myclaim
Namespace: cassandra
StorageClass: rook-ceph-block
Status: Pending
Volume:
Labels: <none>
Annotations: control-plane.alpha.kubernetes.io/leader={"holderIdentity":"dd36afbf-e457-11e8-8335-1a7fdbbc44fe","leaseDurationSeconds":15,"acquireTime":"2018-11-20T05:03:14Z","renewTime":"2018-11-20T05:08:29Z","lea...
kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{},"name":"myclaim","namespace":"cassandra"},"spec":{"accessModes":["ReadWr...
volume.beta.kubernetes.io/storage-provisioner=ceph.rook.io/block
Finalizers: [kubernetes.io/pvc-protection]
Capacity:
Access Modes:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Provisioning 1m (x9 over 5m) ceph.rook.io/block rook-ceph-operator-6cc45dfb48-kpkj6 dd36afbf-e457-11e8-8335-1a7fdbbc44fe External provisioner is provisioning volume for claim "cassandra/myclaim"
Warning ProvisioningFailed 1m (x9 over 5m) ceph.rook.io/block rook-ceph-operator-6cc45dfb48-kpkj6 dd36afbf-e457-11e8-8335-1a7fdbbc44fe Failed to provision volume with StorageClass "rook-ceph-block": Failed to create rook block image replicapool/pvc-958a9ee5-ec81-11e8-849d-000db9331290: failed to create image pvc-958a9ee5-ec81-11e8-849d-000db9331290 in pool replicapool of size 5368709120: Failed to complete '': exit status 2. rbd: error opening pool 'replicapool': (2) No such file or directory
. output:
Normal ExternalProvisioning 16s (x78 over 5m) persistentvolume-controller waiting for a volume to be created, either by external provisioner "ceph.rook.io/block" or manually created by system administrator
toolbox.yamlを適応して、cephコマンドからpoolの状態を確認すると、replicapoolの存在が確認できない事が分かります。
$ kubectl -n rook-ceph exec -it rook-ceph-tools -- ceph osd lspools
$
poolの定義をみると、replicatedとerasureCodedの両方を指定していたので、構成例にもないのでシンプルにreplicatedだけにしました。
$ kubectl -n rook-ceph get pool replicapool -o yaml
apiVersion: ceph.rook.io/v1beta1
kind: Pool
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"ceph.rook.io/v1beta1","kind":"Pool","metadata":{"annotations":{},"name":"replicapool","namespace":"rook-ceph"},"spec":{"erasureCoded":{"codingChunks":1,"dataChunks":2},"replicated":{"size":3}}}
creationTimestamp: 2018-11-20T05:02:46Z
generation: 1
name: replicapool
namespace: rook-ceph
resourceVersion: "32423166"
selfLink: /apis/ceph.rook.io/v1beta1/namespaces/rook-ceph/pools/replicapool
uid: 8539e6e1-ec81-11e8-849d-000db9331290
spec:
erasureCoded:
codingChunks: 1
dataChunks: 2
replicated:
size: 3
poolを削除してから、spec.erasureCodedを削除し、spec.replicatedだけを残したYAMLファイルを再度apply -fで適用して無事に動きました。
これも"apply -f"しただけでは即時に反映されないため、以前にstorageclass.yamlを変更したタイミングから少し遅れてSCを作り直したタイミングで発覚したものと思われます。
【閑話休題】 トラブルシューティング
DashBoard
トラブルシューティングのために、Dashboard ServiceのタイプをClusterIPから、LoadBalancerに変更しました。
$ kubectl -n rook-ceph edit svc rook-ceph-mgr-dashboard
$ kubectl -n rook-ceph get svc rook-ceph-mgr-dashboard
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
rook-ceph-mgr-dashboard LoadBalancer 10.233.49.37 192.168.100.110 7000:32606/TCP 6h
ブラウザからDashboardを確認しましたが、あまりおもしろい結果は得られませんでした。
Dashboardは見栄えはともかく、あまりお勧めできません。
ToolBox
cephコマンドや、rdbコマンドを実行する時には、次のようにします。
あらかじめ toolbox.yaml を kubectl apply -f していることが前提です。
$ kubectl -n rook-ceph exec -it rook-ceph-tools bash
..# ceph osd status
# ceph osd status
+----+-------------------------------------+-------+-------+--------+---------+--------+---------+-----------+
| id | host | used | avail | wr ops | wr data | rd ops | rd data | state |
+----+-------------------------------------+-------+-------+--------+---------+--------+---------+-----------+
| 0 | rook-ceph-osd-id-0-6b577745cd-2dlcf | 35.4G | 421G | 0 | 0 | 0 | 0 | exists,up |
| 1 | rook-ceph-osd-id-1-56c8cb7449-wpmk9 | 21.0G | 206G | 0 | 0 | 0 | 0 | exists,up |
| 2 | rook-ceph-osd-id-2-b8c5d98d5-cmkwh | 20.8G | 206G | 0 | 0 | 1 | 16 | exists,up |
| 3 | rook-ceph-osd-id-3-894fc4d55-7ttr6 | 18.0G | 89.9G | 0 | 0 | 0 | 0 | exists,up |
| 4 | rook-ceph-osd-id-4-86d4557d64-85vvs | 17.5G | 90.4G | 0 | 0 | 1 | 90 | exists,up |
| 5 | rook-ceph-osd-id-5-cbfdb6c8b-ws966 | 16.7G | 37.9G | 0 | 0 | 0 | 0 | exists,up |
| 6 | rook-ceph-osd-id-6-ff948fbbd-dzvfd | 21.0G | 92.2G | 0 | 0 | 0 | 0 | exists,up |
+----+-------------------------------------+-------+-------+--------+---------+--------+---------+-----------+
[root@rook-ceph-tools /]# ceph df
GLOBAL:
SIZE AVAIL RAW USED %RAW USED
1295G 1145G 150G 11.63
POOLS:
NAME ID USED %USED MAX AVAIL OBJECTS
replicapool 1 134M 0.02 834G 48
myfs-metadata 2 15962 0 834G 21
myfs-data0 3 98 0 834G 2
toolbox.yamlを利用して、"ceph osd status"をalias設定しておくなどすると便利だと思われます。
alias show_ceph_osd_status="kubectl -n rook-ceph exec -it rook-ceph-tools -- ceph osd status"
以上