Kubernetes on Raspberry Pi 4B(ARM64)でCephを導入しました

# はじめに
自宅のラズパイkubernetesクラスタの永続ストレージをなんとかしたいと思い、Cephに手を出してしまいました。結構大変な作業量でしたので書き記しておきます。

# 前提環境

簡単な図です。IoTの実験も兼ねて回線品質の悪いwifi経由のkubernetesクラスタが1台おります。
![スクリーンショット 2020-09-21 16.25.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410957/98082d49-e578-0294-044b-22d1c33614dc.png)


- Raspberry Pi 4B(RAM:4GB) * 5台 (Masterノード1台,Workerノード4台)
 - USBのSSD(250GB)から起動
 - 1台は遠隔にあり監視カメラとして利用中(今回使用しない)
 - OSは、Raspberry Pi OS 64bit(beta)

```shell-session:
# kubectl get nodes -o wide
NAME    STATUS   ROLES    AGE    VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                       KERNEL-VERSION   CONTAINER-RUNTIME
chino   Ready    master   8d     v1.19.2   10.0.0.1      <none>        Debian GNU/Linux 10 (buster)   5.4.51-v8+       docker://19.3.13
chiya   Ready    worker   8d     v1.19.2   10.0.0.5      <none>        Debian GNU/Linux 10 (buster)   5.4.51-v8+       docker://19.3.13
cocoa   Ready    worker   46h    v1.19.2   10.0.0.2      <none>        Debian GNU/Linux 10 (buster)   5.4.51-v8+       docker://19.3.13
rize    Ready    worker   2d2h   v1.19.2   10.0.0.3      <none>        Debian GNU/Linux 10 (buster)   5.4.51-v8+       docker://19.3.13
syaro   Ready    worker   42h    v1.19.2   10.0.0.4      <none>        Debian GNU/Linux 10 (buster)   5.4.51-v8+       docker://19.3.13
```

## Ceph用パーティション作成
workerノードのSSDに新規パーティションを作成します。
私の構成ではどのノードもUSBブートで接続しているSSD(250GB)のみの運用なので、今回はmicroSDのRaspberry Pi OSを起動して作業しました。

```shell-session:
# e2fsck -f /dev/sda2
# resize2fs /dev/sda2 100G
# fdisk /dev/sda
Device     Boot     Start       End   Sectors   Size Id Type
/dev/sda1            8192    532479    524288   256M  c W95 FAT32 (LBA)
/dev/sda2          532480 488397167 487864688   233G 83 Linux
※「p」で、パーティション情報を表示。「/」パーティション(/dev/sda2)の開始位置を確認。
※「d」で、第2パーティションを削除。
※ 改めて同じ開始位置から第2パーティション作成。終了位置は「+100G」で指定。
※「w」でパーティション変更を保存。
# partprobe
# e2fsck -f /dev/sda2
※ここでエラーが出る場合は修復せずに「Ctrl」+「c」で抜けて、今度はpartedで/dev/sda2を切り直してみるとうまく行く場合がある。
# fdisk /dev/sda
※「n」で、残り全部で/dev/sda3を作成。パーティションタイプは、8eの「Linux LVM」。
# partprobe
# fdisk -l /dev/sda
Device     Boot     Start       End   Sectors   Size Id Type
/dev/sda1            8192    532479    524288   256M  c W95 FAT32 (LBA)
/dev/sda2          532480 210247679 209715200   100G 83 Linux
/dev/sda3       210247680 488397167 278149488 132.6G 8e Linux LVM
```

これをworkerノード3台で実施。サービスを止めたくなかったので、1台ずつkubernetesのdrainをして、再度参加させました。
Ethernetコンバータ経由でkubernetesクラスタに参加している別の部屋のworkerノード(chiya)は使いません。

あと、電力供給に問題が無いのであれば、USBにもう1台SSDを追加した方がresize2fsで壊すリスクも無いですし、作業は早いです。
...今回、resize2fsに失敗してworkerノードを2台再構築してます。

# Cephの構築
『[ラズパイ上のKubernetesにCephを入れてみた](https://qh8pik1c1k755ety.qrunch.io/entries/B4KCS9X31H3fytN3)』という記事の記載どおりに作業しました。CephのドキュメントURLが変わった事もあり、Ceph関連の情報はデッドリンクが多い中、日本語でこのような情報があると非常に助かりますね。

以下は、masterノードでの作業です。コマンド実行中に、ひたすらパスワードを打ちました。

```shell-session:
# mkdir ceph-deploy
# cd ceph-deploy
# pip install ceph-deploy
# ceph-deploy --username pi new cocoa rize syaro
# ceph-deploy --username pi install cocoa rize syaro
# ceph-deploy install chino
# ceph-deploy --username pi mon create-initial
# ceph-deploy --username pi admin cocoa rize syaro
# ceph-deploy admin chino
# ceph-deploy --username pi osd create cocoa --data /dev/sda3
# ceph-deploy --username pi osd create rize --data /dev/sda3
# ceph-deploy --username pi osd create syaro --data /dev/sda3
# ceph-deploy --username pi mds create cocoa
```

## poolの作成

コマンド最後のPG数は、あまり大きくするとリソース消費量が多くなるそうです。

```shell-session:
# ceph osd pool create kubernetes 128
```

## Ceph-CSI ConfigMapの作成

fsidと、IPアドレス,ポート番号を確認します。

```shell-session:
# ceph mon dump
dumped monmap epoch 2
epoch 2
fsid 56b03534-e602-4856-8734-8bcdf5cc8670
last_changed 2020-09-20 01:24:55.648189
created 2020-09-20 01:24:08.811926
0: 10.0.0.2:6789/0 mon.cocoa
1: 10.0.0.3:6789/0 mon.rize
2: 10.0.0.4:6789/0 mon.syaro
```

作成したyamlは以下です。

```yaml:csi-config-map.yaml
apiVersion: v1

kind: ConfigMap
data:
  config.json: |-
    [
      {
        "clusterID": "56b03534-e602-4856-8734-8bcdf5cc8670",
        "monitors": [
          "10.0.0.2:6789",
          "10.0.0.3:6789",
          "10.0.0.4:6789"
        ]
      }
    ]
metadata:
  name: ceph-csi-config
```

適用します。

```shell-session:
# kubectl apply -f csi-config-map.yaml
```

## RBD Secret作成

クライアント認証用のキーを作成します。

```shell-session:
# ceph auth get-or-create client.kubernetes mon 'profile rbd' osd 'profile rbd pool=kubernetes' mgr 'profile rbd pool=kubernetes'
[client.kubernetes]
	key = AQBrNmZfVCowLBAAeN3EYjhOPBG9442g4NF/bQ==
```

作成したyamlは以下です。


```yaml:csi-rbd-secret.yaml 
apiVersion: v1

kind: Secret
metadata:
  name: csi-rbd-secret
  namespace: default
stringData:
  userID: kubernetes
  userKey: AQBrNmZfVCowLBAAeN3EYjhOPBG9442g4NF/bQ==
```

適用します。

```shell-session:
# kubectl apply -f csi-rbd-secret.yaml
```

## KMS ConfigMap(空)の作成

```yaml:kms-config.yaml 
apiVersion: v1
kind: ConfigMap
data:
  config.json: |-
    {
    }
metadata:
  name: ceph-csi-encryption-kms-config
```

適用します。

```shell-session:
# kubectl apply -f kms-config.yaml 
```

※おそらく「# kubectl create configmap ceph-csi-encryption-kms-config」でもいいんじゃないかと思ってます。

## RBAC定義

```shell-session:
# wget https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-provisioner-rbac.yaml
# kubectl apply -f csi-provisioner-rbac.yaml
# wget https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-nodeplugin-rbac.yaml
# kubectl apply -f csi-nodeplugin-rbac.yaml
```

## コンテナイメージの作成

ここらから参考にしているサイトから若干手順を変えて書きます。作業はmasterノードじゃない方が良いはずですが、masterノードでやりました。
先にcsiのyamlを取得して、cephcsiをarm64に変えておきます。

```shell-session:
# wget https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml  
# sed -i -e 's/quay.io\/cephcsi\/cephcsi:canary/quay.io\/cephcsi\/cephcsi:canary-arm64/g' csi-rbdplugin-provisioner.yaml  

# wget https://raw.githubusercontent.com/ceph/ceph-csi/master/deploy/rbd/kubernetes/csi-rbdplugin.yaml  
# sed -i -e 's/quay.io\/cephcsi\/cephcsi:canary/quay.io\/cephcsi\/cephcsi:canary-arm64/g' csi-rbdplugin.yaml  
```

で、imageで使われているコンテナイメージのバージョンを確認します。

```shell-session:
# grep image: csi-rbdplugin-provisioner.yaml csi-rbdplugin.yaml
csi-rbdplugin-provisioner.yaml:          image: quay.io/k8scsi/csi-provisioner:v1.6.0
csi-rbdplugin-provisioner.yaml:          image: quay.io/k8scsi/csi-snapshotter:v2.1.0
csi-rbdplugin-provisioner.yaml:          image: quay.io/k8scsi/csi-attacher:v2.1.1
csi-rbdplugin-provisioner.yaml:          image: quay.io/k8scsi/csi-resizer:v0.5.0
csi-rbdplugin-provisioner.yaml:          image: quay.io/cephcsi/cephcsi:canary-arm64
csi-rbdplugin-provisioner.yaml:          image: quay.io/cephcsi/cephcsi:canary-arm64
csi-rbdplugin.yaml:          image: quay.io/k8scsi/csi-node-driver-registrar:v1.3.0
csi-rbdplugin.yaml:          image: quay.io/cephcsi/cephcsi:canary-arm64
csi-rbdplugin.yaml:          image: quay.io/cephcsi/cephcsi:canary-arm64
```

バージョンに(大体)合ったブランチをgit cloneします。

```shell-session:
# git clone --single-branch --branch release-1.6 https://github.com/kubernetes-csi/external-provisioner
# git clone --single-branch --branch release-2.1 https://github.com/kubernetes-csi/external-snapshotter
# git clone --single-branch --branch release-2.1 https://github.com/kubernetes-csi/external-attacher
# git clone --single-branch --branch release-0.5 https://github.com/kubernetes-csi/external-resizer
# git clone --single-branch --branch release-1.3 https://github.com/kubernetes-csi/node-driver-registrar
```

※attacher:v2.2.1の取得方法がわからなかったので、2.1にしてます。

goのダウンロードをします。https://golang.org/dl/
先に書いておきますが、上記で取得したバージョンのコンテナイメージのmakeでは「go-1.13」を想定しているようです。
※ここでは、その時最新だったと思われるgo-1.15.1をダウンロードしています。
※世代にもよると思いますが、goは心の中では「ンゴゥ」と読んでしまいます。


```shell-session:
# wget https://dl.google.com/go/go1.15.1.linux-arm64.tar.gz
# tar xzvf go1.15.1.linux-arm64.tar.gz
# rm go1.15.1.linux-arm64.tar.gz 
# echo 'export GOPATH=$HOME/go' >> ~/.bashrc
# echo 'export PATH=$GOPATH/bin:$PATH' >> ~/.bashrc
# source ~/.bashrc
# go version
go version go1.15.1 linux/arm64
```

makeしてdockerイメージを作成します。

```shell-session:
# cd external-provisioner
# make
# docker build -t csi-provisioner:v1.6.0 .
# cd ../external-snapshotter
# make
# cp cmd/csi-snapshotter/Dockerfile .
# docker build -t csi-snapshotter:v2.1.0 .
# cd ../external-attacher
# make
# docker build -t csi-attacher:v2.1.0 .
# cd ../external-resizer
# make
# docker build -t csi-resizer:v0.5.0 .
# cd ../node-driver-registrar
# make
# docker build -t csi-node-driver-registrar:v1.3.0 .
```

snapshotterだけDockerfileがよくわからん事になってました。...おそらく作業手順が違うのでしょうが、強引にイメージ作りました。
イメージはdockerに入るので、saveしてworkerノードのdockerにloadしておきます。

```shell-session:
# docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
csi-node-driver-registrar            v1.3.0              a6e114649cf9        33 hours ago        14.2MB
csi-resizer                          v0.5.0              d6b561c2aa0a        34 hours ago        41.6MB
csi-attacher                         v2.1.0              807c3900bf76        34 hours ago        41.7MB
csi-snapshotter                      v2.1.0              653dbf034d1d        34 hours ago        41.8MB
csi-provisioner                      v1.6.0              4b18fda1685c        34 hours ago        43.4MB
(以下略)
# docker save csi-node-driver-registrar:v1.3.0 -o csi-node-driver-registrar.tar
# docker save csi-resizer:v0.5.0 -o csi-resizer.tar
# docker save csi-attacher:v2.1.0 -o csi-attacher.tar
# docker save csi-snapshotter:v2.1.0 -o csi-snapshotter.tar
# docker save csi-provisioner:v1.6.0 -o csi-provisioner.tar
```

workerノードにscpでもsftpでも何でもいいのでコピーします。
コピーしたら、各workerノードで以下のコマンドを実行します。一応、使用しないworkerノードにも入れました。

```shell-session:
# docker load -i csi-node-driver-registrar.tar
# docker load -i csi-resizer.tar
# docker load -i csi-attacher.tar
# docker load -i csi-snapshotter.tar
# docker load -i csi-provisioner.tar
```

## Cephのデプロイ

で、ceph-deployでの作業に戻ります。
「csi-rbdplugin-provisioner.yaml」と「csi-rbdplugin.yaml」の「image:」箇所を以下のように、作成したイメージのリポジトリ・タグに変更しました。 
※長いので差分だけ表示させました。

```shell-session:
# grep -n image: csi-rbdplugin-provisioner.yaml csi-rbdplugin.yaml 
csi-rbdplugin-provisioner.yaml:35:          image: csi-provisioner:v1.6.0
csi-rbdplugin-provisioner.yaml:52:          image: csi-snapshotter:v2.1.0
csi-rbdplugin-provisioner.yaml:68:          image: csi-attacher:v2.1.0
csi-rbdplugin-provisioner.yaml:82:          image: csi-resizer:v0.5.0
csi-rbdplugin-provisioner.yaml:102:          image: quay.io/cephcsi/cephcsi:canary-arm64
csi-rbdplugin-provisioner.yaml:142:          image: quay.io/cephcsi/cephcsi:canary-arm64
csi-rbdplugin.yaml:28:          image: csi-node-driver-registrar:v1.3.0
csi-rbdplugin.yaml:50:          image: quay.io/cephcsi/cephcsi:canary-arm64
csi-rbdplugin.yaml:102:          image: quay.io/cephcsi/cephcsi:canary-arm64
```

では、デプロイしましょう。成功例しか無いですが...

```shell-session:
# kubectl apply -f csi-rbdplugin-provisioner.yaml
# kubectl apply -f csi-rbdplugin.yaml
# kubectl get pod -o wide
NAME                                         READY   STATUS    RESTARTS   AGE    IP            NODE    NOMINATED NODE   READINESS GATES
csi-rbdplugin-hm9bm                          3/3     Running   3          24h    10.0.0.4      syaro   <none>           <none>
csi-rbdplugin-provisioner-54dd99dd97-f9x2s   6/6     Running   6          24h    172.16.4.40   syaro   <none>           <none>
csi-rbdplugin-provisioner-54dd99dd97-flbh9   6/6     Running   0          24h    172.16.2.28   cocoa   <none>           <none>
csi-rbdplugin-provisioner-54dd99dd97-s9qf4   6/6     Running   0          24h    172.16.3.54   rize    <none>           <none>
csi-rbdplugin-t7569                          3/3     Running   0          24h    10.0.0.3      rize    <none>           <none>
csi-rbdplugin-x4fzk                          3/3     Running   3          24h    10.0.0.5      chiya   <none>           <none>
csi-rbdplugin-xwrnx                          3/3     Running   0          24h    10.0.0.2      cocoa   <none>           <none>
```

デプロイ時に、「# kubectl get events -w」としておく事をお勧めします。

## StorageClassの作成

StorageClassの作成をします。今更ですが、namespaceはdefaultじゃない方がいいかもしれませんね。

```yaml:csi-rbd-sc.yaml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: csi-rbd-sc
provisioner: rbd.csi.ceph.com
parameters:
   clusterID: "56b03534-e602-4856-8734-8bcdf5cc8670"
   pool: kubernetes
   csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
   csi.storage.k8s.io/provisioner-secret-namespace: default
   csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
   csi.storage.k8s.io/node-stage-secret-namespace: default
reclaimPolicy: Delete
mountOptions:
   - discard
```

適用します。

```
# kubectl apply -f csi-rbd-sc.yaml
```

あとは、アプリ側でPVCを作成する際に「storageClassName」を「csi-rbd-sc」指定してください。
ダイナミックプロビジョニングで使え....ません。
参考にしているサイトのPVCの定義では確認できませんでしたが、Rawモードの「volumeMode: Block」を削除したところ、以下のようなeventが出力されました。

```
default   0s     Warning    FailedMount        pod/es-cluster-0       MountVolume.MountDevice failed for volume "pvc- ...
...... rbd error output: modinfo: ERROR: Module rbd not found.
modprobe: FATAL: Module rbd not found in directory /lib/modules/5.4.51-v8+
rbd: failed to load rbd kernel module (1)
rbd: sysfs write failed
rbd: map failed: (2) No such file or directory
deault    0s     Warning    FailedMount        pod/es-cluster-0       Unable to attach or mount volumes: unmounted.....
```

前提に戻っちゃいますが、「rbd」というkernelモジュールが必要です。
そして、Raspberry Pi OS 64bit(Beta)ではそもそも「rbd」モジュールがコンパイルされていません。
つまり、kernelのコンパイルをしなければなりません。

コマンドでは各クライアントから使えるのに、なぜkernelモジュールが必要なのかは、『[Deep Dive Into Ceph’s Kernel Client](https://engineering.salesforce.com/deep-dive-into-cephs-kernel-client-edea75787528)』あたりで理解しました。（半分くらい）
コマンドはライブラリで使えて、コンテナ環境ではkernelモジュールが必要らしいです。

## kernelビルド
Linux kernelのコンパイルですが、昔は日常茶飯事の作業でした。
まだkernel moduleの概念が無かった頃、ロードできるkernelサイズに限度があり、皆自分のマシンに合ったkernelをギリギリのサイズでコンパイルしてました。新しいデバイスを追加する度にkernelコンパイルしてましたね。

なので、やった事はあるけど今どうなってんの？って感じで作業しました。
マニュアルは『[Kernel building](https://www.raspberrypi.org/documentation/linux/kernel/building.md)』になりますが、まだbetaの64bit版なので[実際にやってる方の投稿](https://www.raspberrypi.org/forums/viewtopic.php?t=280341)を参考にしました。
作業は1時間ほどかかりました。

```shell-session:
# apt install git libncurses-dev flex bison openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf bc
# git clone --depth=1 -b rpi-5.4.y https://github.com/raspberrypi/linux.git
# cd linux/
# make bcm2711_defconfig
# vi .config

(「CONFIG_BLK_DEV_RBD=m」を適当に書き加えます。)

# make -j4

(RBDを有効にする関係で、cephfsの設問が出てくると思いますが、よくわからなかったので全部「N」にしました。)

# make modules_install
# make dtbs_install
# cp /boot/kernel8.img /boot/kernel8_old.img
# cp arch/arm64/boot/Image /boot/kernel8.img
# vi /boot/config.txt

(フォーラムの投稿では「#」が付いてますが、以下3行を追記します。）
device_tree=dtbs/5.4.65-v8+/broadcom/bcm2711-rpi-4-b.dtb
overlay_prefix=dtbs/5.4.65-v8+/overlays/
kernel=kernel8.img
```

昔はドキドキしながら再起動させてましたが、さくっとやります。復旧できる自信がありますからね...

```shell-session:
# reboot
```

一通り確認します。カスタムkernelなので、もう有償サポートは受けられません。（そんなもの入ってませんが）

```shell-session:
# uname -a
Linux syaro 5.4.65-v8+ ＃1 SMP PREEMPT Mon Sep 21 01:04:01 JST 2020 aarch64 GNU/Linux
# modprobe rbd
# lsmod | grep rbd
rbd                    98304  0
libceph               278528  1 rbd
# modinfo rbd
filename:       /lib/modules/5.4.65-v8+/kernel/drivers/block/rbd.ko
license:        GPL
description:    RADOS Block Device (RBD) driver
author:         Jeff Garzik <jeff@garzik.org>
author:         Yehuda Sadeh <yehuda@hq.newdream.net>
author:         Sage Weil <sage@newdream.net>
author:         Alex Elder <elder@inktank.com>
srcversion:     BC90D52477A5CE4593C5AC3
depends:        libceph
intree:         Y
name:           rbd
vermagic:       5.4.65-v8+ SMP preempt mod_unload modversions aarch64
parm:           single_major:Use a single major number for all rbd devices (default: true) (bool)
```

良さそうです。
workerのkernelは全部入れ替えました。

```shell-session:
# kubectl get nodes -o wide
NAME    STATUS   ROLES    AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                       KERNEL-VERSION   CONTAINER-RUNTIME
chino   Ready    master   16d   v1.19.2   10.0.0.1      <none>        Debian GNU/Linux 10 (buster)   5.4.51-v8+       docker://19.3.13
chiya   Ready    worker   16d   v1.19.2   10.0.0.5      <none>        Debian GNU/Linux 10 (buster)   5.4.65-v8+       docker://19.3.13
cocoa   Ready    worker   9d    v1.19.2   10.0.0.2      <none>        Debian GNU/Linux 10 (buster)   5.4.65-v8+       docker://19.3.13
rize    Ready    worker   10d   v1.19.2   10.0.0.3      <none>        Debian GNU/Linux 10 (buster)   5.4.65-v8+       docker://19.3.13
syaro   Ready    worker   9d    v1.19.2   10.0.0.4      <none>        Debian GNU/Linux 10 (buster)   5.4.65-v8+       docker://19.3.13
```


# Ceph CSI StorageClass を使ったアプリのデプロイ

それでは、rbd kernelモジュールが無かった事により、一度失敗に終わったデプロイの再試行をします。
とあるサイトで参考にさせてもらっている、elasticsearchのシングルノード(statefulset版)です。
これを元にマルチノードにしようとしてます。（参考にしているサイトにありますが、更にpluginを入れたりしたいので...）

```yaml:es_master_sts.yaml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
spec:
  selector:
    matchLabels:
      app: es
  serviceName: "es-cluster"
  replicas: 1
  template:
    metadata:
      labels:
        app: es
    spec:
      containers:
      - name: es
        image: elasticsearch:7.9.1
        env:
        - name: discovery.type
          value: single-node
        ports:
        - containerPort: 9200
          name: api
        - containerPort: 9300
          name: gossip
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: 
        - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi
      storageClassName: csi-rbd-sc
```

適用します。

```shell-session:
# kubectl apply -f es_master_sts.yaml
```

podの状態を確認。

```shell-session:
# kubectl get pods -w
NAME                                         READY   STATUS             RESTARTS   AGE
csi-rbdplugin-hm9bm                          3/3     Running            3          25h
csi-rbdplugin-provisioner-54dd99dd97-f9x2s   6/6     Running            6          25h
csi-rbdplugin-provisioner-54dd99dd97-flbh9   6/6     Running            0          25h
csi-rbdplugin-provisioner-54dd99dd97-s9qf4   6/6     Running            0          25h
csi-rbdplugin-t7569                          3/3     Running            0          25h
csi-rbdplugin-x4fzk                          3/3     Running            3          25h
csi-rbdplugin-xwrnx                          3/3     Running            0          25h
es-cluster-0                                 0/1     Pending            0          0s
es-cluster-0                                 0/1     Pending            0          0s
es-cluster-0                                 0/1     Pending            0          2s
es-cluster-0                                 0/1     ContainerCreating  0          2s
es-cluster-0                                 1/1     Running            0          8s
```

イベントの監視。

```shell-session:
# kubectl get events -w
LAST SEEN   TYPE     REASON                 OBJECT                                    MESSAGE
0s          Normal   SuccessfulCreate       statefulset/es-cluster                    create Claim data-es-cluster-0 Pod es-cluster-0 in StatefulSet es-cluster success
0s          Normal   ExternalProvisioning   persistentvolumeclaim/data-es-cluster-0   waiting for a volume to be created, either by external provisioner "rbd.csi.ceph.com" or manually created by system administrator
0s          Normal   Provisioning           persistentvolumeclaim/data-es-cluster-0   External provisioner is provisioning volume for claim "default/data-es-cluster-0"
0s          Normal   SuccessfulCreate       statefulset/es-cluster                    create Pod es-cluster-0 in StatefulSet es-cluster successful
0s          Warning  FailedScheduling       pod/es-cluster-0                          0/5 nodes are available: 5 pod has unbound immediate PersistentVolumeClaims.
0s          Warning  FailedScheduling       pod/es-cluster-0                          0/5 nodes are available: 5 pod has unbound immediate PersistentVolumeClaims.
0s          Normal   ProvisioningSucceeded  persistentvolumeclaim/data-es-cluster-0   Successfully provisioned volume pvc-1c1abfad-87fa-4882-a840-8449c6d50326
0s          Normal   Scheduled              pod/es-cluster-0                          Successfully assigned default/es-cluster-0 to syaro
0s          Normal   SuccessfulAttachVolume pod/es-cluster-0                          AttachVolume.Attach succeeded for volume "pvc-1c1abfad-87fa-4882-a840-8449c6d50326"
0s          Normal   Pulled                 pod/es-cluster-0                          Container image "elasticsearch:7.9.1" already present on machine
0s          Normal   Created                pod/es-cluster-0                          Created container es
0s          Normal   Started                pod/es-cluster-0                          Started container es
```

ストレージ関連の確認。

```shell-session:
# kubectl get sc,pv,pvc
NAME                                     PROVISIONER        RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
storageclass.storage.k8s.io/csi-rbd-sc   rbd.csi.ceph.com   Delete          Immediate           false                  25h

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                       STORAGECLASS   REASON   AGE
persistentvolume/pvc-1c1abfad-87fa-4882-a840-8449c6d50326   1Gi        RWO            Delete           Bound    default/data-es-cluster-0   csi-rbd-sc              78s

NAME                                      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/data-es-cluster-0   Bound    pvc-1c1abfad-87fa-4882-a840-8449c6d50326   1Gi        RWO            csi-rbd-sc     78s
```

特にpodの削除再デプロイでも作成したファイルは残ってました。(当たり前）
別項にするほどでもないですが、性能面です。元々私はハード面は苦手で、ここらのノウハウはあんまりないのですが、ddで書き込んだ結果を書いておきます。

```shell-session:
# dd if=/dev/zero of=aaa bs=4096 count=100000
```

|  | 1回目 | 2回目 | 3回目 | 4回目 | 5回目 |
|:-:|:-:|:-:|:-:|:-:|:-:|
| workerのホストOS上  | 186 MB/s  | 133 MB/s  | 141 MB/s  | 140 MB/s  | 133 MB/s  |
| hostpath  | 120 MB/s  | 121 MB/s  | 134 MB/s  | 131 MB/s  | 131 MB/s  |
| ceph-csi  | 186 MB/s  | 185 MB/s  | 174 MB/s  | 178 MB/s  | 180 MB/s  |

ceph-csiは計測時には出なかったんですが、たまに50MB/sの時もあったり、コマンドの戻りが遅かったりとピーキーな感じがしますね。何が影響しているんだろうか...

# namespaceの変更 (追記:2020/10/2)

namespaceを「default」から「storage」に変更しました。

```
# grep -n namespace *.yaml
csi-config-map.yaml:18:  namespace: storage
csi-nodeplugin-rbac.yaml:6:  namespace: storage
csi-nodeplugin-rbac.yaml:12:  namespace: storage
csi-nodeplugin-rbac.yaml:22:  namespace: storage
csi-nodeplugin-rbac.yaml:26:    namespace: storage
csi-provisioner-rbac.yaml:6:  namespace: storage
csi-provisioner-rbac.yaml:13:  namespace: storage
csi-provisioner-rbac.yaml:59:  namespace: storage
csi-provisioner-rbac.yaml:63:    namespace: storage
csi-provisioner-rbac.yaml:73:  # replace with non-default namespace name
csi-provisioner-rbac.yaml:74:  namespace: storage
csi-provisioner-rbac.yaml:89:  # replace with non-default namespace name
csi-provisioner-rbac.yaml:90:  namespace: storage
csi-provisioner-rbac.yaml:94:    # replace with non-default namespace name
csi-provisioner-rbac.yaml:95:    namespace: storage
csi-rbd-sc.yaml:5:   namespace: storage
csi-rbd-sc.yaml:11:   csi.storage.k8s.io/provisioner-secret-namespace: storage
csi-rbd-sc.yaml:13:   csi.storage.k8s.io/node-stage-secret-namespace: storage
csi-rbd-secret.yaml:6:  namespace: storage
csi-rbdplugin-provisioner.yaml:6:  namespace: storage
csi-rbdplugin-provisioner.yaml:23:  namespace: storage
csi-rbdplugin.yaml:6:  namespace: storage
csi-rbdplugin.yaml:167:  namespace: storage
kms-config.yaml:9:  namespace: storage
```

特に問題なく動いているようです。
注意点として、「csi-rbd-secret」は、ceph-csiを利用するアプリの属するnamespace毎に必要になると思われます。

```shell-session:
# kubectl get pod,svc,configmap,secret -n storage
NAME                                             READY   STATUS    RESTARTS   AGE
pod/csi-rbdplugin-2v8bg                          3/3     Running   0          22h
pod/csi-rbdplugin-97p24                          3/3     Running   0          22h
pod/csi-rbdplugin-b86dk                          3/3     Running   0          22h
pod/csi-rbdplugin-b8jtj                          3/3     Running   0          22h
pod/csi-rbdplugin-provisioner-7f97bcf6fc-2zj78   6/6     Running   0          22h
pod/csi-rbdplugin-provisioner-7f97bcf6fc-fntnx   6/6     Running   0          22h
pod/csi-rbdplugin-provisioner-7f97bcf6fc-s99jh   6/6     Running   0          22h

NAME                                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/csi-metrics-rbdplugin       ClusterIP   10.96.142.248   <none>        8080/TCP   22h
service/csi-rbdplugin-provisioner   ClusterIP   10.96.247.32    <none>        8080/TCP   22h

NAME                                       DATA   AGE
configmap/ceph-csi-config                  1      22h
configmap/ceph-csi-encryption-kms-config   1      22h

NAME                                     TYPE                                  DATA   AGE
secret/csi-rbd-secret                    Opaque                                2      22h
secret/default-token-f294q               kubernetes.io/service-account-token   3      22h
secret/rbd-csi-nodeplugin-token-q2sbs    kubernetes.io/service-account-token   3      22h
secret/rbd-csi-provisioner-token-d87mw   kubernetes.io/service-account-token   3      22h
```

# 不足していた事 (追記：2020/10/19)

上記の手順では、ceph-mgrが構築されていませんでした。気になってたのですが、動いてたのでほったらかしていました。

```shell-session:
# ceph health
HEALTH_WARN no active mgr
```

色々調べると、ceph-mgrのサービス自体がなかったので、作業自体が抜けていたようです。
ceph-mgrはコマンド1つで構築できるようです。

```shell-session:chinoで実行
# ceph-deploy mgr create chino
```

状態を再度確認すると、まだ警告が出ていました。

```shell-session:
# ceph health
HEALTH_WARN application not enabled on 1 pool(s)
```

「poolで有効なアプリケーションが無い」というものです。
これも色々調べて以下のコマンドを実施して解消しました。

```shell-session:chinoで実行
# ceph osd pool application enable kubernetes rbd
enabled application 'rbd' on pool 'kubernetes'
```

コマンド最後の「アプリケーション名」は、[ドキュメント](https://docs.ceph.com/en/latest/rados/operations/pools/#associate-pool-to-application)によると、「cephfs」「rbd」「rgw」のどれかを指定するようです。

「rbd」という部分で「おや？」と思ったのですが、ドキュメントでは「RBDで使う場合には初期化が必要です」と書かれています。つまり、「poolの作成」で、[ここ](https://docs.ceph.com/en/latest/rbd/rados-rbd-cmds/#create-a-block-device-pool)に書かれている初期化コマンドを実施しなければならなかったはずです。

```shell-session:
# rbd pool init kubernetes
```

※ elasticsearchのデータを保存していたのですが、「まぁ壊れても...」と思って実行しました。...特に致命的なエラーも出ませんでした。が、実行は自己責任でお願いします。

とりあえず、これでヘルスチェックは問題無い状態になりました。

```shell-session:
# ceph health
HEALTH_OK
```

# Ceph Dashboard (追記：2020/11/4)
ブラウザからCephの状態が確認できる便利なもののようです。以下のコマンドで有効になりました。

```shell-session:
# ceph mgr module enable dashboard
```

確認は以下のコマンドで...

```shell-session:
# ceph mgr module ls
{
    "enabled_modules": [
        "balancer",
        "dashboard",
        "restful",
        "status"
    ],
    "disabled_modules": [
        "influx",
        "localpool",
        "prometheus",
        "selftest",
        "zabbix"
    ]
}
```

アクセス先がわからなかったのですが、以下のコマンドで確認できるようです。
まぁ、「netstat -anpl | grep ceph」でLISTENしているポートにアクセスすれば良かっただけなのですが...

```shell-session:
# ceph mgr services
{
    "dashboard": "http://chino.rabbithouse.net:7000/"
}
```

無事にブラウザから確認できました。(見方や使い方はまだよくわかってません)

![スクリーンショット 2020-11-04 15.29.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410957/7fee806d-4d44-28e5-7ed9-fea26683a12d.png)

# おわりに

どうやら、ceph-deployでの構築は非推奨で、ROOKが推奨のようです。(https://docs.ceph.com/en/latest/install/)
しかし、ここまでできればROOK(operator)で構築するタイプも、なんとかコンテナイメージを変えて動かせられるのではないかと考えています。その方が、OpenShift Container Storageの勉強にもなりそうなので...(ただ、重くなるんだろうなぁ...)
今回、色んな方の資料を参考にさせてもらったのですが、RedHat時代のYuryuさんの資料もあり、「こんな昔にCephやってたのかぁ」とびっくりしました。個人的に尊敬している人の一人で、「Linux Kernel Updates」の時からお世話になってます。

最後の性能面の結果が「？」という部分がありますが...まぁ性能を考えるならラズパイ使わないですし、元々機能としてCSIが使いたかったので、結果は満足しています。これでやっと、永続ストレージを使うpodでhostPathとはおさらばして冗長化ができます。