Namespace間で動的にPVCを共有してみた

# はじめに
- Kubernetes上でシステムを構築・運用していると、Namespace間でPVCを共有したいと思ったことはありませんか？
- Namespace間でのデータの受け渡しであれば、Amazon S3のようなオブジェクトストレージであったりメッセージキューであったりを使えばいいのですが、手っ取り早くPVC自体を共有できたらいいのにという場面はあると思います。
- 本記事では、ネットアップが提供するCSI driver「Astra Trident」を使ってNamespace間でPVCを共有してみます。

:::note info
- Astra Tridentは、ネットアップが提供するストレージをコンテナ環境で利用するためのCSI driverです。（詳しい説明は[こちら](https://qiita.com/star76/items/fa88d715192f565e5832)から）
- ハードウェアのストレージだけでなく、Amazon FSx for NetApp ONTAPや、Azure NetApp Filesなどのパブリッククラウド上のストレージサービスにも対応しているので、インフラの場所を問わず利用可能です。
- このAstra Tridentの2022年12月時点の最新リリース（[v22.10](https://github.com/NetApp/trident/releases/tag/v22.10.0)）で、Namespace間でボリュームを共有することが出来る新機能が実装されたので、今回はその機能を試してみました。
:::

# いきなり結論：できた
- 構成としてはこんな感じになりました。
- Namespace「ns1」の「pvc1」をNamespace「ns2」に「pvc2」として紐づけて、実体としては同じボリュームを共有しています。
![architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523630/770e7dc9-c8af-b3a8-3712-d3359a0e7525.png)

## 設定内容
- Namespace間でのPVCの共有には以下の設定が必要です。

### PersistentVolumeClaim（PVC）への設定
- PVCのマニフェスト内で`shareToNamespace`というアノテーションを使って共有先となるNamespaceを以下のように定義します。（Namespace「ns1」の「pvc1」をNamespace「ns2」に共有する場合）
    ```yaml: pvc1.yaml
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      annotations:
        trident.netapp.io/shareToNamespace: ns2
      name: pvc1
      namespace: ns1
    spec:
      accessModes:
        - ReadWriteMany
      storageClassName: fsx-sc
      resources:
        requests:
          storage: 10Gi
    ```
- 共有される側のPVCのマニフェスト内でも、`shareFromPVC`というアノテーションを使って以下のように定義します。（Namespace「ns2」にNamespace「ns1」の「pvc1」が共有される場合）
    ``` yaml: pvc2.yaml
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      annotations:
        trident.netapp.io/shareFromPVC: ns1/pvc1
      name: pvc2
      namespace: ns2
    spec:
      accessModes:
        - ReadWriteMany
      storageClassName: fsx-sc
      resources:
        requests:
          storage: 10Gi
    ```

### TridentVolumeReference（TVR）の作成
- これはAstra Tridentが使用するアクセス制御用のKubernetesのカスタムリソースで、共有される側のNamespace（今回の例では「ns2」）で以下のように定義します。
    ``` yaml: tvr-ns1-to-ns2.yaml
    apiVersion: trident.netapp.io/v1
    kind: TridentVolumeReference
    metadata:
      name: tvr-ns1-to-ns2
      namespace: ns2
    spec:
      pvcName: pvc1
      pvcNamespace: ns1 
    ```
## 注意事項
- Astra Tridentでは、共有されたボリュームに対する共有先のNamespaceからの書き込みを防ぐことはできません。必要に応じてファイルロックなどで制御する必要があります。
- PVCのマニフェスト内のアノテーション`shareToNamespace`、`shareFromPVC`やTridentVolumeReferenceを削除しても共有先PVCへのアクセスを取り消すことはできません。アクセスを取り消すには、共有先PVC自体を削除する必要があります。
- 共有先PVCではSnapshotやcloneを使うことはできません。

:::note warn
ここから先は具体的な手順になるので、興味が無ければ読み飛ばしても問題ありません。
:::

# やったこと
## 前提
- 以下は準備済みとします。
    - Kubernetesクラスタ
    - ONTAPが動くストレージ（今回はAmazon FSx for NetApp ONTAPを使用）
    - KubernetesクラスタへのAstra Trident（v22.10）のインストールとバックエンドの設定（詳細手順は[こちら](https://qiita.com/seijitanabe/items/c6c91eacaa89dcba2864)）

## StorageClass作成
- バックエンド設定を確認します。
    ```
    # tridentctl -n trident get backend
    +-------------+----------------+--------------------------------------+--------+---------+
    |    NAME     | STORAGE DRIVER |                 UUID                 | STATE  | VOLUMES |
    +-------------+----------------+--------------------------------------+--------+---------+
    | fsx_backend | ontap-nas      | 98f0f5f9-eeb1-4c13-b390-a8377cb5c8fb | online |       0 |
    +-------------+----------------+--------------------------------------+--------+---------+
    ```
- StorageClassのマニフェストは以下の通りです。
    ``` yaml: fsx-sc.yaml
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fsx-sc
    provisioner: csi.trident.netapp.io
    parameters:
      backendType: "ontap-nas"
      fsType: "ext4"
    ```
- StorageClassを作成します。
    ```
    # kubectl apply -f fsx-sc.yaml
    storageclass.storage.k8s.io/fsx-sc created
    ```
- StorageClassが作成されていることを確認します。
    ```
    # kubectl get sc
    NAME     PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
    fsx-sc   csi.trident.netapp.io   Delete          Immediate           false                  10s
    ```
- ちなみに、ストレージ側を見てみると、まだ何もボリュームは作られていません。（`st_trident_svm1_root`と`vol1`はデフォルトで作成されるボリュームなのでノーカウント。）
    ```
    ::> volume show
    Vserver           Volume                Aggregate    State      Type       Size  Available Used%
    --------------   --------------------  ------------ ---------- ---- ---------- ---------- -----
    st-trident-svm1  st_trident_svm1_root  aggr1        online     RW          1GB    972.2MB    0%
    st-trident-svm1  vol1                  aggr1        online     RW         10GB     9.50GB    0%
    2 entries were displayed.
    ```

## Namespace作成
- Namespace「ns1」を作成します。
    ```
    # kubectl create namespace ns1
    namespace/ns1 created
    ```
- Namespace「ns2」を作成します。
    ```
    # kubectl create namespace ns2
    namespace/ns2 created
    ```
- Namespaceが作成されていることを確認します。
    ```
    # kubectl get namespaces
    NAME              STATUS   AGE
    default           Active   23m
    kube-flannel      Active   22m
    kube-node-lease   Active   23m
    kube-public       Active   23m
    kube-system       Active   23m
    ns1               Active   20s
    ns2               Active   10s
    trident           Active   17m
    ```
## PVC1作成
- PVC1のマニフェストは以下の通りです。
    ```yaml: pvc1.yaml
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      annotations:
        trident.netapp.io/shareToNamespace: ns2
      name: pvc1
      namespace: ns1
    spec:
      accessModes:
        - ReadWriteMany
      storageClassName: fsx-sc
      resources:
        requests:
          storage: 10Gi
    ```
- PVC1を作成します。
    ```
    # kubectl apply -f pvc1.yaml
    persistentvolumeclaim/pvc1 created
    ```
- ストレージ側を見てみると、Astra Tridentによって作成されたボリューム（`trident_pvc_`で始まるもの）が1つできていました。
    ```
    ::> volume show
        Vserver           Volume                                           Aggregate    State      Type       Size  Available Used%
        --------------   ------------------------------------------------  ------------ ---------- ---- ---------- ---------- -----
        st-trident-svm1  st_trident_svm1_root                              aggr1        online     RW          1GB    972.2MB    0%
        st-trident-svm1  trident_pvc_5d442ce8_f79c_4613_b896_be78860a9c90  aggr1        online     RW         10GB    10.00GB    0%
        st-trident-svm1  vol1                                              aggr1        online     RW         10GB     9.50GB    0%
        3 entries were displayed.
    ```

## TridentVolumeReference作成
- TridentVolumeReferenceのマニフェストは以下の通りです。
    ``` yaml: tvr-ns1-to-ns2.yaml
    apiVersion: trident.netapp.io/v1
    kind: TridentVolumeReference
    metadata:
      name: tvr-ns1-to-ns2
      namespace: ns2
    spec:
      pvcName: pvc1
      pvcNamespace: ns1 
    ```
- TridentVolumeReferenceを作成します。
    ```
    # kubectl apply -f tvr-ns1-to-ns2.yaml 
    tridentvolumereference.trident.netapp.io/tvr-ns1-to-ns2 created
    ```
## PVC2作成
- PVC2のマニフェストは以下の通りです。
    ``` yaml: pvc2.yaml
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      annotations:
        trident.netapp.io/shareFromPVC: ns1/pvc1
      name: pvc2
      namespace: ns2
    spec:
      accessModes:
        - ReadWriteMany
      storageClassName: fsx-sc
      resources:
        requests:
          storage: 10Gi
    ```
- PVC2を作成します。
    ```
    # kubectl apply -f pvc2.yaml
    persistentvolumeclaim/pvc2 created
    ```
- 作成されているPVCを確認します。
    ```
    # kubectl get pvc --all-namespaces
    NAMESPACE   NAME   STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    ns1         pvc1   Bound    pvc-5d442ce8-f79c-4613-b896-be78860a9c90   10Gi       RWX            fsx-sc         6m34s
    ns2         pvc2   Bound    pvc-6a5a270e-e0fe-4967-a1aa-e9ca54f3336a   10Gi       RWX            fsx-sc         26s
    ```
- Astra Trident側で確認するとこんな感じでした。
    ```
    # tridentctl get volume -n trident
    +------------------------------------------+--------+---------------+----------+--------------------------------------+-------------+---------+
    |                   NAME                   |  SIZE  | STORAGE CLASS | PROTOCOL |             BACKEND UUID             |    STATE    | MANAGED |
    +------------------------------------------+--------+---------------+----------+--------------------------------------+-------------+---------+
    | pvc-5d442ce8-f79c-4613-b896-be78860a9c90 | 10 GiB | fsx-sc        | file     | 98f0f5f9-eeb1-4c13-b390-a8377cb5c8fb | online      | true    |
    | pvc-6a5a270e-e0fe-4967-a1aa-e9ca54f3336a | 10 GiB | fsx-sc        | file     | 98f0f5f9-eeb1-4c13-b390-a8377cb5c8fb | subordinate | true    |
    +------------------------------------------+--------+---------------+----------+--------------------------------------+-------------+---------+
    ```
- ストレージ側を見てみると、Astra Tridentによって作成されたボリューム（`trident_pvc_`で始まるもの）が1つだけでした。2つのPVCで1つのボリュームを共有していることがわかります。
    ```
    ::> volume show
        Vserver           Volume                                           Aggregate    State      Type       Size  Available Used%
        --------------   ------------------------------------------------  ------------ ---------- ---- ---------- ---------- -----
        st-trident-svm1  st_trident_svm1_root                              aggr1        online     RW          1GB    972.2MB    0%
        st-trident-svm1  trident_pvc_5d442ce8_f79c_4613_b896_be78860a9c90  aggr1        online     RW         10GB    10.00GB    0%
        st-trident-svm1  vol1                                              aggr1        online     RW         10GB     9.50GB    0%
        3 entries were displayed.
    ```
## PVCをマウントしてみて確認
### PVC1をマウントするPodを作成
- Namespace「ns1」に作成するPodのマニフェストは以下の通りです。
    ``` yaml: ns1-centos-deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: ns1-centos
      namespace: ns1
      labels:
        app: ns1-centos
    spec:
      selector:
        matchLabels:
          app: ns1-centos
      template:
        metadata:
          labels:
            app: ns1-centos
        spec:
          containers:
          - image: centos:8
            name: ns1-centos
            command:
              - "/sbin/init"
            volumeMounts:
            - name: ns1-pvc1
              mountPath: /mnt
            securityContext:
              allowPrivilegeEscalation: true
              capabilities: {}
              privileged: true
              readOnlyRootFilesystem: false
              seLinuxOptions: {}
          volumes:
          - name: ns1-pvc1
            persistentVolumeClaim:
              claimName: pvc1
    ```
- Namespace「ns1」にCentOSのPodを作成します。
    ```
    # kubectl apply -f ns1-nccentos-deployment.yaml 
    deployment.apps/ns1-centos created
    ```
- Podが作成されていることを確認します。
    ```
    # kubecrtl get pods -n ns1
    NAME                          READY   STATUS              RESTARTS   AGE
    ns1-centos-787447579d-xfnx4   0/1     ContainerCreating   0          11s
    ```

### PVC1にファイルを書き込み
- PVC1がマウントされているPodのシェルを取得します。
    ```
    # kubectl exec -n ns1 -it ns1-centos-787447579d-xfnx4 /bin/bash
    ```
- PVCがマウントされていることを確認します。
    ```
    # df
    Filesystem                                                  1K-blocks    Used Available Use% Mounted on
    overlay                                                       8376300 4492116   3884184  54% /
    tmpfs                                                           65536       0     65536   0% /dev
    tmpfs                                                          991916       0    991916   0% /sys/fs/cgroup
    10.0.1.48:/trident_pvc_5d442ce8_f79c_4613_b896_be78860a9c90  10485760     320  10485440   1% /mnt
    /dev/nvme0n1p1                                                8376300 4492116   3884184  54% /etc/hosts
    shm                                                             65536       0     65536   0% /dev/shm
    tmpfs                                                          991916    8204    983712   1% /run
    ```
- 適当にファイルを書き込んでみます。
    ```
    # echo "Hello from ns1." > /mnt/test.txt
    ```
- ファイルの中身を確認します。
    ```
    # cat /mnt/test.txt
    Hello from ns1.
    ```

### PVC2をマウントするPodを作成
- Namespace「ns2」に作成するPodのマニフェストは以下の通りです。
    ``` yaml: ns2-centos-deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: ns2-centos
      namespace: ns2
      labels:
        app: ns2-centos
    spec:
      selector:
        matchLabels:
          app: ns2-centos
      template:
        metadata:
          labels:
            app: ns2-centos
        spec:
          containers:
          - image: centos:8
            name: ns2-centos
            command:
              - "/sbin/init"
            volumeMounts:
            - name: ns2-pvc2
              mountPath: /mnt
            securityContext:
              allowPrivilegeEscalation: true
              capabilities: {}
              privileged: true
              readOnlyRootFilesystem: false
              seLinuxOptions: {}
          volumes:
          - name: ns2-pvc2
            persistentVolumeClaim:
              claimName: pvc2
    ```
- Namespace「ns2」にCentOSのPodを作成します。
    ```
    # kubectl apply -f ns2-nccentos-deployment.yaml 
    deployment.apps/ns2-centos created
    ```
- Podが作成されていることを確認します。
    ```
    # kubecrtl get pods -n ns2
    NAME                          READY   STATUS              RESTARTS   AGE
    ns1-centos-787447579d-xfnx4   0/1     ContainerCreating   0          11s
    ```

### PVC2の中身を確認
- PVC2がマウントされているPodのシェルを取得します。
    ```
    # kubectl exec -n ns2 -it ns2-centos-6dd94ccd9c-t4x2n /bin/bash
    ```
- PVCがマウントされていることを確認します。PVC1と同じパスです。
    ```
    # df
    Filesystem                                                  1K-blocks    Used Available Use% Mounted on
    overlay                                                       8376300 4492888   3883412  54% /
    tmpfs                                                           65536       0     65536   0% /dev
    tmpfs                                                          991916       0    991916   0% /sys/fs/cgroup
    10.0.1.48:/trident_pvc_5d442ce8_f79c_4613_b896_be78860a9c90  10485760     320  10485440   1% /mnt
    /dev/nvme0n1p1                                                8376300 4492888   3883412  54% /etc/hosts
    shm                                                             65536       0     65536   0% /dev/shm
    tmpfs                                                          991916    8208    983708   1% /run
    ```
- PVC内のファイルの中身を確認します。PVC1で書き込んだファイルがありました。
    ```
    # cat /mnt/test.txt
    Hello from ns1.
    ```
- できました！

# まとめ
- このような感じで、Astra Tridentの機能で簡単にNamespace間で動的にPVCを共有することができました。

# 参考リンク
- https://docs.netapp.com/ja-jp/trident/trident-use/volume-share.html
- https://bluexp.netapp.com/blog/astra-blg-sharing-volumes-between-namespaces-say-hello-to-cross-namespace-volume-access