ROOK Ceph v1.1 -> v1.2 アップデート が本当にうまくいくか、試してみました

#はじめに
ROOK を使って Ceph を構築すると普段の運用はとても便利になりますが、代わりに ROOK 自体のアップデートが必要になる可能性があります。
今回の検証では、無事に v1.1 -> v1.2 へのアップデートが実現できるのか、ARM64 版のクラスタを用いてテストした結果をまとめます。

- 参考情報
  - [Rook-Ceph Upgrades](https://rook.io/docs/rook/v1.2/ceph-upgrade.html)

#アップデート手順
アップデートの手順についてはとても単純で、3つの yaml ファイルを kubectl コマンドに流し込むだけで完了します。
ただし、次の点について事前に確認を済ませておく必要があります。

- Cephクラスタが正常に稼働していない場合は、先に問題を解決すること
- fuse client、または rbd-nbd を使用していないこと
- CSI Driver に Custom Image を使用している場合は、別途アップデートが必要

##ROOK v1.2 のダウンロード
はじめに、ROOK v1.2 をダウンロードします。

```bash
# 旧バージョンのディレクトリをバックアップします
mv rook rook.v.1.1.2

# gitコマンドを用いて rook をダウンロードします
git clone https://github.com/rook/rook
cd rook
git checkout -b v1.2.0 refs/tags/v1.2.0
```

##v1.1で使用していた設定ファイルの v1.2 へのコピー
現在稼働しているクラスタ(v1.1)と v1.2 について、設定の差分を確認するために、現在の設定ファイルを v1.2 のフォルダにもコピーしておきます。

```bash
cd cluster/examples/kubernetes/ceph
cp ~/rook.v1.1.2/cluster/examples/kubernetes/ceph/my-cluster.yaml ./
cp ~/rook.v1.1.2/cluster/examples/kubernetes/ceph/my-operator.yaml ./
```

##Health 状況の確認
クラスタが正常に稼働していることを確認します。k8s または Ceph のクラスタにて、Health に問題が見つかった場合は、予め修復をしておきます。

```bash
export ROOK_SYSTEM_NAMESPACE="rook-ceph"
export ROOK_NAMESPACE="rook-ceph"

# Podが正常に稼働していることを確認します
kubectl -n $ROOK_SYSTEM_NAMESPACE get pods
kubectl -n $ROOK_NAMESPACE get pods

# Ceph が正常に稼働していることを確認します
TOOLS_POD=$(kubectl -n $ROOK_NAMESPACE get pod -l "app=rook-ceph-tools" -o jsonpath='{.items[0].metadata.name}')
kubectl -n $ROOK_NAMESPACE exec -it $TOOLS_POD -- ceph status

# Ceph Pod で稼働している Ceph の version を確認します
POD_NAME=$(kubectl -n $ROOK_NAMESPACE get pod -o custom-columns=name:.metadata.name --no-headers | grep rook-ceph-mon-b)
kubectl -n $ROOK_NAMESPACE get pod ${POD_NAME} -o jsonpath='{.spec.containers[0].image}'

# 稼働している ROOK の 稼働状況および version 状況を一覧する場合は、次のコマンドが使えます
kubectl -n $ROOK_SYSTEM_NAMESPACE get pod -o jsonpath='{range .items[*]}{.metadata.name}{"\n\t"}{.status.phase}{"\t\t"}{.spec.containers[0].image}{"\t"}{.spec.initContainers[0]}{"\n"}{end}' && \
kubectl -n $ROOK_NAMESPACE get pod -o jsonpath='{range .items[*]}{.metadata.name}{"\n\t"}{.status.phase}{"\t\t"}{.spec.containers[0].image}{"\t"}{.spec.initContainers[0].image}{"\n"}{end}'
```

##RBAC および CRD のアップデート
rook-ceph-operator のアップデートに先立って、事前の変更・追加が必要なリソースを kubectl apply コマンドにて適用しておきます。

```bash
kubectl apply -f upgrade-from-v1.1-apply.yaml
```

##CSI のアップデートについて、事前準備
CSI Driver のアップデートに伴い、Driver のプロセスが再起動します。この際に、次のサービスを利用している場合は通信断が発生します。

- fuse
- rbd-nbd

今回はどちらも使用していないので、特に気にせず次のステップに進みます。

##ROOK Operator　のアップデート
rook-ceph-operator のアップデートを実施します。実行するコマンド自体は単純で、使用する image のバージョンの変更を kubectl コマンドで実施します。

```bash
kubectl -n $ROOK_SYSTEM_NAMESPACE set image deploy/rook-ceph-operator rook-ceph-operator=rook/ceph:v1.2.0
```

##アップデート完了まで待機
rook-ceph-operator のアップデートが開始すると、関連するコンテナが逐次アップデートされます。
次のコマンドを実行することで、各 Container のアップデート状況が確認できます。

```bash
watch --exec kubectl -n $ROOK_NAMESPACE get deployments -l rook_cluster=$ROOK_NAMESPACE -o jsonpath='{range .items[*]}{.metadata.name}{"  \treq/upd/avl: "}{.spec.replicas}{"/"}{.status.updatedReplicas}{"/"}{.status.readyReplicas}{"  \trook-version="}{.metadata.labels.rook-version}{"\n"}{end}'
```

##ROOK-Ceph CRD のアップデート (CSI Driver Container のバージョンを指定していた場合)
通常であれば以上で作業は完了となりますが、CSI Driver のバージョンを指定していた場合 (ARM64対応等)は、動作に互換性がない模様で正しく稼働しませんでした。

Private Repositoryを使用したい、または ARM64 を使用している等、特別な要件がある場合、予め ROOK-Ceph v1.2 が default で使用しているバージョンにてコンテナイメージを build / push しておきます。

- ceph/ceph-csi
  - v1.2.2
- kubernetes-csi/external-provisioner
  - v1.4.0
- kubernetes-csi/external-snapshotter
  - v1.2.2

次に、Deployment (rook-ceph-operator) の ROOK_CSI_CEPH_*_IMAGE について、次のように変更します。

```bash
kubectl -n rook-ceph edit deploy rook-ceph-operator
```

```yaml
      - name: ROOK_CSI_CEPH_IMAGE
        value: "sogabe/cephcsi:v1.2.2"
      - name: ROOK_CSI_REGISTRAR_IMAGE
        value: "sogabe/csi-node-driver-registrar:v1.1.0"
      - name: ROOK_CSI_PROVISIONER_IMAGE
        value: "sogabe/csi-provisioner:v1.4.0"
      - name: ROOK_CSI_SNAPSHOTTER_IMAGE
        value: "sogabe/csi-snapshotter:v1.2.2"
      - name: ROOK_CSI_ATTACHER_IMAGE
        value: "sogabe/csi-attacher:v1.2.0"
```

CSI Driver コンテナの作成方法については、次のような手順で実施できます。

```bash
mkdir ~/ceph-csi-build
cd ~/ceph-csi-build
wget https://dl.google.com/go/go1.13.3.linux-arm64.tar.gz
sudo tar -C /usr/local -xzf go1.13.3.linux-arm64.tar.gz
export PATH=$PATH:/usr/local/go/bin
mkdir $HOME/golang
cd $HOME/golang
export GOPATH=$HOME/golang
export PATH=$GOPATH/bin:$PATH
go get -d github.com/ceph/ceph-csi
cd $GOPATH/src/github.com/ceph/ceph-csi
git checkout -b v1.2.2 refs/tags/v1.2.2
docker login
make image-cephcsi CSI_IMAGE_NAME=sogabe/cephcsi
make push-image-cephcsi CSI_IMAGE_NAME=sogabe/cephcsi

go get -d github.com/kubernetes-csi/external-provisioner
cd $GOPATH/src/github.com/kubernetes-csi/external-provisioner
git checkout -b v1.4.0 refs/tags/v1.4.0
make push REGISTRY_NAME=sogabe

go get -d github.com/kubernetes-csi/external-snapshotter
cd $GOPATH/src/github.com/kubernetes-csi/external-snapshotter
git checkout -b v1.2.2 refs/tags/v1.2.2
vi release-tools/build.make
# image の architecture を arm64 に指定するため、option --platform arm を追加
# docker の version が新しくないと error になります
make push REGISTRY_NAME=sogabe
```

全てのアップデートが完了すると、確認コマンドの実行結果が次のようになります。

```bash
watch --exec kubectl -n $ROOK_NAMESPACE get deployments -l rook_cluster=$ROOK_NAMESPACE -o jsonpath='{range .items[*]}{.metadata.name}{"  \treq/upd/avl: "}{.spec.replicas}{"/"}{.status.updatedReplicas}{"/"}{.status.readyReplicas}{"  \trook-version="}{.metadata.labels.rook-version}{"\n"}{end}'

rook-ceph-mgr-a         req/upd/avl: 1/1/1      rook-version=v1.2.0
rook-ceph-mon-a         req/upd/avl: 1/1/1      rook-version=v1.2.0
rook-ceph-mon-b         req/upd/avl: 1/1/1      rook-version=v1.2.0
rook-ceph-mon-d         req/upd/avl: 1/1/1      rook-version=v1.2.0
rook-ceph-osd-0         req/upd/avl: 1/1/1      rook-version=v1.2.0
rook-ceph-osd-2         req/upd/avl: 1/1/1      rook-version=v1.2.0
rook-ceph-osd-3         req/upd/avl: 1/1/1      rook-version=v1.2.0
rook-ceph-rgw-my-store-a        req/upd/avl: 1/1/1      rook-version=v1.2.0
```

##ROOK-Ceph CRD のアップデート
ROOK-Ceph のアップデート完了後に、CRD のアップデートを実施します。

```bash
kubectl apply -f upgrade-from-v1.1-crds.yaml
```

##ROOK ceph の動作テスト
アップデートが完了したら、Custom Resource を用いた RBD Image の作成やPOD作成等を実施して、問題なくクラスタが稼働していることを確認します。

##まとめ
今回、ARM64 のクラスタを用いて ROOK Ceph のアップデートが問題なくできるか検証をしてみました。結果としては、少しだけハマり所はあったものの、簡単にアップデートが実現できました。
k8s で稼働しているアプリはアップデートが簡単にできるとは良く言われますが、実のところは Operator 側が煩雑な作業を引き受けているからこそ実現できている、ということも垣間みえてきます。

自分で Operator や CRD を実装して運用作業をガンガン自動化してやるぜ! というモチベーションの高い人たちにとっては、k8s は最強のツールであることがあらためて感じられました。
ROOK を使うと、そんな人たちが作ったノウハウの結晶を自分のインフラでも簡単に再現させることができるので、とても面白いですね。