Kubesprayで構築したk8s環境で、Rook Ceph v1.0.1を試してみた

# はじめに

以前に投稿した[KubernetesにRookを導入して、Block Storageを試してみた](https://qiita.com/YasuhiroABE/items/dcc8cc532459e506644d)の内容が少し古くなってきたと感じています。Rookはバージョン毎に微妙に手順が増えたり、対象のファイル名が変更されていたりするので、利用するバージョンのドキュメントを参照する必要があります。

最近構築したk8sクラスタにrookを構成したので、その時のメモを残しておきます。


:::note alert
filestoreの利用例として残していますが、この記事は完全に時代遅れです。
filestoreを使用した古いrook/cephはアップグレードせず、HDDを加えてbluestoreを有効にした最新版を新規に導入することをお勧めします。
:::

# 参考資料

* [rook v1.0公式マニュアル](https://rook.github.io/docs/rook/v1.0/) 
* [【CNDT2019への道】GWにやってきた！Rook v1.0のCSIを試す](https://qiita.com/tzkoba/items/4baea914e22e279fab9b)
* [Rook公式サイト: Ceph Block Storage](https://rook.github.io/docs/rook/v1.0/ceph-block.html)

# 環境

下記のkubesprayのバージョンで、私の環境では問題が発生しました。
そのワークアラウンドを含めて、まとめています。

* Kubernetes v1.12.7 (kubespray v2.8.5)
* Rook v1.0.1

ノードは多数ありますが、node01-05はディスクが少ないため、software raid1を構成している/dev/md0が存在するノード(node06-09)だけをrookに参加させる予定です。
具体的にノード名を指定する方法もありますが、今回は該当Nodeのlablesに対応する値を設定する方法を採用しています。

```bash:NODEの状況
~# kubectl get node
NAME       STATUS   ROLES         AGE   VERSION
node01    Ready    node          39d   v1.12.7
node02    Ready    node          39d   v1.12.7
node03    Ready    node          39d   v1.12.7
node04    Ready    node          39d   v1.12.7
node05    Ready    node          39d   v1.12.7
node06    Ready    master,node   39d   v1.12.7
node07    Ready    master,node   39d   v1.12.7
node08    Ready    node          39d   v1.12.7
node09    Ready    node          39d   v1.12.7
```

# 準備作業

masterノードで次の要領でファイルを準備して、ディレクトリを移動しておきます。

```bash:rookリポジトリのclone
$ git clone https://github.com/rook/rook
$ cd rook
$ git checkout refs/tags/v1.0.1 -b my_v1.0.1
$ cd cluster/examples/kubernetes/ceph/
```

# 実際の作業

[rook v1.0公式マニュアル](https://rook.github.io/docs/rook/v1.0/) の手順に従います。
v0.8やv0.9の2つから1つ増えて、3つのファイルをkubectl -f applyコマンドの引数に指定することになっていますが、今回は少し構成を変更して進めます。

### common.ymlファイルはそのまま適用

```bash
$ kubectl apply -f common.yml
```

## operator.ymlファイルの編集

operator.ymlはkubespray環境ではFLEXVOLUMEに関係するプロパティを変更する必要があります。
内容は https://rook.github.io/docs/rook/v1.0/flexvolume.html に記述されているとおりです。

```diff:operator.yml
diff --git a/cluster/examples/kubernetes/ceph/operator.yaml b/cluster/examples/kubernetes/ceph/operator.yaml
index 52f83376..d9a2b23c 100644
--- a/cluster/examples/kubernetes/ceph/operator.yaml
+++ b/cluster/examples/kubernetes/ceph/operator.yaml
@@ -62,8 +62,8 @@ spec:
         # - name: AGENT_MOUNT_SECURITY_MODE
         #   value: "Any"
         # 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"
         # Set the path where kernel modules can be found
         # - name: LIB_MODULES_DIR_PATH
         #  value: "<PathToLibModules>"
```

```bash
$ kubectl apply -f operator.yml
```

## cluster.ymlファイルの編集

cluster.ymlファイルでは、利用するノードを指定するために、placement:のセクションを有効化しています。

また参考資料に挙げたRook v1.0の解説で、デフォルトのdatastoreがfilestoreからbluestoreに変更になっているという記述があったので、storetType, directories も変更しています。

```diff:cluster.yml
--- a/cluster/examples/kubernetes/ceph/cluster.yaml
+++ b/cluster/examples/kubernetes/ceph/cluster.yaml
@@ -51,21 +51,21 @@ spec:
   # To control where various services will be scheduled by kubernetes, use the placement configuration sectio
ns below.
   # The example under 'all' would have all services scheduled on kubernetes nodes labeled with 'role=storage-
node' and
   # tolerate taints with a key of 'storage-node'.
-#  placement:
-#    all:
-#      nodeAffinity:
-#        requiredDuringSchedulingIgnoredDuringExecution:
-#          nodeSelectorTerms:
-#          - matchExpressions:
-#            - key: role
-#              operator: In
-#              values:
-#              - storage-node
-#      podAffinity:
-#      podAntiAffinity:
-#      tolerations:
-#      - key: storage-node
-#        operator: Exists
+  placement:
+    all:
+      nodeAffinity:
+        requiredDuringSchedulingIgnoredDuringExecution:
+          nodeSelectorTerms:
+          - matchExpressions:
+            - key: role
+              operator: In
+              values:
+              - storage-node
+      podAffinity:
+      podAntiAffinity:
+      tolerations:
+      - key: storage-node
+        operator: Exists
 # The above placement information can also be specified for mon, osd, and mgr components
 #    mon:
 #    osd:
@@ -89,22 +89,22 @@ spec:
 #    mon:
 #    osd:
   storage: # cluster level storage configuration and selection
-    useAllNodes: true
-    useAllDevices: true
+    useAllNodes: false
+    useAllDevices: false
     deviceFilter:
     location:
     config:
       # The default and recommended storeType is dynamically set to bluestore for devices and filestore for directories.
       # Set the storeType explicitly only if it is required not to use the default.
-      # storeType: bluestore
+      storeType: filestore
       # metadataDevice: "md0" # specify a non-rotational storage so ceph-volume will use it as block db device of bluestore.
       # databaseSizeMB: "1024" # uncomment if the disks are smaller than 100 GB
       # journalSizeMB: "1024"  # uncomment if the disks are 20 GB or smaller
       # osdsPerDevice: "1" # this value can be overridden at the node or device level
       # encryptedDevice: "true" # the default value for this option is "false"
 # Cluster level list of directories to use for filestore-based OSD storage. If uncommented, this example would create an OSD under the dataDirHostPath.
-    #directories:
-    #- path: /var/lib/rook
+    directories:
+    - path: /var/lib/rook
 # Individual nodes and their config can be specified as well, but 'useAllNodes' above must be set to false. Then, only the named
 # nodes below will be used as storage resources.  Each node's 'name' field should match their 'kubernetes.io/hostname' label.
 #    nodes:
diff --git a/cluster/examples/kubernetes/ceph/operator.yaml b/cluster/examples/kubernetes/ceph/operator.yaml
index 52f83376..d9a2b23c 100644
--- a/cluster/examples/kubernetes/ceph/operator.yaml
+++ b/cluster/examples/kubernetes/ceph/operator.yaml
@@ -62,8 +62,8 @@ spec:
         # - name: AGENT_MOUNT_SECURITY_MODE
         #   value: "Any"
         # 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"
         # Set the path where kernel modules can be found
         # - name: LIB_MODULES_DIR_PATH
         #  value: "<PathToLibModules>"
diff --git a/cluster/examples/kubernetes/ceph/storageclass.yaml b/cluster/examples/kubernetes/ceph/storageclass.yaml
index ae18858f..2ed4ac1d 100644
--- a/cluster/examples/kubernetes/ceph/storageclass.yaml
+++ b/cluster/examples/kubernetes/ceph/storageclass.yaml
@@ -11,7 +11,7 @@ metadata:
   namespace: rook-ceph
 spec:
   replicated:
-    size: 3
+    size: 2
 ---
 apiVersion: storage.k8s.io/v1
 kind: StorageClass
```

変更したファイルをapplyしておきます。

```bash
$ kubectl apply -f cluster.yml
```

またディスクを利用したいノードの定義を編集して、labelsに*role: storage-node*を設定しておきます。

```bash
$ kubectl edit node node01
...
  labels:
...
    role: storage-node
  name: node01
...
```

ここまでで、作成されたNamespaceは*rook-ceph*の1つだけです。
v0.9まではrook-cephとrook-ceph-systemに分散していたpodは1つにまとめてられています。


# 遭遇した問題

今回の作業で遭遇した問題についてまとめておきます。

## OSDノードがまったく起動しない

この状態ではPVCを作成しようとしても、Pendingの状態のままで、CephFilesystemもStorageClassも定義はできても動かない状態になります。

空きデバイスがまったくない状態で、storeTypeがbluestoreになっていたためで、cluster.ymlを編集する際に明示的にfilestoreに変更しています。

## ログに/usr/libexec/kubernetes/kubelet-plugins/volume/exec 以下のコマンドを実行している記録が残る

うまく動いていない理由を調査していたところ、ログを確認していくとkubeletに--volume-plugin-dirが設定されていない事が分かりました。

githubのissuesに報告されていて、少なくとも最新のkubespray v2.10.0では改善されていますし、コードを見る限りv2.9.0でも対応されているようです。
（別のタイミングでv2.9.0はうまく動かなかったので個人的にはv2.8.5とv2.10.0を利用しています）

* https://github.com/kubernetes-sigs/kubespray/issues/4285

v2.8.5ではファイル名は違っていますが、KUBELET_VOLUME_PLUGIN環境変数が定義されていません。

```bash:./roles/kubernetes/node/templates/kubelet.kubeadm.env.j2
$ grep KUBELET_VOLUME_PLUGIN ./roles/kubernetes/node/templates/kubelet.kubeadm.env.j2
KUBELET_VOLUME_PLUGIN="--volume-plugin-dir={{ kubelet_flexvolumes_plugins_dir }}"
```

とりあえず手動でkubespray(v2.8.5)側のファイルを修正し、upgrade-cluster.ymlによって反映させています。

```diff
--- a/roles/kubernetes/node/templates/kubelet.kubeadm.env.j2
+++ b/roles/kubernetes/node/templates/kubelet.kubeadm.env.j2
@@ -102,6 +102,7 @@ KUBELET_NETWORK_PLUGIN="--network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni
 {% elif kube_network_plugin is defined and kube_network_plugin == "cloud" %}
 KUBELET_NETWORK_PLUGIN="--hairpin-mode=promiscuous-bridge --network-plugin=kubenet"
 {% endif %}
+KUBELET_VOLUME_PLUGIN="--volume-plugin-dir={{ kubelet_flexvolumes_plugins_dir }}"
 # Should this cluster be allowed to run privileged docker containers
 KUBE_ALLOW_PRIV="--allow-privileged=true"
 {% if cloud_provider is defined and cloud_provider in ["openstack", "azure", "vsphere", "aws"] %}
```

# rookの再インストールの際に考慮すること

マニュアルにはCleanupの方法が指定されています。
基本的にはnamespaceを削除すれば良いのですが、k8s上の定義を消す事の他に、各ノードの /var/lib/rook ディレクトリを削除する事が大切です。

* https://rook.github.io/docs/rook/v1.0/ceph-teardown.html

```kubectl delete```コマンドの他に、ansibleを利用して、全ノードの/var/lib/rookディレクトリを削除しています。

以上