Rook-CephのCephFSを使ってみる

こんにちは
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

この記事では前回作成したRook-Ceph上にCephFSを作成し、podのデータをCephFS上に置き、永続化してみたいと思います。
Rook-Cephの構築は以下をご覧ください。

https://qiita.com/ohtsuka-shota/items/cc45d595f742f3625099

# 用語
## CephFS
個人的にはNFSと近しいイメージを持っています。
とりあえずは、そのようなイメージを持っておけば一旦良いのかなと思います。

>「CephFS」は、分散ストレージシステム「Ceph」のストレージクラスタで使用される分散ファイルシステムです。
>ストレージプール上の仮想的ファイルシステムを複数のサーバから同時にマウントしてアクセスできます。

https://www.ossnews.jp/oss_info/CephFS

## 分散ファイルシステム
>DFS (分散ファイルシステム) は複数のファイルサーバーや複数の場所にまたがって存在するファイルシステムです。
>どこにあるどのデバイスからでも、ローカルに保存されているのと同じようにファイルにアクセスすることができます。DFSは、ネットワーク上の許可されたユーザー間で、制御された方法で情報やファイルを共有するのに便利です。

https://www.cohesity.com/jp/glossary/%E5%88%86%E6%95%A3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0/

# 環境イメージ
今回の環境構築イメージは以下となります。
前回構築したRook-Ceph環境にk8sのyamlファイルを使ってCephFSやStorageClass,PVCをデプロイしていきます。

![ceph-ページ7.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/edc5da12-f0cf-8fcc-db70-a8182cd1d7de.png)

# 構築
rook/deploy/examples配下を確認すると様々なyamlファイルがあります。この中のfilesystem.yamlというファイルを使用することでCephFSをデプロイすることができます。中身を見てみるとmyfsというcephFSが作成されることが分かります。このCephFSの名前だけではなくmetadata poolやdata poolを設定するような記述もyaml内にあるのですが、Ceph側の領域の話であるため、ここでは説明を省きます。
```
root@rancher:~# cd ~/rook/deploy/examples
root@rancher:~/rook/deploy/examples# ls
bucket-notification-endpoint.yaml  common.yaml                           images.txt                             object-multisite.yaml            rbdmirror.yaml
bucket-notification.yaml           crds.yaml                             import-external-cluster.sh             object-openshift.yaml            README.md
bucket-topic.yaml                  create-external-cluster-resources.py  monitoring                             object-test.yaml                 rgw-external.yaml
ceph-client.yaml                   create-external-cluster-resources.sh  mysql.yaml                             object-user.yaml                 Rook-Ceph-NodePort.yaml
cluster-external-management.yaml   csi                                   nfs-test.yaml                          object.yaml                      storageclass-bucket-delete.yaml
cluster-external.yaml              dashboard-external-https.yaml         nfs.yaml                               operator-openshift.yaml          storageclass-bucket-retain.yaml
cluster-on-local-pvc.yaml          dashboard-external-http.yaml          object-bucket-claim-delete.yaml        operator.yaml                    subvolumegroup.yaml
cluster-on-pvc.yaml                dashboard-ingress-https.yaml          object-bucket-claim-notification.yaml  osd-env-override.yaml            toolbox-job.yaml
cluster-stretched-aws.yaml         dashboard-loadbalancer.yaml           object-bucket-claim-retain.yaml        osd-purge.yaml                   toolbox.yaml
cluster-stretched.yaml             direct-mount.yaml                     object-ec.yaml                         pool-device-health-metrics.yaml  volume-replication-class.yaml
cluster-test.yaml                  filesystem-ec.yaml                    object-external.yaml                   pool-ec.yaml                     volume-replication.yaml
cluster.yaml                       filesystem-mirror.yaml                object-multisite-pull-realm-test.yaml  pool-mirrored.yaml               wordpress.yaml
common-external.yaml               filesystem-test.yaml                  object-multisite-pull-realm.yaml       pool-test.yaml
common-second-cluster.yaml         filesystem.yaml                       object-multisite-test.yaml             pool.yaml

root@rancher:~/rook/deploy/examples# cat filesystem.yaml
中略
metadata:
  name: myfs
  namespace: rook-ceph # namespace:cluster
中略
```

デプロイをしていきます。
```
root@rancher:~/rook/deploy/examples# kubectl apply -f filesystem.yaml
cephfilesystem.ceph.rook.io/myfs created
```

デプロイが出来ているかどうかはtool-boxからも確認出来ますが、CephのWebコンソールからも確認することが出来ます。以下がその画面となります。CephFSだけではなくFS作成に必要なmetadata poolやdata poolも併せて作成されています。
![202361701.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/d9b0952a-b003-1356-e357-fcd187ddaef6.png)

次にStorageClassをデプロイしていきます。サンプルのyamlはrook/deploy/examples/csi/cephfs配下にあります。storageclass.yamlというファイルがそれになるのですが、このままだとreclaimPolicyがDeleteになり使い勝手が悪そうだったので、Retainに修正をしています。
```
root@rancher:~/rook/deploy/examples# cd csi/cephfs/
root@rancher:~/rook/deploy/examples/csi/cephfs# ls
kube-registry.yaml  pod-ephemeral.yaml  pod.yaml  pvc-clone.yaml  pvc-restore.yaml  pvc.yaml  snapshotclass.yaml  snapshot.yaml  storageclass-ec.yaml  storageclass.yaml
root@rancher:~/rook/deploy/examples/csi/cephfs# cat storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: rook-cephfs
# Change "rook-ceph" provisioner prefix to match the operator namespace if needed
provisioner: rook-ceph.cephfs.csi.ceph.com # driver:namespace:operator
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 # namespace:cluster

  # CephFS filesystem name into which the volume shall be created
  fsName: myfs

  # Ceph pool into which the volume shall be created
  # Required for provisionVolume: "true"
  pool: myfs-replicated

  # 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-cephfs-provisioner
  csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph # namespace:cluster
  csi.storage.k8s.io/controller-expand-secret-name: rook-csi-cephfs-provisioner
  csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph # namespace:cluster
  csi.storage.k8s.io/node-stage-secret-name: rook-csi-cephfs-node
  csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph # namespace:cluster

  # (optional) The driver can use either ceph-fuse (fuse) or ceph kernel client (kernel)
  # If omitted, default volume mounter will be used - this is determined by probing for ceph-fuse
  # or by setting the default mounter explicitly via --volumemounter command-line argument.
  # mounter: kernel
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
  # uncomment the following line for debugging
  #- debug

root@rancher:~/rook/deploy/examples/csi/cephfs# cp -p storageclass.yaml storageclass.yaml.org
root@rancher:~/rook/deploy/examples/csi/cephfs# vi storageclass.yaml
root@rancher:~/rook/deploy/examples/csi/cephfs# diff storageclass.yaml storageclass.yaml.org
32c32
< reclaimPolicy: Retain
---
> reclaimPolicy: Delete
```

修正した後デプロイを行います。
```
root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl apply -f storageclass.yaml
storageclass.storage.k8s.io/rook-cephfs created
```

次にPVCを作成していきます。pvc.yamlというyamlファイルも上記と同じディレクトリ上に存在しているはずです。このファイルは特に変更をかけずにデプロイします。
pvとpvcを確認するとboundステートになっているかと思います。
```
root@rancher:~/rook/deploy/examples/csi/cephfs# cat pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: cephfs-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: rook-cephfs
root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl apply -f pvc.yaml
persistentvolumeclaim/cephfs-pvc created

root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
pvc-af074c12-5bba-4015-80e6-9c7f4fe033ed   1Gi        RWO            Retain           Bound    default/cephfs-pvc   rook-cephfs             4s

root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
cephfs-pvc   Bound    pvc-af074c12-5bba-4015-80e6-9c7f4fe033ed   1Gi        RWO            rook-cephfs    7s
```

次に、CephFS（ないしStorageClass,PVC）を使用するpodを作成していきます。podのサンプルyamlも同ディレクトリにあるかと思います。内容は特に変更せずにデプロイします。
```
root@rancher:~/rook/deploy/examples/csi/cephfs# cat pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: csicephfs-demo-pod
  labels:
    app: nginx
spec:
  containers:
    - name: web-server
      image: nginx
      volumeMounts:
        - name: mypvc
          mountPath: /var/lib/www/html
  volumes:
    - name: mypvc
      persistentVolumeClaim:
        claimName: cephfs-pvc
        readOnly: false

root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl apply -f pod.yaml
pod/csicephfs-demo-pod created
root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl get pod
NAME                               READY   STATUS    RESTARTS   AGE
csicephfs-demo-pod                 1/1     Running   0          8s
```

CephFS側にデータが保存されるか検証をしていきたいと思います。
デプロイしたpod内に入り、/var/lib/www/html配下にrok-ceph.htmlというファイルを作成します。
```
root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl get pod
NAME                               READY   STATUS    RESTARTS   AGE
csicephfs-demo-pod                 1/1     Running   0          3m8s
wordpress-mysql-6b49db8c4c-tbgkw   0/1     Pending   0          22m
root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl exec -it csicephfs-demo-pod -- /bin/bash
root@csicephfs-demo-pod:/# cd /var/lib/www/html
root@csicephfs-demo-pod:/var/lib/www/html# ls -ltr
total 0
root@csicephfs-demo-pod:/var/lib/www/html# vi rook-ceph.html
root@csicephfs-demo-pod:/var/lib/www/html# cat rook-ceph.html
Hello Rook-Ceph!!
```

このpodからexitして、replaceを行ってみます。replaceは内部でdeleteとreplaceが走るので、もしcreate後のpodの/var/lib/wwww/html配下にrook-ceph.htmlがあれば、pod内ではなくpod外、今回はCephFS内にデータが保存されていることが確認できます。
以下がreplaceの実行結果です。AGEの部分を見ていただくことでも、replaceが走って新しいpodがデプロイされていることがわかるかと思います、
```
root@csicephfs-demo-pod:/var/lib/www/html# exit
exit
command terminated with exit code 130
root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl get pod
NAME                               READY   STATUS    RESTARTS   AGE
csicephfs-demo-pod                 1/1     Running   0          5m53s
wordpress-mysql-6b49db8c4c-tbgkw   0/1     Pending   0          35m
root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl replace --force -f pod.yaml
pod "csicephfs-demo-pod" deleted
pod/csicephfs-demo-pod replaced
root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl get pod
NAME                               READY   STATUS    RESTARTS   AGE
csicephfs-demo-pod                 1/1     Running   0          19s
wordpress-mysql-6b49db8c4c-tbgkw   0/1     Pending   0          35m
```

replace後のpodにexecします。
/var/lib/www/html配下にrook-ceph.htmlがあることからCephFS側にデータが保存されていることがわかりますね。
```
root@rancher:~/rook/deploy/examples/csi/cephfs# kubectl exec -it csicephfs-demo-pod -- /bin/bash
root@csicephfs-demo-pod:/# cd /var/lib/www/html
root@csicephfs-demo-pod:/var/lib/www/html# ls -ltr
total 1
-rwxrwxrwx 1 root root 18 Jun 17 03:00 rook-ceph.html
root@csicephfs-demo-pod:/var/lib/www/html# cat rook-ceph.html
Hello Rook-Ceph!!
```

イメージとしては以下のような形です。
podの/var/lib/www/html配下はCephFSにマウントしており、ここにファイルを作るとpodから作成したファイルは見えるけれど、実はpod側ではなくFS側にデータが作成されている、ということですね。
![ceph-ページ8.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/5dbcfd71-4a58-587d-b40f-494cfd8ccb33.png)