【CNDT2019への道】GWにやってきた！Rook v1.0のCSIを試す

# TL;DR

- 5月頭にリリースされた**Rook v1.0**を試してみた。
- 7月に発表予定のDB on Kubernetesの検証なのでCeph RBDで使ってみる。
- RookではBeta版のCSIでやってみる。
- 手順が煩雑でドキュメントに分かりづらい部分もあるけど動いたよ。

# Rook v1.0、リリース
Rookはv0.8の頃から試しており、過去にPostgreSQL on Rookということで試してきました。
※過去の投稿は[この辺](https://qiita.com/tzkoba/items/0c2d6f92bc18c6549117)にあります。

今回、5/3に公式ブログでRook v1.0のリリースが発表されました。
このタイミングでブランディングも更新されたようで、これまでの城砦ロゴだけではなく**SirSir**（ドルアーガの塔っぽい？？）というマスコットが登場しました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr">Hey everyone I&#39;m proud to introduce the updated <a href="https://twitter.com/rook_io?ref_src=twsrc%5Etfw">@rook_io</a> design and mascot SirSir! For the v1.0 release I refreshed Rook&#39;s entire brand giving the visuals more vibrance and pop. Make sure to a lookout for us at <a href="https://twitter.com/KubeCon_?ref_src=twsrc%5Etfw">@KubeCon_</a> <a href="https://twitter.com/upbound_io?ref_src=twsrc%5Etfw">@upbound_io</a> <a href="https://t.co/8yChpPryZf">pic.twitter.com/8yChpPryZf</a></p>&mdash; Chris (@Chrishdesigns90) <a href="https://twitter.com/Chrishdesigns90/status/1128369765502865409?ref_src=twsrc%5Etfw">2019年5月14日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


ブログにはいろいろと新しいFeatureが紹介されていますが、現在私が行っている検証で気になるのは下記のあたりですね。

- Ceph Nautilusに対応。性能も改善しているらしい。
- CSI対応は*now available for testing*でBeta版。v1.1でStableを目指すらしい。

# RookをCeph CSIで試す
では、Rookを用いて構築したCephクラスタにCSIでアクセスする構成を検証していきます。
今回のシステム構成図は以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54853/2df9697f-2e6e-1e6b-dea6-089044b630d6.png)

コンポーネントは大きく分けると、RookとCeph、そしてCeph CSIの3つから構成されています。

検証時のKubernetesバージョンは**1.13.4**で、Amazon EC2のインスタンス上にクラスタを構築しています。ホストOSはCentOS7の公式イメージで、[こちら](https://rook.io/docs/rook/v1.0/k8s-pre-reqs.html)にあるような事前準備は不要でした。
更にCeph-CSIを利用する際の前提で[こちらのページ](https://rook.io/docs/rook/v1.0/ceph-csi-drivers.html)に以下2点があげられています。これも確認しておきましょう

- Kubernetesのバージョンはv1.13以上が必須。CSI Spec 1.0のサポートが必要なので。
- ```--allow-privileged=true``` をkubeletとAPI-serverに設定する。

##【前提】BlueStoreとFileStore
Rook v1.0を試す際に注意して欲しいのは、git上のyamlで構築するとRawデバイスが必要な構成になります。これはCephのFileStoreではなくBlueStoreを使うようになっているためで、簡単なお試しには少しハードルが高くなります。

BlueStoreについては[こちらの資料](https://www.slideshare.net/sageweil1/bluestore-a-new-faster-storage-backend-for-ceph)などをご覧下さい。

#### FileStoreへの切替はこんな感じで
具体的にBlueStoreでなく、FileStoreで使う場合にはcluster.yamlを以下のように変更します。

```yaml:cluster.yaml
（修正後）
  storage: # cluster level storage configuration and selection
    useAllNodes: true
    # useAllDevicesをtrue -> falseに変更
    useAllDevices: false
    deviceFilter:
    location:
    config:
      # storeTypeでfilestoreを指定
      storeType: filestore
    # FileStoreを配置するディレクトリを以下で指定
    directories:
    - path: /var/lib/rook
```

（yaml修正サンプルは[こちら](https://github.com/tzkoba/postgres-on-k8s/blob/develop/rook-install/filestore-rook-cluster.yaml)）
なお、以降の検証ではBlueStoreを使っています。

## 【手順１．】Ceph Clusterの作成
v0.9からファイル構成が変更になっています。過去バージョンではoperator.yamlとcluster.yamlの2本がメインでしたが、common.yamlが追加され、それぞれには以下の内容が記述されています。

- **common.yaml** : CephClusterなどの各種CRD、NamespaceやRole等を宣言
- **operator.yaml** : rook-ceph-operatorを定義
- **cluster.yaml** : CephClusterの内容を定義する、使うCephのバージョン(Luminous,mimic,nautilus)もここで選べる

リポジトリをcloneしたら順番にapplyしていきましょう。

```shell:Ceph&nbsp;Clusterの作成
（※標準出力が長いので割愛） 
$ git clone https://github.com/rook/rook.git
$ cd rook/rook/cluster/examples/kubernetes/ceph/

$ kubectl apply -f common.yaml
$ kubectl apply -f operator.yaml
$ kubectl apply -f cluster.yaml
```

ここまで正常に終了し、Ceph Clusterが立ち上がると以下のようにPodが起動しているはずです。

```shell:Ceph&nbsp;Clusterの起動結果
$ kubectl get pod -n rook-ceph
NAME                                           READY   STATUS      RESTARTS   AGE
rook-ceph-agent-78jxd                          1/1     Running     0          20m
rook-ceph-agent-bdhcj                          1/1     Running     0          20m
rook-ceph-agent-jlp7l                          1/1     Running     0          20m
rook-ceph-mgr-a-6486bdd777-drf4n               1/1     Running     0          14m
rook-ceph-mon-a-b6589c4c4-k78cb                1/1     Running     0          15m
rook-ceph-mon-b-b7b95d74-vngbt                 1/1     Running     0          15m
rook-ceph-mon-c-58f8748475-h7s4b               1/1     Running     0          14m
rook-ceph-operator-7bc8459977-zkz5r            1/1     Running     0          10m
rook-ceph-osd-0-5dcf7ffcb6-zsjkh               1/1     Running     0          14m
rook-ceph-osd-1-75bf9f64c-mdr9j                1/1     Running     0          14m
rook-ceph-osd-2-5c56d485d8-wbt79               1/1     Running     0          14m
rook-ceph-osd-prepare-ip-172-31-16-45-bvbmb    0/2     Completed   0          9m16s
rook-ceph-osd-prepare-ip-172-31-19-51-6j42j    0/2     Completed   0          9m14s
rook-ceph-osd-prepare-ip-172-31-28-223-44prs   0/2     Completed   0          9m12s
rook-discover-jgb2n                            1/1     Running     0          20m
rook-discover-sqggt                            1/1     Running     0          20m
rook-discover-v847d                            1/1     Running     0          20m
```

## 【手順２．】operator-with-csiの適用
先ほどrook-ceph-operatorを作成しましたが、そちらにCSI関連のパラメータを与えて更新します。そのためにはRBACの設定も必要になるので、そちらも忘れずに適用しておきます。


```shell:operator-with-csi.yamlの適用
$ kubectl apply -f csi/rbac/rbd/
serviceaccount/rook-csi-rbd-plugin-sa created
clusterrole.rbac.authorization.k8s.io/rbd-csi-nodeplugin created
clusterrole.rbac.authorization.k8s.io/rbd-csi-nodeplugin-rules created
clusterrolebinding.rbac.authorization.k8s.io/rbd-csi-nodeplugin created
serviceaccount/rook-csi-rbd-provisioner-sa 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/rbd-csi-provisioner-role created

$ kubectl apply -f rook-operator-with-csi.yaml
deployment.apps/rook-ceph-operator configured
```

これで何が行われたかというと、csi-rbdplugin-provisonerというStatefulSetとcsi-rbdpluginというDaemonSetが作成され、Podが下記のように起動してきます。

```shell:csi-rbdpluginのPod
$ kubectl get pod -n rook-ceph
NAME                                           READY   STATUS      RESTARTS   AGE
csi-rbdplugin-h2n9j                            2/2     Running     0          4m42s
csi-rbdplugin-hbpzx                            2/2     Running     0          4m42s
csi-rbdplugin-provisioner-0                    4/4     Running     0          6m16s
csi-rbdplugin-tst94                            2/2     Running     0          4m42s
（※以下略）
```

## 【手順３．】CSIの動的プロビジョニング/アタッチを試す
ここまででRookによるCephクラスタの構築と、CSIのrbdpluginの準備が完了しました。
しかし、ここからStorageClass->PVC->PVとボリュームを動的にプロビジョニングしてアタッチするまでに、実はもう一山あります。

それが公式ドキュメントの[Test RBD CSI Driver](https://rook.io/docs/rook/v1.0/ceph-csi-drivers.html#Test-RBD-CSI-Driver)になるのですが、手順としては以下のようになります。

1. RBD用のreplicapoolを作成
1. csi-rbdpluginを呼び出すStorageClassでmonのアドレスを修正して作成
1. Cephクラスタにclient.Kubernetesの認証情報を作成
1. ceph auth get-keyでキーを取得してsecret.yamlに反映して作成
1. PVCを作成
1. 5.のPVCを利用するPodを作成

コマンドを実行した結果は以下のようになります。

```shell:PodからCSIのプロビジョニング/アタッチを試すところまで
#1. RBD用のreplicapoolを作成
$ kubectl apply -f pool.yaml
cephblockpool.ceph.rook.io/rbd created

#2.csi-rbdpluginを呼び出すStorageClassでmonのアドレスを修正して作成 
$ cd csi/exsample/rbd/
$ vi storageclass.yaml
#   以下の箇所を修正。アドレスには各monのサービス名:portを指定する
#   monitors: mon1:port,mon2:port,...
$ kubectl apply -f storageclass.yaml

#3. Cephクラスタにclient.Kubernetesの認証情報を作成
$ kubectl exec -ti -n rook-ceph rook-ceph-operator-xxx -- bash -c "ceph -c /var/lib/rook/rook-ceph/rook-ce
ph.config auth get-or-create-key client.kubernetes mon \"allow profile rbd\" osd \"profile rbd pool=rbd\""

#4. ceph auth get-keyでキーを取得してsecret.yamlに反映して作成
$ kubectl exec -ti -n rook-ceph rook-ceph-operator-7bc8459977-zkz5r -- ceph auth get-key client.admin|base64
# この結果を（１）としてメモ
$ kubectl exec -ti -n rook-ceph rook-ceph-operator-7bc8459977-zkz5r -- ceph auth get-key client.kubernetes|base64
# こちらは（２）としてメモ
$ vi secret.yaml
  #   以下の箇所を修正
   # Key value corresponds to a user name defined in Ceph cluster
   # admin: （１）を記入
   # Key value corresponds to a user name defined in Ceph cluster
   # kubernetes: （２）を記入
$ kubectl apply -f secret.yaml

#5. PVCを作成
$ kubectl apply -f pvc.yaml
persistentvolumeclaim/rbd-pvc created

#6. 5.のPVCを利用するPodを作成
$ kubectl apply -f pod.yaml
pod/csirbd-demo-pod created

```

2.から4.までがかなり作業として煩雑です。CSIを使った場合には、provisionerが外部に出てしまうので、そこに認証情報を渡すには今はこれしかないという感じなのでしょうか。
今後のupdateを望む部分です。

ここまで完了するとPVCと動的にプロビジョニングされたPVが作成されています。

```shell:csi-rbdpluginで作成されたPVC、PV
[ec2-user@ip-172-31-9-219 rbd]$ kubectl get pvc,pv
NAME      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
rbd-pvc   Bound    pvc-50f21ed8-7a4a-11e9-a568-062c66232d1c   1Gi        RWO            csi-rbd        43s

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
pvc-50f21ed8-7a4a-11e9-a568-062c66232d1c   1Gi        RWO            Delete           Bound    default/rbd-pvc   csi-rbd                 25m
```

## 初期化が危ない！
ここまで一本道のように見えますが、実はいろいろと試行錯誤をしました。おそらく皆さんが試す際にも同じようになるはずです。

ここで注意点が一つ。

clusterやoperatorを再作成したいという場合、[こちら](https://rook.io/docs/rook/v1.0/ceph-teardown.html)にあるクリーンアップの手順に従うことになります。v0.9の頃からRookのクリーンアップはkubectl deleteだけでは済まずに、ホスト上のファイル削除が必要でした。

しかし、上で書いたBlueStoreを使う場合、さらに一つ手順が追加されます。
それが以下のシェルを作成して各ノードで実行、というもの。

> \#!/usr/bin/env bash
> DISK="/dev/xxx"
> \# Zap the disk to a fresh, usable state (zap-all is important, b/c MBR has to be clean)
> \# You will have to run this step for all disks.
> sgdisk --zap-all $DISK
> 
> \# These steps only have to be run once on each node
> \# If rook sets up osds using ceph-volume, teardown leaves some devices mapped that lock the disks.
> ls /dev/mapper/ceph-* | xargs -I% -- dmsetup remove %
> \# ceph-volume setup can leave ceph-<UUID> directories in /dev (unnecessary clutter)
> rm -rf /dev/ceph-*

これが**<font color="red">非常に危険</font>**です。
上記でDISKを書き換えて実行すると**<font color="red">該当ディスクをまっさらに初期化</font>**してくれます。
rootボリュームでも容赦なし、です（試してませんが）。

この初期化を考えるだけでも、**テスト時はFileStoreでの構築を強くお勧めします。**

# まとめ
さて、どうだったでしょうか。

今回はかなりの長文となりましたが、それほど現在のCeph CSIの利用手順は複雑です。
CSIの仕組み自体の解説などは全く出来てませんので、次回はこのRookを例にCSIの各モジュールの配置などが解説できればと思います。

あ、タイトルの説明を忘れていました。
今回のRook、Ceph CSIを使った検証内容を[CloudNativeDays Tokyo 2019](https://cloudnativedays.jp/cndt2019/)で発表予定です。こちらもご期待下さい。

よろしくお願いします。