Rook v1.0で読み解くCSI

# TL;DR

- CSIはKubernetesなどからストレージを扱うインターフェースのこと。
- Kubernetes CSI Developer Documentationに実装すべき事項、サンプルが整理されている。
- Rook v1.0＋Ceph CSIも上記に準拠して実装されていることを確認する。 


# そもそもCSIとは
CSIはContainer Storage Interfaceの略で、ここからも分かるとおり、Kubernetesだけを対象としたものではなく、コンテナオーケストレーションツールからストレージを扱うインターフェースを定めた規格です。
Kubernetesでは1.13からGAとなっているため、先日書いた[この投稿](DRBD)や[こちら](Rook)でも1.13.4を使って検証を行っています。

CSIがそれ以前のストレージ関連機能と何が違うのか？という観点では、**in-tree**か**out-of-tree**かということが良く語られますので、以下に整理してみました。

- **in-tree**のVolume Plugin
    - Kubernetesのソースツリーに組み込まれる形で開発されているVolume Plugin。
    - 具体的には[kubernetesのgithubリポジトリのここ](https://github.com/kubernetes/kubernetes/tree/master/pkg/volume)にソースがある。
    - 各クラウドベンダのストレージやiSCSIのPlugin等が存在することが確認できる。
    - <font color="blue">リリースをKubernetes本体と揃えなければならないため、開発に制約が生じていた。</font>
- **FlexVolume**
    - CSIより以前から存在する、out-of-tree形式のVolume Plugin。
    - 各ノードにドライバをデプロイし、Kubeletがそれを使ってVolumeをプロビジョニング/アタッチ/デタッチする
    - ソースツリー外での開発が可能となったが、ドライバファイルをルートファイルシステム（具体的には<font color="blue"> /usr/libexec/kubernetes/kubelet-plugins/volume/exec/</font>）に配置する必要がある。
    - 簡単な構成図はこんな感じ。 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54853/e310d2de-d0ba-e54b-344b-08291db97d15.png)
    - CSIがGAとなってもFlexVolumeがなくなるわけではない。
- **CSI**
    - FlexVolumeの制約を回避し、より柔軟にVolume Pluginのデプロイが行えることを目指している。
    - provisionerやattacherがdriverを利用して動的なボリュームのプロビジョニング、コンテナへのアタッチ/デタッチを行う。
    - <font color="blue">構成が複雑となり、Rook v1.0の検証でも指摘したように運用に制約が生じるケースがありそう</font>と個人的には思っている。

# CSIに対応したPluginを開発するには
Rookの話に入る前に、ストレージプロバイダ（ベンダ）がCSI対応のVolume Pluginを開発するというのはどういうことなのかを見てみましょう。

[Kubernetes CSI Developer Documentation](https://kubernetes-csi.github.io/docs/#kubernetes-container-storage-interface-csi-documentation)にCSIの開発例が記述されていますが、それぞれコンテナ化された下記のコンポーネントを用意する必要があります。

≪DaemonSetに含まれるコンテナ≫
- CSI Driver Container：コンテナ形式で提供されるドライバ本体。
- node-driver-registar：ドライバを各ノードのkubeletに登録する。

≪StatefulSetに含まれるコンテナ≫
- CSI Driver Container：コンテナ形式で提供されるドライバ本体。
- external-provisioner：APIサーバと通信してPVCの作成をウォッチし、対応するボリュームのプロビジョニングを行う。PVC削除時にはボリュームの削除も担う。
- external-attacher：APIサーバと通信してボリュームのアタッチを行う。
- cluster-driver-registar：CSIDriver Objectを作成して、ドライバをk8sクラスタに登録する。

公式ドキュメントには以下のような図もあげられており、ストレージベンダが実際に開発するのはドライバ本体のみで、それ以外はKubernetesの開発チームが提供することも明示されています。
<img src="https://github.com/kubernetes/community/blob/master/contributors/design-proposals/storage/container-storage-interface_diagram1.png?raw=true">

# RookでCeph CSIを使うケースで考える
さて、ここまで長々とCSIの意義・仕様を見てきましたが、実際にRook v1.0でCSIを利用した際にどのようにCSI関連のコンテナが配置されているかを見てみましょう。

### Rook v1.0によるCeph Clusterの構築ができた段階
先日の記事で書いた[Rook v1.0によるCeph Clusterの構築とoperator-with-csiの適用が終わった時点](https://qiita.com/tzkoba/items/4baea914e22e279fab9b#%E6%89%8B%E9%A0%86%EF%BC%92operator-with-csi%E3%81%AE%E9%81%A9%E7%94%A8)で、CSIの各コンテナまでを詳細に書くと以下のような構成になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54853/d2f113d1-0c63-8996-bd37-31434c3ecaa0.png)
※左側workerがCSIのprovisionerなどが含まれるStatefulSetのPodがデプロイされたノード、右側workerがDaemonSetのPodのみがデプロイされたノードです。真ん中のノードは右側と同一構成となるため、これ以降では省略します。
※RookもCeph CSIによるプロビジョニング・アタッチには関与しません。それらも上図から除外しています。

### PVCの作成＝CSI Provisionerの動作
以降で先日書いた[CSIの動的プロビジョニング/アタッチを試す](https://qiita.com/tzkoba/items/4baea914e22e279fab9b#%E6%89%8B%E9%A0%86%EF%BC%93csi%E3%81%AE%E5%8B%95%E7%9A%84%E3%83%97%E3%83%AD%E3%83%93%E3%82%B8%E3%83%A7%E3%83%8B%E3%83%B3%E3%82%B0%E3%82%A2%E3%82%BF%E3%83%83%E3%83%81%E3%82%92%E8%A9%A6%E3%81%99)の中で、実際にCeph CSIで何が行われているかを見ていきます。

先日記事では以下のような手順を書きました。

> 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を作成

この中でCeph CSIと関連するのは5.と6.になります。
5.でPVCを作成すると、下図のようにCeph上にRBDのボリュームが作成され、PVとバインドされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54853/5da81042-eb48-b831-f731-4d4392de7905.png)

実際にはPVCから参照されるStorageClassにStorage Provisionerが明記されているわけですが、図中では省略しています。

### PodでPVをマウント
作成したPVCを利用するPodがデプロイされた時の動きを次に見てみましょう。
先ほどの`6. 5.のPVCを作成するPodを作成`の部分です。こちらも図で見てみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54853/61cf28d4-b112-b9f8-2ab8-2030bebf52f1.png)

attacherがrbdpluginを使用してPodが稼働するノードでRBDボリュームのアタッチをしています。
ここまででCeph CSIによるプロビジョニングとアタッチは完了です。

# まとめ
如何だったでしょうか。
CSIの各コンポーネントの配置、そしてそれを利用したプロビジョニング・アタッチはかなり複雑な処理であることがわかります。

また、その複雑さを説明した資料は日本語で殆ど見つからないため、内部の動きを追うことは難しいといえます。興味が出てきたという方は、先ほど提示したCSIデベロッパー向けドキュメントや[こちら](https://events.linuxfoundation.jp/wp-content/uploads/2017/12/Internals-of-Docking-Storage-with-Kubernetes-Workloads-Dennis-Chen-Arm.pdf)の資料をご覧下さい。

よろしくお願いします。