Rookの紹介 ~ver 1.1の新機能 PVC-Based Clusterを試す~

# はじめに
Rookは「Cloud Native Storage for Kubernetes」を実現するために開発されているストレージオーケストレーターです。先日最新バージョンであるver 1.1が公開されましたが、その中でも特に注目された機能である「PVC-Based Cluster」について紹介したいと思います。

# Cloud Native Storageとは
KubernetesはVolume Pluginを利用してストレージを抽象的に扱い、Kubernetes上のアプリケーションに対してストレージを提供します。しかしKubernetesでストレージを利用する際の課題も存在し、それはKubernetesが云々というよりもストレージ自体の抱える課題です。

例えばオンプレミスのストレージを利用する場合、可搬性や拡張性に制限のある場合があります。またストレージを管理者が管理・維持する必要もあり、デプロイする際の手間も発生します。
クラウドを利用すれば、上記課題をある程度は解決することができますが、ベンダーロックイン（他ベンダーへの切り替え時に発生する金額的・技術的コスト）の問題が発生します。
またオンプレミス・クラウド双方で問題となるのが運用です。監視・メンテナンス・トラブルシューティングなどを日々行い、必要に応じてパッチのリリースも行います。このような運用の課題は、その過程が単純であれ複雑であれ、シンプルに管理するべきものです。

![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177048/801f31e8-2554-f1ae-cd16-151c05e1c347.png)

※KubeCon+CloudNativeCon NA 2019 スライドより[(動画リンク)](https://www.youtube.com/watch?v=f3Wyk968VR8)

Cloud Native Storageとは、Cloud Nativeな世界を実現するために、ストレージとしてどのような姿であるべきか、を定義したようなものです。[StorageOSの公開する記事](https://storageos.com/storageos-vision-cloud-native-storage-todays-modern/)では、以下のようなポイントを挙げています。

* **アプリケーション中心**：ホストOSとストレージは疎結合であるべきであり、ストレージがアプリケーションを追従する。
* **プラットフォームに依存しない**：特定のプラットフォームなどに依存せず、アプリケーションの要求に応じてスケーリングする。
* **宣言的構成**：ストレージは宣言的リソースとして管理され、アプリケーションと合わせてデプロイされる。
* **APIドリブン・自己管理**：API経由で管理・操作され、オーケストレーターと統合して利用される
* **アジャイル**：ストレージは環境の変化に応じて動的に変化し、アプリケーソンの移動・要求するストレージサイズの変化に対応する。
* **初期段階からセキュアに利用できる**：暗号化・RBACを利用し、別製品を利用せずにセキュアな環境を構築する。
* **効率的**：分散環境において期待される性能要求を満たし、最小限のリソースで効率的にスケールする。
* **常に利用可能**：高可用性・耐久性・データの一貫性を保証し、アプリケーションと独立してデータ損失から回復する。

# Rookとは
[Rook](https://rook.io/)とは、上に挙げたCloud Native Storageな世界を実現するために開発されたソフトウェアの一つと言って良いと思います。Rookは**Cloud Native Storage for Kubernetes**と打ち出しており、Kubernetesの機能を利用し、多様なストレージサービスをCloud Nativeな環境に統合する、ストレージオーケストレーションツールになります。CNCFではIncubating Projectとして登録されています。

開発当初は[Ceph](https://ceph.io/)をベースにCloud Native向けにデザインされた分散ストレージシステムと紹介されていたり、Rookの開発者の多くはCephのコントリビューターも含まれていたりと、Cephとの関連性が強いソフトウェアですが、ver 0.8あたりからCeph以外のストレージプラットフォームも追加され、ver 1.1では[EdgeFSがstable版として利用](https://medium.com/edgefs/kubernetes-rook-edgefs-1-1-released-452799283fce)可能になりました。

Rookの機能としては以下のようなものが挙げられます。

* ストレージプラットフォームのデプロイと管理の自動化
  * ストレージプラットフォームのデプロイ簡便化
  * ストレージの構成管理（yamlによる管理）
  * アップグレード簡便化
  * 監視（Prometheus向けメトリクス）
* 複数のストレージプロバイダーのサポート
  * 複数のストレージプロバイダーをCloud-Native環境に統合するフレームワーク
  * 安定版: Ceph, EdgeFS
  * α版: Cassandra, CockroachDB, NFS, YugabyteDB, Apache Ozone(ver 1.2から)
* Kubernetes機能を利用した各機能
  * コンポーネントの自動復旧
  * スケール

またRookの構成は以下の通りです。RookはストレージプロバイダーごとにOperatorを作成し、デプロイや運用自動化をもたらします。またRook Discoverはノード上のストレージデバイスを監視し、デバイスが追加された際は自動的にCephクラスターに追加します。
ちなみにver 1.1からはCeph CSI Driverがデフォルトで利用され、Rook Agentは利用されません。ver 1.0まではRook Agentがデフォルトで設定されており、FlexVolume Driverを構成しています。将来的にはCSI Driverの機能拡張に従い、Rook Agentはdeprecateされる予定のようです。

![rook-architecture](https://rook.io/docs/rook/v1.1/media/rook-architecture.png)

![kubernetes](https://rook.io/docs/rook/v1.1/media/kubernetes.png)


# PVC-Based Clusterとは
Rookは2019年9月中旬にver 1.1が公開され、Cephに関連する複数の新機能が発表されました。その中で、PVC-Based Clusterというものがあります。

これまでRook-Cephを利用する場合、Ceph Monitor DaemonやOSDをPodとしてデプロイする際、host pathを指定する必要がありました (Host-Based Cluster)。これによりCephクラスターに利用できるのはKubernetesノードにアタッチされたストレージのみとなり、Podとノードとの結びつきが強くなってしまいます。その結果として柔軟性に欠けたストレージクラスターとなり、アプリケーションの動向に応じてストレージが追従する、というCloud Native Storageのコンセプトを実現するのが難しくなってしまいます。

一方、PVC-Based Clusterは、文字どおりPersistentVolumeClaimsを利用してCeph Monitor Daemon、OSDをデプロイできます。これによりKubernetesノードから独立したストレージデバイスも利用できるため、Podとノードとの結びつきが弱まります。またこの機能は[KubernetesのRaw Block Volume機能](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#raw-block-volume-support)を利用しており、利用するストレージデバイスがこれに対応している必要があります。

![01 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177048/1665af20-2bf2-def9-b2be-d5a927f7da2d.png)

PVC-Based ClusterはRookの新機能の中でも注目されている機能であり、先日開催された[KubeCon+CloudNativeCon NA 2019 (動画リンク)](https://www.youtube.com/watch?v=f3Wyk968VR8)では、PVC-Based Clusterがもたらすメリットについて紹介しています。PVC-Based Clusterを利用してCephクラスターをクラウド上に配置することで、クラウドプロバイダーの欠点を補い、以下のようなことが実現できます。

* **AZをまたいだストレージの利用**：異なるAZに配置されたストレージは同一のVMにアタッチすることができませんが、PVC-Baseの場合は各ストレージにアクセスできれば利用可能となります。
* **フェイルオーバー時間の短縮**：VMにストレージをアタッチした場合と比べ、VMがダウンしてからの復旧時間がより短縮されます。
* **ノードあたりのPV数の制限を超える**：AWS EC2などでは、VMあたりにアタッチできるデバイスの数は制限されています。PVC-Baseの場合はそのような制限はなく、結果的により巨大なサイズのストレージクラスターを構築できます。

![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177048/c54637fd-cf94-ec5b-ab1a-a3687eaa6088.png)

※KubeCon+CloudNativeCon NA 2019 スライドより[(動画リンク)](https://www.youtube.com/watch?v=f3Wyk968VR8)


# PVC-Based Clusterを試す
ここからPVC-Based Clusterを試してみます。基本的な構築方法は[Rookの公式ドキュメント](https://rook.io/docs/rook/v1.1/ceph-quickstart.html)に紹介されているので、そちらを見ながら進めます。

## 検証環境
今回は[Azure Kubernetes Service (AKS)](https://docs.microsoft.com/ja-jp/azure/aks/intro-kubernetes)を利用して検証します。マネージドなKubernetesを利用することで、初期状態からCloud Providerが有効であったり、Azure DiskをprovisionerとするStorage Classが利用できたりと、検証するには都合のいい状態が作れます。なお、今回の操作はすべてAzure Cloud Shellで行っています。

* Kubernetes version: 1.14.8
* Node数: 3
* Node size: Standard_DS2_v2
* Network Configuration: Basic (Kubenetを利用)
* region: 西日本

## Rookのデプロイ
はじめにRookを利用するためのリソースをデプロイします。この辺りはHost-Basedの場合とも共通ですが、まずはCRD、RBACなどが定義された```common.yaml```をデプロイします。

```
$ git clone https://github.com/rook/rook.git
$ cd rook/cluster/examples/kubernetes/ceph/
$ kubectl apply -f common.yaml
namespace/rook-ceph created
customresourcedefinition.apiextensions.k8s.io/cephclusters.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/cephfilesystems.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/cephnfses.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/cephobjectstores.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/cephobjectstoreusers.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/cephblockpools.ceph.rook.io created
customresourcedefinition.apiextensions.k8s.io/volumes.rook.io created
customresourcedefinition.apiextensions.k8s.io/objectbuckets.objectbucket.io created
customresourcedefinition.apiextensions.k8s.io/objectbucketclaims.objectbucket.io created
clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-object-bucket created
clusterrole.rbac.authorization.k8s.io/rook-ceph-cluster-mgmt created
clusterrole.rbac.authorization.k8s.io/rook-ceph-cluster-mgmt-rules created
role.rbac.authorization.k8s.io/rook-ceph-system created
clusterrole.rbac.authorization.k8s.io/rook-ceph-global created
clusterrole.rbac.authorization.k8s.io/rook-ceph-global-rules created
clusterrole.rbac.authorization.k8s.io/rook-ceph-mgr-cluster created
clusterrole.rbac.authorization.k8s.io/rook-ceph-mgr-cluster-rules created
clusterrole.rbac.authorization.k8s.io/rook-ceph-object-bucket created
serviceaccount/rook-ceph-system created
rolebinding.rbac.authorization.k8s.io/rook-ceph-system created
clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-global created
serviceaccount/rook-ceph-osd created
serviceaccount/rook-ceph-mgr created
serviceaccount/rook-ceph-cmd-reporter created
role.rbac.authorization.k8s.io/rook-ceph-osd created
clusterrole.rbac.authorization.k8s.io/rook-ceph-osd created
clusterrole.rbac.authorization.k8s.io/rook-ceph-mgr-system created
clusterrole.rbac.authorization.k8s.io/rook-ceph-mgr-system-rules created
role.rbac.authorization.k8s.io/rook-ceph-mgr created
role.rbac.authorization.k8s.io/rook-ceph-cmd-reporter created
rolebinding.rbac.authorization.k8s.io/rook-ceph-cluster-mgmt created
rolebinding.rbac.authorization.k8s.io/rook-ceph-osd created
rolebinding.rbac.authorization.k8s.io/rook-ceph-mgr created
rolebinding.rbac.authorization.k8s.io/rook-ceph-mgr-system created
clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-mgr-cluster created
clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-osd created
rolebinding.rbac.authorization.k8s.io/rook-ceph-cmd-reporter created
podsecuritypolicy.policy/rook-privileged created
clusterrole.rbac.authorization.k8s.io/psp:rook created
clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-system-psp created
rolebinding.rbac.authorization.k8s.io/rook-ceph-default-psp created
rolebinding.rbac.authorization.k8s.io/rook-ceph-osd-psp created
rolebinding.rbac.authorization.k8s.io/rook-ceph-mgr-psp created
rolebinding.rbac.authorization.k8s.io/rook-ceph-cmd-reporter-psp created
serviceaccount/rook-csi-cephfs-plugin-sa created
serviceaccount/rook-csi-cephfs-provisioner-sa created
role.rbac.authorization.k8s.io/cephfs-external-provisioner-cfg created
rolebinding.rbac.authorization.k8s.io/cephfs-csi-provisioner-role-cfg created
clusterrole.rbac.authorization.k8s.io/cephfs-csi-nodeplugin created
clusterrole.rbac.authorization.k8s.io/cephfs-csi-nodeplugin-rules created
clusterrole.rbac.authorization.k8s.io/cephfs-external-provisioner-runner created
clusterrole.rbac.authorization.k8s.io/cephfs-external-provisioner-runner-rules created
clusterrolebinding.rbac.authorization.k8s.io/rook-csi-cephfs-plugin-sa-psp created
clusterrolebinding.rbac.authorization.k8s.io/rook-csi-cephfs-provisioner-sa-psp created
clusterrolebinding.rbac.authorization.k8s.io/cephfs-csi-nodeplugin created
clusterrolebinding.rbac.authorization.k8s.io/cephfs-csi-provisioner-role created
serviceaccount/rook-csi-rbd-plugin-sa created
serviceaccount/rook-csi-rbd-provisioner-sa created
role.rbac.authorization.k8s.io/rbd-external-provisioner-cfg created
rolebinding.rbac.authorization.k8s.io/rbd-csi-provisioner-role-cfg created
clusterrole.rbac.authorization.k8s.io/rbd-csi-nodeplugin created
clusterrole.rbac.authorization.k8s.io/rbd-csi-nodeplugin-rules 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/rook-csi-rbd-plugin-sa-psp created
clusterrolebinding.rbac.authorization.k8s.io/rook-csi-rbd-provisioner-sa-psp created
clusterrolebinding.rbac.authorization.k8s.io/rbd-csi-nodeplugin created
clusterrolebinding.rbac.authorization.k8s.io/rbd-csi-provisioner-role created
```

次にRook Operatorを定義する```operator.yaml```をデプロイします。

```
# Operatorデプロイ

$ kubectl apply -f operator.yaml
deployment.apps/rook-ceph-operator created


# デプロイ後の確認

$ kubectl get pods -n rook-ceph
NAME                                  READY   STATUS    RESTARTS   AGE
rook-ceph-operator-65d489f456-zr5gq   1/1     Running   0          106s
rook-discover-2t2s6                   1/1     Running   0          63s
rook-discover-v2vhd                   1/1     Running   0          63s
rook-discover-xfjwm                   1/1     Running   0          63s
```

## Ceph Clusterのデプロイ
続いてCephのクラスターをデプロイします。まずはクラスターを構築する際に利用する```cluster-on-pvc.yaml```を眺めてみます。

```yaml
#################################################################################################################
# Define the settings for the rook-ceph cluster with common settings for a production cluster.
# All nodes with available raw devices will be used for the Ceph cluster. At least three nodes are required
# in this example. See the documentation for more details on storage settings available.

# For example, to create the cluster:
#   kubectl create -f common.yaml
#   kubectl create -f operator.yaml
#   kubectl create -f cluster-on-pvc.yaml
#################################################################################################################

apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph
  namespace: rook-ceph
spec:
  dataDirHostPath: /var/lib/rook
  mon:
    count: 3
    allowMultiplePerNode: false
    # A volume claim template can be specified in which case new monitors (and
    # monitors created during fail over) will construct a PVC based on the
    # template for the monitor's primary storage. Changes to the template do not
    # affect existing monitors. Log data is stored on the HostPath under
    # dataDirHostPath. If no storage requirement is specified, a default storage
    # size appropriate for monitor data will be used.
    volumeClaimTemplate:
      spec:
        storageClassName: gp2
        resources:
          requests:
            storage: 10Gi
  cephVersion:
    image: ceph/ceph:v14.2.4-20190917
    allowUnsupported: false
  dashboard:
    enabled: true
    ssl: true
  network:
    hostNetwork: false
  storage:
    storageClassDeviceSets:
    - name: set1
      # The number of OSDs to create from this device set
      count: 3
      # IMPORTANT: If volumes specified by the storageClassName are not portable across nodes
      # this needs to be set to false. For example, if using the local storage provisioner
      # this should be false.
      portable: true
      # Since the OSDs could end up on any node, an effort needs to be made to spread the OSDs
      # across nodes as much as possible. Unfortunately the pod anti-affinity breaks down
      # as soon as you have more than one OSD per node. If you have more OSDs than nodes, K8s may
      # choose to schedule many of them on the same node. What we need is the Pod Topology
      # Spread Constraints, which is alpha in K8s 1.16. This means that a feature gate must be
      # enabled for this feature, and Rook also still needs to add support for this feature.
      # Another approach for a small number of OSDs is to create a separate device set for each
      # zone (or other set of nodes with a common label) so that the OSDs will end up on different
      # nodes. This would require adding nodeAffinity to the placement here.
      placement:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - rook-ceph-osd
                - key: app
                  operator: In
                  values:
                  - rook-ceph-osd-prepare
              topologyKey: kubernetes.io/hostname
      resources:
      #   limits:
      #     cpu: "500m"
      #     memory: "4Gi"
      #   requests:
      #     cpu: "500m"
      #     memory: "4Gi"
      volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          resources:
            requests:
              storage: 10Gi
          # IMPORTANT: Change the storage class depending on your environment (e.g. local-storage, gp2)
          storageClassName: gp2
          volumeMode: Block
          accessModes:
            - ReadWriteOnce
  disruptionManagement:
    managePodBudgets: false
    osdMaintenanceTimeout: 30
    manageMachineDisruptionBudgets: false
    machineDisruptionBudgetNamespace: openshift-machine-api
```


上記ファイルのうち、```spec.mon```あたりはCeph Monitor Daemonの設定、```spec.storage. storageClassDeviceSets```あたりはCeph OSDの設定になります。デフォルトの設定ではMonitor、OSDともに3つずつデプロイされ、利用されるStorage Classは```gp2```になります。また、Monitorは```spec.mon.allowMultiplePerNode```、OSDは```spec.storage.storageClassDeviceSets.placement```でデプロイするノードの条件を指定できるようです。

今回は以下のように修正した```test-cluster-on-pvc.yaml```を作成し、これを利用します。

```shell

# StorageClassとストレージサイズを変更

$ diff cluster-on-pvc.yaml test-cluster-on-pvc.yaml
30c30
<         storageClassName: gp2
---
>         storageClassName: default
33c33
<             storage: 10Gi
---
>             storage: 20Gi
90c90
<               storage: 10Gi
---
>               storage: 20Gi
92c92
<           storageClassName: gp2
---
>           storageClassName: default
```

それでは```test-cluster-on-pvc.yaml```をデプロイします。

```
# Clusterデプロイ
$ kubectl apply -f test-cluster-on-pvc.yaml
cephcluster.ceph.rook.io/rook-ceph created

# デプロイ後の確認
$ kubectl get pods -n rook-ceph
NAME                                                              READY   STATUS      RESTARTS   AGE
csi-cephfsplugin-2bc5p                                            3/3     Running     0          37m
csi-cephfsplugin-2c4sm                                            3/3     Running     0          37m
csi-cephfsplugin-c2jt4                                            3/3     Running     0          37m
csi-cephfsplugin-provisioner-5dfb8fcdd5-dp9mb                     4/4     Running     0          37m
csi-cephfsplugin-provisioner-5dfb8fcdd5-xlsll                     4/4     Running     0          37m
csi-rbdplugin-hpvvm                                               3/3     Running     0          37m
csi-rbdplugin-jn7v5                                               3/3     Running     0          37m
csi-rbdplugin-mjwj5                                               3/3     Running     0          37m
csi-rbdplugin-provisioner-57dfcf95c5-2jtnh                        5/5     Running     0          37m
csi-rbdplugin-provisioner-57dfcf95c5-hb98k                        5/5     Running     0          37m
rook-ceph-crashcollector-aks-agentpool-25377305-vmss0000002vwjd   1/1     Running     0          32m
rook-ceph-crashcollector-aks-agentpool-25377305-vmss000001rvqj6   1/1     Running     0          35m
rook-ceph-crashcollector-aks-agentpool-25377305-vmss00000264cjn   1/1     Running     0          34m
rook-ceph-mgr-a-6f78c8c74d-5l7g8                                  1/1     Running     0          31m
rook-ceph-mon-a-6d58c8fff7-p9xrm                                  1/1     Running     0          35m
rook-ceph-mon-b-7d4cfbc59d-ndxxb                                  1/1     Running     0          34m
rook-ceph-mon-c-7c687b7bbc-bmh8p                                  1/1     Running     0          32m
rook-ceph-operator-65d489f456-nzr2b                               1/1     Running     0          40m
rook-ceph-osd-0-5b8c65dbb-9r6l4                                   1/1     Running     0          23m
rook-ceph-osd-1-587c8f4b95-cmx2j                                  1/1     Running     0          21m
rook-ceph-osd-2-6db9fb7947-sgbh6                                  1/1     Running     0          21m
rook-ceph-osd-prepare-set1-0-data-f2hbj-4l6sx                     0/1     Completed   0          31m
rook-ceph-osd-prepare-set1-1-data-n6hq5-w6qd6                     0/1     Completed   0          31m
rook-ceph-osd-prepare-set1-2-data-5c57b-f9dbk                     0/1     Completed   0          31m
rook-discover-7fg8l                                               1/1     Running     0          39m
rook-discover-vdjhm                                               1/1     Running     0          39m
rook-discover-zc6x7                                               1/1     Running     0          39m

$ kubectl get pvc -n rook-ceph
NAME                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
rook-ceph-mon-a     Bound    pvc-afb4db9c-184d-11ea-9b39-16d8ab163d04   20Gi       RWO            default        48m
rook-ceph-mon-b     Bound    pvc-b8c8330b-184d-11ea-9b39-16d8ab163d04   20Gi       RWO            default        48m
rook-ceph-mon-c     Bound    pvc-c1c37a06-184d-11ea-9b39-16d8ab163d04   20Gi       RWO            default        48m
set1-0-data-f2hbj   Bound    pvc-6eb3ca6e-184e-11ea-9b39-16d8ab163d04   20Gi       RWO            default        43m
set1-1-data-n6hq5   Bound    pvc-6eb9cb4d-184e-11ea-9b39-16d8ab163d04   20Gi       RWO            default        43m
set1-2-data-5c57b   Bound    pvc-6ebe6dbc-184e-11ea-9b39-16d8ab163d04   20Gi       RWO            default        43m

$ kubectl get pv -n rook-ceph
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                         STORAGECLASS   REASON   AGE
pvc-6eb3ca6e-184e-11ea-9b39-16d8ab163d04   20Gi       RWO            Delete           Bound    rook-ceph/set1-0-data-f2hbj   default                 43m
pvc-6eb9cb4d-184e-11ea-9b39-16d8ab163d04   20Gi       RWO            Delete           Bound    rook-ceph/set1-1-data-n6hq5   default                 43m
pvc-6ebe6dbc-184e-11ea-9b39-16d8ab163d04   20Gi       RWO            Delete           Bound    rook-ceph/set1-2-data-5c57b   default                 43m
pvc-afb4db9c-184d-11ea-9b39-16d8ab163d04   20Gi       RWO            Delete           Bound    rook-ceph/rook-ceph-mon-a     default                 48m
pvc-b8c8330b-184d-11ea-9b39-16d8ab163d04   20Gi       RWO            Delete           Bound    rook-ceph/rook-ceph-mon-b     default                 48m
pvc-c1c37a06-184d-11ea-9b39-16d8ab163d04   20Gi       RWO            Delete           Bound    rook-ceph/rook-ceph-mon-c     default                 48m
```

無事にデプロイが完了し、Ceph Monitor Daemon、OSDとしてPodが立ち上がりました。

またAzureではAzure Diskが作成されたことも確認できます。

![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177048/0b7115f3-f25c-fea3-a512-ffa9662cbe79.png)

![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177048/ab12da5b-4b24-5058-386d-27fe2253ece2.png)


## StorageClass/CephBlockPoolのデプロイ
ここまででCephクラスターが構築されたので、あとはCeph上で利用するストレージプールとなる```CephBlockPool```をデプロイします。

```shell
# StorageClass/CephBlockPoolデプロイ

$ cd csi/rbd
$ $ kubectl apply -f storageclass.yaml
cephblockpool.ceph.rook.io/replicapool created
storageclass.storage.k8s.io/rook-ceph-block created

# デプロイ後確認

$ kubectl get sc
NAME                PROVISIONER                  AGE
default (default)   kubernetes.io/azure-disk     68m
managed-premium     kubernetes.io/azure-disk     68m
rook-ceph-block     rook-ceph.rbd.csi.ceph.com   2m

$ kubectl get cephblockpool.ceph.rook.io -n rook-ceph
NAME          AGE
replicapool   2m32s
```

なお```storageclass.yaml```は以下の通りです。provisionerを```rook-ceph.rbd.csi.ceph.com```に指定しています。

```storageclass.yaml
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
  name: replicapool
  namespace: rook-ceph
spec:
  failureDomain: host
  replicated:
    size: 3
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: rook-ceph-block
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
    # clusterID is the namespace where the rook cluster is running
    # If you change this namespace, also change the namespace below where the secret namespaces are defined
    clusterID: rook-ceph

    # Ceph pool into which the RBD image shall be created
    pool: replicapool

    # RBD image format. Defaults to "2".
    imageFormat: "2"

    # RBD image features. Available for imageFormat: "2". CSI RBD currently supports only `layering` feature.
    imageFeatures: layering

    # The secrets contain Ceph admin credentials. These are generated automatically by the operator
    # in the same namespace as the cluster.
    csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
    csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
    csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
    csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
    # Specify the filesystem type of the volume. If not specified, csi-provisioner
    # will set default as `ext4`.
    csi.storage.k8s.io/fstype: ext4
# uncomment the following to use rbd-nbd as mounter on supported nodes
#mounter: rbd-nbd
reclaimPolicy: Delete
```

## テスト用Podのデプロイ
上記手順により、Cephのストレージプールまで構築されました。最後にストレージプールを利用できることを確認するため、テスト用のPodをデプロイします。
利用するPod、PersistentVolumeClaimsはそれぞれ以下の通り定義されています。

```pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: rbd-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: rook-ceph-block
```

```pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: csirbd-demo-pod
spec:
  containers:
   - name: web-server
     image: nginx
     volumeMounts:
       - name: mypvc
         mountPath: /var/lib/www/html
  volumes:
   - name: mypvc
     persistentVolumeClaim:
       claimName: rbd-pvc
       readOnly: false
```


```shell
# PersistentVolumeClaimsデプロイ

$ kubectl apply -f pvc.yaml
persistentvolumeclaim/rbd-pvc created

$ kubectl get pvc
NAME      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE
rbd-pvc   Bound    pvc-63072b5e-1855-11ea-9b39-16d8ab163d04   1Gi        RWO            rook-ceph-block   17s

# Podデプロイ
$ kubectl apply -f pod.yaml
pod/csirbd-demo-pod created

$ kubectl get pods
NAME              READY   STATUS    RESTARTS   AGE
csirbd-demo-pod   1/1     Running   0          28s
```

以上の通りPodも正常に動作しています。

# 最後に
Rookはストレージオーケストレーターとして位置付けられていますが、現状ではCephを利用することを念頭に開発されているように感じます。そのため、Rookを導入するか否かを判断する基準としては、既にKubernetes上でCephを構築している、あるいはこれからCephを利用することを検討している現場が向いているのでは、と考えています。またRookについて詳しく知ろうと思うと、Cephのことを十分理解していないと難しいでしょう。RookはあくまでOperatorとしての機能が強く、実際はその上に乗ったストレージプロバイダーがストレージとして機能します。変な話に聞こえるかもしれませんが、 **Rookを扱うにはまずCephを扱えるようになるべきだ**、というのが個人的な感想です。

また日本国内でも少しずつ注目を集めているRookですが、[Rookを本番環境で利用する例が発表されています](https://blog.cybozu.io/entry/2019/12/03/114746)。これからさらに実例も増え、開発が活発になることが予想されますので、引き続き注目していきたいと思います。

# 参考リンク
* [Rook公式ドキュメント](https://rook.io/)
* [Rook v1.1: Accelerating Storage Providers](https://blog.rook.io/rook-v1-1-accelerating-storage-providers-5b9e8d5901d8)
* [Rook v1.1: Ceph CSI, Bucket Provisioning, External Clusters, and much more!](https://ceph.io/community/rook-v1-1-ceph-features/)
* [Youtube - Rook: Cloud-Native Storage Orchestration (Introduction and Deep Dive)](https://www.youtube.com/watch?v=f3Wyk968VR8)