ここは今からストレージです。

Rookの概要とRook-Ceph

f:id:ututaq:20191125022735p:plain

この記事はRookだらけの Advent Calendar 2019 1日目の記事です。

うつぼです。Advent Calendar用にRookの記事を書きます。赤帽エンジニアブログに(勝手に)連載中のストレージオーケストレーター Rookよりも、ちょっとおとなしい感じで書こうと思います。

とは言え初日はRookの概要的なことをまとめたりRook-Cephをやってみます。赤帽エンジニアブログで書いた内容と思いっきりカブりますが、これがないと2日目以降がやりにくくなると思うので…

Rookとは何か

Rookとは、Kubernetesで使うストレージオーケストレーターです。
RookによってCephやEdgeFSなどのSDSや、CockroachDBやYugabyteDBといった分散データベースを、Kubernetesクラスタ上で簡単に展開して分散ストレージクラスタを作ることができます。 Rookは対応するソフトウェアごとにOperatorが用意されます。OperatorによってDay1およびDay 2のオペレーションが(全部ではないが)自動化されることで、Kubernetesのストレージ周りの運用を楽にしてくれます。

Rookは2018年にCNCFの15番目のプロジェクトとして承認され、現在IncubatingなOSSです。日に日に開発が行われている、現在アツいストレージ関連のソフトウェアの一つと言えるでしょう。

Rookに対応するソフトウェア

Rookに対応するソフトウェアは、12/1の時点で7つです。

Name API Group Status
Ceph ceph.rook.io/v1 V1(Stable)
EdgeFS edgefs.rook.io/v1 V1(Stable)
Cassandra cassandra.rook.io/v1alpha1 Alpha
CockroachDB cockroachdb.rook.io/v1alpha1 Alpha
Minio minio.rook.io/v1alpha1 Alpha
NFS nfs.rook.io/v1alpha1 Alpha
YugabyteDB yugabytedb.rook.io/v1alpha1 Alpha

https://rook.io/docs/rook/v1.1/quickstart-toc.html

kubernetesクラスター上でストレージを展開する意義

ストレージやDBと言えば止まったら大事故に繋がるシステムなので、直感的には、コンテナで構成するのはどうなの?と感じるかもしれません。しかし大きなメリットがあるのです。

Kubernetesの神がかったリソース管理の仕組みを使える

Kubernetesクラスター上でストレージが展開されるということで、これらは全てPodで稼働します。
例えば仮にストレージソフトウェアのPodが落ちたとしても、自動で配置可能なノードにPodを再スケジュールしてストレージクラスタに再加入させて、自動修復させることができます。ストレージクラスタの拡張/縮小はkubernetes scaleをベースに行えるし、ソフトウェアアップグレードもコンテナイメージを順次新しい物に置き換えてPodを上げればいいです。データの冗長性や整合性はストレージソフトウェアの機能によって担保します。

インフラ管理者の宿命と諦めていた従来のストレージ運用作業はかなり楽になります。

Interoperabilityという名の鎖から解き放たれる

Kubernetesのバージョンさえ満たしていればどんな環境でも展開できます。オンプレだろうがクラウドだろうが関係ありません。OSやハイパーバイザーのバージョンだとかI/Oアダプターの型番だとかドライバーのバージョンだとか、従来のストレージでネックになっていた事をほとんど考慮しなくていいという実践的な嬉しさもあります。

アップグレードやストレージ更改のたびに戦ってきたInteroeprabilityとほとんど戦わなくてすみます。

Rook-Cephをやってみる

Twitterでjapan_rookとか名乗ってるわりに、うつぼはRook-Cephしか芸がありません。恥ずかしいですが、ここでもRook-Cephをやってみます。
普通にやるだけではさすがに芸がないので、Cephクラスターが展開される様子を追っかけてみましょう。

Kubernetesクラスターを用意します

master x 3, worker(kubernetes.io/role=node) x 3です。workerにはそれぞれ20GiBのデバイスを1つずつ搭載しています。このデバイスをCephで使います。

[utubo@tutsunom ceph]$ kubectl get node --sort-by=".metadata.creationTimestamp" 
NAME                             STATUS   ROLES    AGE     VERSION
ip-172-20-104-194.ec2.internal   Ready    master   4m20s   v1.15.5
ip-172-20-36-239.ec2.internal    Ready    master   4m4s    v1.15.5
ip-172-20-79-199.ec2.internal    Ready    master   3m32s   v1.15.5
ip-172-20-32-157.ec2.internal    Ready    node     103s    v1.15.5
ip-172-20-82-111.ec2.internal    Ready    node     101s    v1.15.5
ip-172-20-103-26.ec2.internal    Ready    node     99s     v1.15.5

Quickstartの手順にしたがってみます

Ceph Storage Quickstartkuberctl create3発が一番簡単にCephをデプロイする方法です。

[utubo@tutsunom ~]$ git clone -b release-1.1 https://github.com/rook/rook.git
[utubo@tutsunom ~]$ cd rook/cluster/examples/kubernetes/ceph
kubectl create -f common.yaml

最初にnamespaceを作って、CRDやRBAC, ServiceAccountなどのリソースを作ってくれます。

[utubo@tutsunom ceph]$ kubectl create -f common.yaml
namespace/rook-ceph created
customresourcedefinition.apiextensions.k8s.io/cephclusters.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/cephfilesystems.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/cephnfses.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/cephobjectstores.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/cephobjectstoreusers.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/cephblockpools.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/volumes.rook.io created
customresourcedefinition.apiextensions.k8s.io/objectbuckets.objectbucket.io created
customresourcedefinition.apiextensions.k8s.io/objectbucketclaims.objectbucket.io created
clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-object-bucket created
clusterrole.rbac.authorization.k8s.io/rook-ceph-cluster-mgmt created
clusterrole.rbac.authorization.k8s.io/rook-ceph-cluster-mgmt-rules created
role.rbac.authorization.k8s.io/rook-ceph-system created
clusterrole.rbac.authorization.k8s.io/rook-ceph-global created
clusterrole.rbac.authorization.k8s.io/rook-ceph-global-rules created
clusterrole.rbac.authorization.k8s.io/rook-ceph-mgr-cluster created
clusterrole.rbac.authorization.k8s.io/rook-ceph-mgr-cluster-rules created
clusterrole.rbac.authorization.k8s.io/rook-ceph-object-bucket created
serviceaccount/rook-ceph-system created
rolebinding.rbac.authorization.k8s.io/rook-ceph-system created
clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-global created
serviceaccount/rook-ceph-osd created
serviceaccount/rook-ceph-mgr created
serviceaccount/rook-ceph-cmd-reporter created
role.rbac.authorization.k8s.io/rook-ceph-osd created
clusterrole.rbac.authorization.k8s.io/rook-ceph-osd created
clusterrole.rbac.authorization.k8s.io/rook-ceph-mgr-system created
clusterrole.rbac.authorization.k8s.io/rook-ceph-mgr-system-rules created
role.rbac.authorization.k8s.io/rook-ceph-mgr created
role.rbac.authorization.k8s.io/rook-ceph-cmd-reporter created
rolebinding.rbac.authorization.k8s.io/rook-ceph-cluster-mgmt created
rolebinding.rbac.authorization.k8s.io/rook-ceph-osd created
rolebinding.rbac.authorization.k8s.io/rook-ceph-mgr created
rolebinding.rbac.authorization.k8s.io/rook-ceph-mgr-system created
clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-mgr-cluster created
clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-osd created
rolebinding.rbac.authorization.k8s.io/rook-ceph-cmd-reporter created
podsecuritypolicy.policy/rook-privileged created
clusterrole.rbac.authorization.k8s.io/psp:rook created
clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-system-psp created
rolebinding.rbac.authorization.k8s.io/rook-ceph-default-psp created
rolebinding.rbac.authorization.k8s.io/rook-ceph-osd-psp created
rolebinding.rbac.authorization.k8s.io/rook-ceph-mgr-psp created
rolebinding.rbac.authorization.k8s.io/rook-ceph-cmd-reporter-psp created
serviceaccount/rook-csi-cephfs-plugin-sa created
serviceaccount/rook-csi-cephfs-provisioner-sa created
role.rbac.authorization.k8s.io/cephfs-external-provisioner-cfg created
rolebinding.rbac.authorization.k8s.io/cephfs-csi-provisioner-role-cfg created
clusterrole.rbac.authorization.k8s.io/cephfs-csi-nodeplugin created
clusterrole.rbac.authorization.k8s.io/cephfs-csi-nodeplugin-rules created
clusterrole.rbac.authorization.k8s.io/cephfs-external-provisioner-runner created
clusterrole.rbac.authorization.k8s.io/cephfs-external-provisioner-runner-rules created
clusterrolebinding.rbac.authorization.k8s.io/rook-csi-cephfs-plugin-sa-psp created
clusterrolebinding.rbac.authorization.k8s.io/rook-csi-cephfs-provisioner-sa-psp created
clusterrolebinding.rbac.authorization.k8s.io/cephfs-csi-nodeplugin created
clusterrolebinding.rbac.authorization.k8s.io/cephfs-csi-provisioner-role created
serviceaccount/rook-csi-rbd-plugin-sa created
serviceaccount/rook-csi-rbd-provisioner-sa created
role.rbac.authorization.k8s.io/rbd-external-provisioner-cfg created
rolebinding.rbac.authorization.k8s.io/rbd-csi-provisioner-role-cfg created
clusterrole.rbac.authorization.k8s.io/rbd-csi-nodeplugin created
clusterrole.rbac.authorization.k8s.io/rbd-csi-nodeplugin-rules created
clusterrole.rbac.authorization.k8s.io/rbd-external-provisioner-runner created
clusterrole.rbac.authorization.k8s.io/rbd-external-provisioner-runner-rules created
clusterrolebinding.rbac.authorization.k8s.io/rook-csi-rbd-plugin-sa-psp created
clusterrolebinding.rbac.authorization.k8s.io/rook-csi-rbd-provisioner-sa-psp created
clusterrolebinding.rbac.authorization.k8s.io/rbd-csi-nodeplugin created
clusterrolebinding.rbac.authorization.k8s.io/rbd-csi-provisioner-role created
kubectl create -f operator.yaml

rook-ceph-operatorを作ってくれます。別窓でwatch kubectl -n rook-ceph get podしていると、operatorの次にrook-discover agentが生まれるのが見えます。rook-discoverはdaemonsetなので各worker上で動きます。

[utubo@tutsunom ceph]$ kubectl create -f operator.yaml 
deployment.apps/rook-ceph-operator created

別窓

[utubo@tutsunom ceph]$ watch kubectl -n rook-ceph get pod
 
NAME                                 READY   STATUS              RESTARTS   AGE
rook-ceph-operator-fb8b96548-rxzn6   0/1     ContainerCreating   0          11s
↓
NAME                                 READY   STATUS              RESTARTS   AGE
rook-ceph-operator-fb8b96548-rxzn6   1/1     Running             0          33s
rook-discover-5qqs8                  1/1     Running             0          9s
rook-discover-8jn4w                  0/1     ContainerCreating   0          9s
rook-discover-msfjm                  0/1     ContainerCreating   0          9s
↓
NAME                                 READY   STATUS    RESTARTS   AGE
rook-ceph-operator-fb8b96548-rxzn6   1/1     Running   0          52s
rook-discover-5qqs8                  1/1     Running   0          28s
rook-discover-8jn4w                  1/1     Running   0          28s
rook-discover-msfjm                  1/1     Running   0          28s
kubectl create -f cluster.yaml

ここが見どころです。operatorによって次々とCephのコンポーネントが生成される様子は圧巻。ぜひここでも別窓でwatch kubectl -n rook-ceph get podしておいて下さい。
※出力が長くなるので生まれるPodだけ書きます。

[utubo@tutsunom ceph]$ kubectl create -f cluster.yaml 
cephcluster.ceph.rook.io/rook-ceph created

別窓

[utubo@tutsunom ceph]$ watch kubectl -n rook-ceph get pod
1. Ceph-CSIのpluginを生成
NAME                                           READY   STATUS              RESTARTS   AGE
csi-cephfsplugin-79hrg                         3/3     Running             0          8s
csi-cephfsplugin-pnrg9                         3/3     Running             0          8s
csi-cephfsplugin-provisioner-974b566d9-kk95l   4/4     Running             0          8s
csi-cephfsplugin-provisioner-974b566d9-r8p5d   0/4     ContainerCreating   0          8s
csi-cephfsplugin-zwm4m                         0/3     ContainerCreating   0          8s
csi-rbdplugin-4mhvb                            3/3     Running             0          8s
csi-rbdplugin-5gwtb                            3/3     Running             0          8s
csi-rbdplugin-j7kx9                            0/3     ContainerCreating   0          8s
csi-rbdplugin-provisioner-579c546f5-s7j7q      0/5     ContainerCreating   0          8s
csi-rbdplugin-provisioner-579c546f5-z84jx      0/5     ContainerCreating   0          8s
rook-ceph-detect-version-vfxrt                 0/1     Init:0/1            0          2s
---略---

↓

2. rook-ceph-mon-x-canary --> rook-ceph-mon-x の順でCeph MONを生成
NAME                                           READY   STATUS        RESTARTS   AGE
---略---
rook-ceph-mon-a-canary-7469b766b6-4r9wn        0/1     Terminating   0          11s
rook-ceph-mon-b-canary-795fb574cc-ldmwr        1/1     Running       0          10s
rook-ceph-mon-c-canary-9c4dbbd5b-6ff8b         1/1     Running       0          10s
---略---
↓
NAME                                           READY   STATUS     RESTARTS   AGE
---略---
rook-ceph-mon-a-9b889cd9f-dvmr2                1/1     Running    0          24s
rook-ceph-mon-b-758455b88f-cw6pz               1/1     Running    0          15s
rook-ceph-mon-c-698785577b-plhb2               0/1     Init:0/2   0          1s
---略---

↓

3. Ceph MGRを生成
NAME                                           READY   STATUS    RESTARTS   AGE
---略---
rook-ceph-mgr-a-546669c64f-pvr4d               1/1     Running   0          16s
---略---

↓

4. rook-ceph-osd-prepare --> rook-ceph-osd-x の順でCeph OSDを生成
NAME                                                        READY   STATUS            RESTARTS   AGE
---略---
rook-ceph-osd-prepare-ip-172-20-103-26.ec2.internal-hmpl5   0/1     PodInitializing   0          3s
rook-ceph-osd-prepare-ip-172-20-32-157.ec2.internal-9jsgh   0/1     PodInitializing   0          3s
rook-ceph-osd-prepare-ip-172-20-82-111.ec2.internal-6d4b9   0/1     PodInitializing   0          3s
---略---
↓
NAME                                                        READY   STATUS      RESTARTS   AGE
---略---
rook-ceph-osd-0-757bf68778-p9qx8                            1/1     Running     0          8s
rook-ceph-osd-1-6d4b48bb84-r7mdz                            1/1     Running     0          7s
rook-ceph-osd-2-5bcf8c96dc-snxx2                            1/1     Running     0          7s
rook-ceph-osd-prepare-ip-172-20-103-26.ec2.internal-hmpl5   0/1     Completed   0          39s
rook-ceph-osd-prepare-ip-172-20-32-157.ec2.internal-9jsgh   0/1     Completed   0          39s
rook-ceph-osd-prepare-ip-172-20-82-111.ec2.internal-6d4b9   0/1     Completed   0          39s
---略---

以上で終わりです。最後はこんな感じになります。

[utubo@tutsunom ceph]$ kubectl -n rook-ceph get pod
NAME                                                        READY   STATUS      RESTARTS   AGE
csi-cephfsplugin-79hrg                                      3/3     Running     0          2m11s
csi-cephfsplugin-pnrg9                                      3/3     Running     0          2m11s
csi-cephfsplugin-provisioner-974b566d9-kk95l                4/4     Running     0          2m11s
csi-cephfsplugin-provisioner-974b566d9-r8p5d                4/4     Running     0          2m11s
csi-cephfsplugin-zwm4m                                      3/3     Running     0          2m11s
csi-rbdplugin-4mhvb                                         3/3     Running     0          2m11s
csi-rbdplugin-5gwtb                                         3/3     Running     0          2m11s
csi-rbdplugin-j7kx9                                         3/3     Running     0          2m11s
csi-rbdplugin-provisioner-579c546f5-s7j7q                   5/5     Running     0          2m11s
csi-rbdplugin-provisioner-579c546f5-z84jx                   5/5     Running     0          2m11s
rook-ceph-mgr-a-546669c64f-pvr4d                            1/1     Running     0          63s
rook-ceph-mon-a-9b889cd9f-dvmr2                             1/1     Running     0          100s
rook-ceph-mon-b-758455b88f-cw6pz                            1/1     Running     0          91s
rook-ceph-mon-c-698785577b-plhb2                            1/1     Running     0          77s
rook-ceph-operator-fb8b96548-rxzn6                          1/1     Running     0          4m6s
rook-ceph-osd-0-757bf68778-p9qx8                            1/1     Running     0          8s
rook-ceph-osd-1-6d4b48bb84-r7mdz                            1/1     Running     0          7s
rook-ceph-osd-2-5bcf8c96dc-snxx2                            1/1     Running     0          7s
rook-ceph-osd-prepare-ip-172-20-103-26.ec2.internal-hmpl5   0/1     Completed   0          39s
rook-ceph-osd-prepare-ip-172-20-32-157.ec2.internal-9jsgh   0/1     Completed   0          39s
rook-ceph-osd-prepare-ip-172-20-82-111.ec2.internal-6d4b9   0/1     Completed   0          39s
rook-discover-5qqs8                                         1/1     Running     0          3m42s
rook-discover-8jn4w                                         1/1     Running     0          3m42s
rook-discover-msfjm                                         1/1     Running     0          3m42s

ちゃんとCephとして稼働してることも確認できます。

[utubo@tutsunom ceph]$ kubectl create -f toolbox.yaml 
deployment.apps/rook-ceph-tools created
[utubo@tutsunom ceph]$ kubectl exec -it `kubectl get pod -l app=rook-ceph-tools -o 'jsonpath={.items[].metadata.name}'` \
> ceph status
  cluster:
    id:     a12e3584-b4b0-4939-98ba-1a086579d233
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum a,b,c (age 3m)
    mgr: a(active, since 3m)
    osd: 3 osds: 3 up (since 2m), 3 in (since 2m)
 
  data:
    pools:   0 pools, 0 pgs
    objects: 0 objects, 0 B
    usage:   3.0 GiB used, 54 GiB / 57 GiB avail
    pgs:     

コマンド3発だけで、CSI pluginとMON, MGR, OSDが次々と生まれてCephクラスタができました。こんなに簡単にクラスタを作れるのは衝撃的です。
しかもAGEのカラムに注目すると、rook-ceph-operatorを作ってからわずか4分です。Rookのおかげで4分でストレージが作れます。これはもう革命だ!

まとめ

Rookは対応するSDSやDBのoperatorでストレージのオーケストレーションを行うツールです。
ストレージの構築から運用まで、強力に手助けしてくれる革命的なツールです。
後半はRookでCephクラスタを作るところを追っかけてみましたが、実際に試してみるとよりRookの威力を実感すると思います。ぜひ試してみて下さいね。