[Kubernetes]PersistentVolume/PersistentVolumeClaimの動作を確認する 3

#はじめに
今回はPersistentVolume（PV）のアクセスモードの動作を確認したいと思います。
アクセスモードには以下の3つがあります。

|アクセスモード|概要
|:---|:---
|ReadWriteOnce |単一のNodeで読み取り/書き込みとしてマウントできます
|ReadOnlyMany |多数のNodeで読み取り専用としてマウントできます
|ReadWriteMany |多数のNodeで読み取り/書き込みとしてマウントできます

ReadWriteManyはこれまで確認してますので、「ReadWriteOnce」「ReadOnlyMany」について確認します。
ReadWriteManyの動作は[こちら](https://qiita.com/dingtianhongjie/items/d9064471cf8c97c994d4)をご参照ください。

#ReadWriteOnce

##環境準備
以下のマニフェストのPV/PVC/Podを作成します。

```pv-rwo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 500Mi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /share
    server: k8s-client
```

```pvc-rwo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi
  storageClassName: slow
```

```nginx-dep-rwo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-dep
spec:
  replicas: 4
  selector:
    matchLabels:
      app: nginx-dep
  template:
    metadata:
      labels:
        app: nginx-dep
    spec:
      containers:
      - image: nginx:latest
        name: nginx-container
        volumeMounts:
        - mountPath: /cache
          name: cache-pv
      volumes:
      - name: cache-pv
        persistentVolumeClaim:
          claimName: myclaim
          readOnly: false
```

```
$ kubectl apply -f pv-rwo.yaml
persistentvolume/pv0003 created
$ kubectl apply -f pvc-rwo.yaml
persistentvolumeclaim/myclaim created
$ kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
pv0003   500Mi      RWO            Recycle          Bound    default/myclaim   slow                    55s
$ kubectl get pvc
NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
myclaim   Bound    pv0003   500Mi      RWO            slow           9s
$ kubectl apply -f nginx-dep-rwo.yaml
deployment.apps/nginx-dep created
$ kubectl get pod -o wide
NAME                         READY   STATUS      RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES
nginx-dep-54b4455fbd-75vfj   1/1     Running     0          16s   192.168.79.79    k8s-worker01   <none>           <none>
nginx-dep-54b4455fbd-bkmsm   1/1     Running     0          16s   192.168.69.208   k8s-worker02   <none>           <none>
nginx-dep-54b4455fbd-hfsbs   1/1     Running     0          16s   192.168.79.65    k8s-worker01   <none>           <none>
nginx-dep-54b4455fbd-kc9dn   1/1     Running     0          16s   192.168.69.221   k8s-worker02   <none>           <none>
```

##動作確認

1つのPVを4つのPodで共用した形になっています。また4つのPodはそれぞれworker01/worker02に分散してデプロイされています。

別ノードにデプロイされている2つのPodからPVに書き込んでみます。

```
$ kubectl exec -it nginx-dep-54b4455fbd-75vfj touch /cache/testfile
$ kubectl exec -it nginx-dep-54b4455fbd-75vfj ls /cache/
testfile
$ kubectl exec -it nginx-dep-54b4455fbd-bkmsm touch /cache/testfile2
$ kubectl exec -it nginx-dep-54b4455fbd-75vfj ls /cache/
testfile  testfile2
```

書き込みできましたね。。。
調べてみると、ReadWriteOnceは各Podからのアクセスを制御するものではないようです。

>参考
>https://www.kimullaa.com/entry/2020/02/20/212517

また、GCPのマニュアルですが、ReadWriteOnceはDeployment（ReplicaSet）では使用しないで、StatefulSetで使用するものだと書かれています。

>Deployment はステートレス アプリケーション用に設計されているため、Deployment のすべてのレプリカで同じ PersistentVolumeClaim が共有されます。作成されたレプリカ Pod はそれぞれ同一であるため、この設定では ReadOnlyMany モードと ReadWriteMany モードのボリュームだけが動作します。
>・・・中略・・・
>この場合は ReadWriteOnce ボリュームで StatefulSet を使用します。

>https://cloud.google.com/kubernetes-engine/docs/concepts/persistent-volumes?hl=ja#deployments_vs_statefulsets

###StatefulSetの作成
DeploymentからStatefulSetに変えて、再度確認します。
まず、Deploymentは削除しておきます。

```
$ kubectl delete -f nginx-dep-rwo.yaml
deployment.apps "nginx-dep" deleted
```

以下のマニフェストのStatefulSetをapplyします。

```nginx-statefulset-rwo.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "slow"
      resources:
        requests:
          storage: 500Mi
```

```
$ kubectl apply -f nginx-statefulset-rwo.yaml
statefulset.apps/web created
$ kubectl get statefulsets
NAME   READY   AGE
web    0/3     22s
$ kubectl get pod
web-0                 0/1     Pending     0          27s
$ kubectl describe pod web-0
Name:           web-0
・・・
Events:
  Type     Reason            Age                From               Message
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  46s (x2 over 46s)  default-scheduler  error while running "VolumeBinding" filter plugin for pod "web-0": pod has unbound immediate PersistentVolumeClaims
```
PodがPendingになっているので調べてみると、PVCのBoundに失敗しているようです。

いったん削除します。

```
$ kubectl delete -f nginx-statefulset-rwo.yaml
statefulset.apps "web" deleted
$ kubectl get pvc
NAME        STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
myclaim     Bound     pv0003   500Mi      RWO            slow           23h
www-web-0   Pending                                      slow           2m40s
$ kubectl delete pvc www-web-0
persistentvolumeclaim "www-web-0" deleted
```

PodやDeploymentと違って、StatefulSetのマニフェストの中でPVを要求しているんですね。知らなかった・・・
ちなみに、StatefulSetで作成されるPVCはStatefulSetを削除しても残ります。PVCは個別に削除する必要があります。

---
myclaimを削除して、再度StatefulSetをapplyします。


```
$ kubectl delete -f pvc-rwo.yaml
persistentvolumeclaim "myclaim" deleted
$ kubectl apply -f nginx-statefulset-rwo.yaml
statefulset.apps/web created
$ kubectl get statefulsets -o wide
NAME   READY   AGE    CONTAINERS   IMAGES
web    1/4     100s   nginx        k8s.gcr.io/nginx-slim:0.8
$ kubectl get pod
web-0                 1/1     Running     0          84s
web-1                 0/1     Pending     0          12s
```

2つ目のPodがPendingになっています。

```
$ kubectl get pvc
NAME        STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound     pv0003   500Mi      RWO            slow           8m51s
www-web-1   Pending                                      slow           7m39s
```

PVCを確認すると、2つ目のPVCがPendingになってますね。
PVCは1つなのかと思いましたが、1つのPodに1つのPVCが作成されるようです。ってことは、PVもPodの数分必要になるということですね。

---

PVを全部で3つにしました。

```
$ kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM               STORAGECLASS   REASON   AGE
pv0001   500Mi      RWO            Recycle          Bound       default/www-web-0   slow                    24s
pv0002   500Mi      RWO            Recycle          Bound       default/www-web-1   slow                    24s
pv0003   500Mi      RWO            Recycle          Available   default/www-web-2   slow                    18s
$ kubectl get pvc
NAME        STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pv0001   500Mi      RWO            slow           21s
www-web-1   Bound    pv0002   500Mi      RWO            slow           17s
www-web-2   Bound    pv0003   500Mi      RWO            slow           14s
$ kubectl get pod -o wide
web-0                 1/1     Running     0          31s   192.168.79.86    k8s-worker01   <none>           <none>
web-1                 1/1     Running     0          27s   192.168.69.218   k8s-worker02   <none>           <none>
web-2                 1/1     Running     0          24s   192.168.79.91    k8s-worker01   <none>           <none>
```
今度はすべてのPodがデプロイできました。
ReadWriteOnceは単一の「ノード」からRead/Writeを許可するので、同一ノード内の複数PodからのRead/Writeは可能なのかと思っていましたが、単一ノードではなく単一「Pod」と思っていた方がよさそうですね。
そもそもStatefulSetで使用する時点でPodとPVが1対1になりますし。

#ReadOnlyMany 
次にReadOnlyManyの動作を確認します。

##環境準備
以下のマニフェストのPV/PVC/Podを作成します。

```pv-rox.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 500Mi
  volumeMode: Filesystem
  accessModes:
    - ReadOnlyMany
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /share
    server: k8s-client
```

```pvc-rox.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 500Mi
  storageClassName: slow
```

```nginx-dep-rox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-dep
spec:
  replicas: 4
  selector:
    matchLabels:
      app: nginx-dep
  template:
    metadata:
      labels:
        app: nginx-dep
    spec:
      containers:
      - image: nginx:latest
        name: nginx-container
        volumeMounts:
        - mountPath: /cache
          name: cache-pv
      volumes:
      - name: cache-pv
        persistentVolumeClaim:
          claimName: myclaim
          readOnly: true
```

```
$ kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
pv0003   500Mi      ROX            Recycle          Bound    default/myclaim   slow                    15s
$ kubectl get pvc
NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
myclaim   Bound    pv0003   500Mi      ROX            slow           11s
$ kubectl get pod -o wide
NAME                         READY   STATUS      RESTARTS   AGE     IP               NODE           NOMINATED NODE   READINESS GATES
nginx-dep-6d787c9bd7-5r7w5   1/1     Running     0          84s     192.168.79.96    k8s-worker01   <none>           <none>
nginx-dep-6d787c9bd7-6l4fx   1/1     Running     0          84s     192.168.69.239   k8s-worker02   <none>           <none>
nginx-dep-6d787c9bd7-9b2rk   1/1     Running     0          84s     192.168.79.100   k8s-worker01   <none>           <none>
nginx-dep-6d787c9bd7-l5qd4   1/1     Running     0          84s     192.168.69.238   k8s-worker02   <none>           <none>
```

##動作確認
あらかじめNFS ServerのShareしたディレクトリにテスト用のファイルを置いておきます。
そのファイルを複数のPodから読み込めることを確認します。

```
$ kubectl exec -it nginx-dep-6d787c9bd7-5r7w5 cat /cache/testfile
NFS Server
$ kubectl exec -it nginx-dep-6d787c9bd7-6l4fx cat /cache/testfile
NFS Server
```
別々のworkerノードにデプロイされているPodから読み込めましたね。
念のため、書き込みできないことも確認しておきます。

```
$ kubectl exec -it nginx-dep-6d787c9bd7-5r7w5 touch /cache/aaaa
touch: cannot touch '/cache/aaaa': Read-only file system
command terminated with exit code 1
$ kubectl exec -it nginx-dep-6d787c9bd7-6l4fx touch /cache/aaaa
touch: cannot touch '/cache/aaaa': Read-only file system
command terminated with exit code 1
```

#まとめ
ReadWriteOnceは想定とは違った動きでした。やはりやってみないとわからないものです。
でも、そのおかげでStatefulSetの動作も確認できました。