Rook-Ceph OSD on PVC(前半)
この記事はRookだらけの Advent Calendar 2019 6日目の記事です。
Rook 1.1でできるようになった OSD on PVC の説明をします。
OSD on PVC ???
OSD on PVCというのは、ceph-osdをPVC から作ろうということです。
CephはブロックデバイスからOSDを作成するため、言うまでもなくceph-osdにはブロックデバイスが必要です。
osdのブロックデバイスはrook-ceph-osd Podが稼働するworker nodeから取ってくるので、事前に何らかの方法でworker nodeにブロックデバイスをattachしておかなくてはなりません。
しかしこのworker nodeにブロックデバイスをattachする作業は、Kubernetesが乗るプラットフォーム側の操作です。Kubernetesで何かしらやりようはあると思いますが、基本的にはKubernetes外の管理操作だと思います。
せっかくRookでKubernetes内で完結できるストレージシステムが作れるというのに、根本にある保存媒体はKubernetes外でmanageされる。それってなんかイケてないですよね。
そういうわけで、
「既存のPV/PVCの仕組みでceph-osdのPodにブロックデバイスを直でattachしようや、そしたら何もないところから自動でOSDが作れるやろ」
を実現するのがOSD on PVCです。
この考えの元にあるのがKubernetes 1.11からでてきたRaw Block Volumeのfeatureです。(1.14でbetaながらデフォルトでenableになっているfeature)
この技術を使ってPVをブロックデバイスとして見せることができるようになりました。
https://kubernetes.io/docs/concepts/storage/volumes/#csi-raw-block-volume-support
これってまるでCloud(Container) Native Storageのためにあるようなfeature。ええやん、ステキやん。
実際にやってみる
OSDはベースのPVC-baseのブロックデバイスにするとし、MONの構成ファイルをどうするかという選択肢もあります。
これまでMONの構成ファイルはworker nodeの/var/lib/rook
などyamlで記載されたディレクトリに置かれていたのですが、これもPVC-baseのファイルPVに置くというパターンが選べます。せっかくだからこっちでやってみましょう。
前回までと同じように3master+3workerのクラスタです。
[utubo@tutsunom ceph]$ kubectl get node --sort-by=".metadata.creationTimestamp" --show-labels NAME STATUS ROLES AGE VERSION LABELS ip-172-20-91-64.ec2.internal Ready master 25h v1.15.5 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.medium,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1b,kops.k8s.io/instancegroup=master-us-east-1b,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-74-29.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=master,node-role.kubernetes.io/master= ip-172-20-53-29.ec2.internal Ready master 25h v1.15.5 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.medium,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1a,kops.k8s.io/instancegroup=master-us-east-1a,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-63-169.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=master,node-role.kubernetes.io/master= ip-172-20-113-40.ec2.internal Ready master 25h v1.15.5 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.medium,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1c,kops.k8s.io/instancegroup=master-us-east-1c,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-121-240.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=master,node-role.kubernetes.io/master= ip-172-20-93-28.ec2.internal Ready node 25h v1.15.5 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.large,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1a,kops.k8s.io/instancegroup=nodes,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-52-182.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=node,node-role.kubernetes.io/node= ip-172-20-42-193.ec2.internal Ready node 25h v1.15.5 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.large,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1c,kops.k8s.io/instancegroup=nodes,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-118-92.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=node,node-role.kubernetes.io/node= ip-172-20-102-19.ec2.internal Ready node 25h v1.15.5 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=t2.large,beta.kubernetes.io/os=linux,failure-domain.beta.kubernetes.io/region=us-east-1,failure-domain.beta.kubernetes.io/zone=us-east-1b,kops.k8s.io/instancegroup=nodes,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-172-20-74-57.ec2.internal,kubernetes.io/os=linux,kubernetes.io/role=node,node-role.kubernetes.io/node=
まず、ベースの元になるPV/PVCのStorageClassをチェックしましょう。
ベースのStorageClassがvolumeBindingMode: WaitForFirstConsumer
で定義されていることが大事です。volumeBindingMode: Immediate
だと動きません。ダメです。ここちょっとハマりました。
[utubo@tutsunom ceph]$ cat my-sc-gp2.yaml kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: my-gp2 annotations: storageclass.kubernetes.io/is-default-class: "true" provisioner: kubernetes.io/aws-ebs volumeBindingMode: WaitForFirstConsumer [utubo@tutsunom ceph]$ kubectl create -f my-sc-gp2.yaml
それではgithubにあるサンプルのcluster-on-pvc.yaml
から、StorageClassと(gp2 --> my-gp2)とosdの数(count:3 --> 9)だけ変更して、Cephクラスタを作ってみます。(長いのでコメント行は省略)
yamlの中のmon:
とstorage:
(=osd)にあるvolumeClaimTemplate
がPVC-baseの証。
[utubo@tutsunom ceph]$ cat my-cluster-on-pvc.yaml apiVersion: ceph.rook.io/v1 kind: CephCluster metadata: name: rook-ceph namespace: rook-ceph spec: dataDirHostPath: /var/lib/rook mon: count: 3 allowMultiplePerNode: false volumeClaimTemplate: spec: storageClassName: my-gp2 resources: requests: storage: 10Gi cephVersion: image: ceph/ceph:v14.2.4-20190917 allowUnsupported: false dashboard: enabled: true ssl: true network: hostNetwork: false storage: topologyAware: true storageClassDeviceSets: - name: set1 count: 9 portable: true placement: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - rook-ceph-osd - key: app operator: In values: - rook-ceph-osd-prepare topologyKey: kubernetes.io/hostname resources: volumeClaimTemplates: - metadata: name: data spec: resources: requests: storage: 10Gi storageClassName: my-gp2 volumeMode: Block accessModes: - ReadWriteOnce disruptionManagement: managePodBudgets: false osdMaintenanceTimeout: 30 manageMachineDisruptionBudgets: false machineDisruptionBudgetNamespace: openshift-machine-api [utubo@tutsunom ceph]$ [utubo@tutsunom ceph]$ kubectl create -f common.yaml [utubo@tutsunom ceph]$ kubectl create -f operator.yaml [utubo@tutsunom ceph]$ kubectl create -f my-cluster-on-pvc.yaml [utubo@tutsunom ceph]$ [utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod NAME READY STATUS RESTARTS AGE csi-cephfsplugin-jbs7g 3/3 Running 0 4m53s csi-cephfsplugin-lvtg9 3/3 Running 0 4m53s csi-cephfsplugin-provisioner-974b566d9-b24zs 4/4 Running 0 4m53s csi-cephfsplugin-provisioner-974b566d9-wgx4l 4/4 Running 0 4m53s csi-cephfsplugin-rpg6v 3/3 Running 0 4m53s csi-rbdplugin-4pvjd 3/3 Running 0 4m53s csi-rbdplugin-hw59q 3/3 Running 0 4m53s csi-rbdplugin-mrq2g 3/3 Running 0 4m53s csi-rbdplugin-provisioner-579c546f5-k4x78 5/5 Running 0 4m53s csi-rbdplugin-provisioner-579c546f5-tlb8s 5/5 Running 0 4m53s rook-ceph-mgr-a-8656964c64-d6rxd 1/1 Running 0 2m50s rook-ceph-mon-a-5964fd74f7-fpqhl 1/1 Running 0 4m3s rook-ceph-mon-b-858958c794-mwfzz 1/1 Running 0 3m36s rook-ceph-mon-c-576b4d7687-94ts9 1/1 Running 0 3m14s rook-ceph-operator-fb8b96548-f5dl5 1/1 Running 0 5m1s rook-ceph-osd-0-66db6b5bbc-6v82x 1/1 Running 0 59s rook-ceph-osd-1-7b76f55b96-bqknl 1/1 Running 0 49s rook-ceph-osd-2-57f696464b-jlzjr 1/1 Running 0 74s rook-ceph-osd-3-7b975f57c7-6snsr 1/1 Running 0 78s rook-ceph-osd-4-5d485c8997-8fq7r 1/1 Running 0 75s rook-ceph-osd-5-6f4f87db77-b52z6 1/1 Running 0 43s rook-ceph-osd-6-6999896cdf-7d4mv 1/1 Running 0 26s rook-ceph-osd-7-c66d49fcb-5lrwr 1/1 Running 0 29s rook-ceph-osd-8-6bdf79c57c-prnzq 1/1 Running 0 28s rook-ceph-osd-prepare-set1-0-data-bhmrj-rjbnk 0/1 Completed 0 2m22s rook-ceph-osd-prepare-set1-1-data-dcr5r-gcdl8 0/1 Completed 0 2m21s rook-ceph-osd-prepare-set1-2-data-klbr2-bnlqv 0/1 Completed 0 2m21s rook-ceph-osd-prepare-set1-3-data-7b2pk-7n6wl 0/1 Completed 0 2m21s rook-ceph-osd-prepare-set1-4-data-249gz-b2z79 0/1 Completed 0 2m20s rook-ceph-osd-prepare-set1-5-data-znf49-l7xbz 0/1 Completed 0 2m19s rook-ceph-osd-prepare-set1-6-data-57bft-qtnwt 0/1 Completed 0 2m19s rook-ceph-osd-prepare-set1-7-data-sm244-v546c 0/1 Completed 0 2m18s rook-ceph-osd-prepare-set1-8-data-cd5hb-zt2gk 0/1 Completed 0 2m18s rook-discover-5jzcr 1/1 Running 0 5m rook-discover-glvgr 1/1 Running 0 5m rook-discover-jh9pr 1/1 Running 0 5m
おっ、イケるやん。worker nodeにあらかじめブロックデバイスをつけることなくCephクラスタが作れました。
[utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod -l app=rook-ceph-mon -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES rook-ceph-mon-a-5964fd74f7-fpqhl 1/1 Running 0 4m56s 100.96.6.128 ip-172-20-93-28.ec2.internal <none> <none> rook-ceph-mon-b-858958c794-mwfzz 1/1 Running 0 4m29s 100.96.7.198 ip-172-20-42-193.ec2.internal <none> <none> rook-ceph-mon-c-576b4d7687-94ts9 1/1 Running 0 4m7s 100.96.8.117 ip-172-20-102-19.ec2.internal <none> <none>
MONはしっかり3ノードに分かれています。OSDは、
[utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod -l app=rook-ceph-osd -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES rook-ceph-osd-0-66db6b5bbc-6v82x 1/1 Running 0 119s 100.96.6.136 ip-172-20-93-28.ec2.internal <none> <none> rook-ceph-osd-1-7b76f55b96-bqknl 1/1 Running 0 109s 100.96.6.137 ip-172-20-93-28.ec2.internal <none> <none> rook-ceph-osd-2-57f696464b-jlzjr 1/1 Running 0 2m14s 100.96.7.204 ip-172-20-42-193.ec2.internal <none> <none> rook-ceph-osd-3-7b975f57c7-6snsr 1/1 Running 0 2m18s 100.96.7.202 ip-172-20-42-193.ec2.internal <none> <none> rook-ceph-osd-4-5d485c8997-8fq7r 1/1 Running 0 2m15s 100.96.7.203 ip-172-20-42-193.ec2.internal <none> <none> rook-ceph-osd-5-6f4f87db77-b52z6 1/1 Running 0 103s 100.96.6.138 ip-172-20-93-28.ec2.internal <none> <none> rook-ceph-osd-6-6999896cdf-7d4mv 1/1 Running 0 86s 100.96.6.141 ip-172-20-93-28.ec2.internal <none> <none> rook-ceph-osd-7-c66d49fcb-5lrwr 1/1 Running 0 89s 100.96.6.139 ip-172-20-93-28.ec2.internal <none> <none> rook-ceph-osd-8-6bdf79c57c-prnzq 1/1 Running 0 88s 100.96.6.140 ip-172-20-93-28.ec2.internal <none> <none>
ええー!osd全然3ノードにバラけてないやん!!一応Cephで見てみると…
[utubo@tutsunom ceph]$ kubectl create -f toolbox.yaml [utubo@tutsunom ceph]$ kubectl -n rook-ceph exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` ceph osd tree ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT PRI-AFF -1 0.07910 root default -5 0.07910 region us-east-1 -4 0.02637 zone us-east-1a -9 0.00879 host set1-0-data-bhmrj 4 ssd 0.00879 osd.4 up 1.00000 1.00000 -11 0.00879 host set1-2-data-klbr2 2 ssd 0.00879 osd.2 up 1.00000 1.00000 -3 0.00879 host set1-6-data-57bft 3 ssd 0.00879 osd.3 up 1.00000 1.00000 -14 0.05273 zone us-east-1b -13 0.00879 host set1-1-data-dcr5r 0 ssd 0.00879 osd.0 up 1.00000 1.00000 -19 0.00879 host set1-3-data-7b2pk 5 ssd 0.00879 osd.5 up 1.00000 1.00000 -17 0.00879 host set1-4-data-249gz 1 ssd 0.00879 osd.1 up 1.00000 1.00000 -23 0.00879 host set1-5-data-znf49 6 ssd 0.00879 osd.6 up 1.00000 1.00000 -21 0.00879 host set1-7-data-sm244 8 ssd 0.00879 osd.8 up 1.00000 1.00000 -25 0.00879 host set1-8-data-cd5hb 7 ssd 0.00879 osd.7 up 1.00000 1.00000
(´・ω・`)
yamlのosd Affinity ruleを見ると、
placement: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - rook-ceph-osd - key: app operator: In values: - rook-ceph-osd-prepare topologyKey: kubernetes.io/hostname
うーん…確かに3osdより多くなるとうまく散らばらなそうやけど3nodeにすら散らばらんのはどうなっとるんや…
実はコレ想定内の問題なんです。サンプルのcluster-on-pvc.yaml
で使われているpodAntiAffinity ruleではうまく散らばらないのは織り込み済みです。yamlのコメントをよく読んでみると次のことが書かれています。
# Since the OSDs could end up on any node, an effort needs to be made to spread the OSDs # across nodes as much as possible. Unfortunately the pod anti-affinity breaks down # as soon as you have more than one OSD per node. If you have more OSDs than nodes, K8s may # choose to schedule many of them on the same node. What we need is the Pod Topology # Spread Constraints, which is alpha in K8s 1.16. This means that a feature gate must be # enabled for this feature, and Rook also still needs to add support for this feature. # Another approach for a small number of OSDs is to create a separate device set for each # zone (or other set of nodes with a common label) so that the OSDs will end up on different
なるほど。要約すると、
- いまのPod Anti-Affinityだと上手くosdをバラけさせられない。まだ努力が必要やわ。
- Pod Topology Spread Constraints(k8s 1.16 alpha)を使えばピチッとバラけさせられるけど、Rookがまだ対応してないねん。これからの子やねん。
- Node Affinityも使った組み合わせたらイケるかも。それでどないや?
ということです。そうか、この分野ではRookはまだまだこれからの子なんか。それならしゃーない。
そういうわけで、今の時点ではマニュアルで3nodeの散らばるように書く必要がありそうですね。
まとめ
今回はOSD on PVCのコンセプトの紹介をし、実際にやってみて問題に直面しました。
この悲しき現実に立ち向かう方法たるやいかに。次回バジリスク甲賀忍法帖…というノリはやめといて、次回はこの問題を回避する方法を紹介します。
BGMは甲賀忍法帖でした。(やっぱり)