27
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Kubernetes: ReadWriteOnceとReadWriteOncePodの動作検証

Last updated at Posted at 2021-08-31

はじめに

Kubernetes v1.22にてPVC/PVの新しいAccessModeとして登場したReadWriteOncePod (RWOP)について、これまでのAccessModeのReadWriteOnce (RWO)との違いを動作検証を交えながら紹介します。

AccessModeとは

PVC/PVにて使用するオプションで、これによりマウントする際のアクセス権が指定されます。
Kubernetes v1.21まではReadWriteOnce (RWO), ReadOnlyMany (ROX), ReadWriteMany (RWX)の3つがありました。

accessmode.png

このAccessModeに、Kubernetes v1.22 ではアルファ機能としてReadWriteOncePod (RWOP)が新たに追加されました。

ReadWriteOnceとReadWriteOncePodの違い

ReadWriteOnceReadWriteOncePodは、一見似ているのですが、対象がNodeかPodなのかが異なります。

  • ReadWriteOnce
    • 1つのNodeからRead/Writeでマウントできる
  • ReadWriteOncePod
    • 1つのPodからRead/Writeでマウントできる

言い換えると、ReadWriteOnceは、同じNode上のPodであればRead/Writeでマウントできます。
以下に図で示します。

rwo.png

ReadWriteOnceはNode2のようなことが可能となります。
それに対し、新たに追加されたReadWriteOncePodは、1PodからのみRead/Writeできるように制限されます。

ReadWriteOnce又はReadWriteOncePodを利用するケースの多くは、ブロックストレージを利用する場合ではないでしょうか。
ブロックストレージは、ロックのメカニズムを持っていないため、1台のホスト(Node)からのみWriteされるように構成を組む必要があります。

blockstorage.png

そのため、ストレージ視点だと場合、Writeを発行するNodeは1台と保証するAccessModeであるReadWriteOnceが必要となります。
しかし、上記のように同じNode上のPodからは同時にWriteできてしまうため、注意が必要でした。
(StatefulSetのVolumeClaimTemplateを利用している場合は1Pod/PVC/PVとなるため、本構成にはなりません)

このReadWriteOnceは、ストレージ視点では正しいかと思うのですが、Pod視点で見た場合には、少々不自然な印象でした。
Kubernetesでは、インフラを抽象化したモデルで操作するのを特徴としているにも関わらず、ストレージのマウント構成が滲みでてしまっています。
ユーザは、同じReadWriteOnceのPVC/PVを同じNodeでマウントしないように注意しなければならず、インフラを意識せざるを得ませんでした。
そこで、登場したのがReadWriteOncePodです。ReadWriteOncePodは、Pod視点で1Pod/PVC/PVとなるように設計されたAccess Modeになります。

動作検証

本検証では、上記で説明したReadWriteOnceReadWriteOncePodの違いを検証します。

検証環境の構築

minikube v1.22.0 を使い、2node構成のKubernetes v1.22.0をセットアップします。
また、ReadWriteOncePodはアルファ機能のため、feature-gatesで有効化します。

$ minikube start --driver=virtualbox --kubernetes-version=1.22.0 --nodes 2 --feature-gates='ReadWriteOncePod=true'

上記コマンドで、構築したKubernetesの構成は以下となっています。

$ kubectl get node,sc
NAME                STATUS   ROLES                  AGE   VERSION
node/minikube       Ready    control-plane,master   45m   v1.22.0
node/minikube-m02   Ready    <none>                 43m   v1.22.0

NAME                                             PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
storageclass.storage.k8s.io/standard (default)   k8s.io/minikube-hostpath   Delete          Immediate           false                  45m

ReadWriteOnceの検証

まずは、ReadWriteOnceを検証します。
PVCのManifest(RWO-PVC1.yaml, RWO-PVC2.yaml)を用意します。
RWO-PVC1.yamlRWO-PVC2.yamlはNameのみ異なっています。

  • RWO-PVC1.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  storageClassName: standard
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  • RWO-PVC2.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc2
spec:
  storageClassName: standard
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

上記PVCのManifestをデプロイします。

$ kubectl apply -f RWO-PVC1.yaml -f RWO-PVC2.yaml 
persistentvolumeclaim/pvc1 created
persistentvolumeclaim/pvc2 created

つづいて、pvc1をマウントするPod(pod1)とpvc2を同じNodeでマウントするPod(pod2, pod3)を立ち上げます。
以下にManifestを示します。
NodeSelectorを使いデプロイするNodeを指定しています。

  • pod1.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: pod1
  name: pod1
spec: 
  nodeSelector:
    kubernetes.io/hostname: minikube
  containers:
  - image: ubuntu:18.04
    name: pod1
    command:
    - sleep
    - infinity
    volumeMounts:
    - mountPath: /mnt/data
      name: myvolume
  volumes:
  - name: myvolume
    persistentVolumeClaim:
      claimName: pvc1
  • pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: pod2
  name: pod2
spec: 
  nodeSelector:
    kubernetes.io/hostname: minikube-m02
  containers:
  - image: ubuntu:18.04
    name: pod2
    command:
    - sleep
    - infinity
    volumeMounts:
    - mountPath: /mnt/data
      name: myvolume
  volumes:
  - name: myvolume
    persistentVolumeClaim:
      claimName: pvc2
  • pod3.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: pod3
  name: pod3
spec: 
  nodeSelector:
    kubernetes.io/hostname: minikube-m02
  containers:
  - image: ubuntu:18.04
    name: pod3
    command:
    - sleep
    - infinity
    volumeMounts:
    - mountPath: /mnt/data
      name: myvolume
  volumes:
  - name: myvolume
    persistentVolumeClaim:
      claimName: pvc2

pod2.yamlとpod3.yamlは同一Node上で同じPVCをマウントするため、Pod名やコンテナ名しか違いはありません。

 diff -u pod2.yaml pod3.yaml 
--- pod2.yaml   2021-08-31 15:36:55.000000000 +0900
+++ pod3.yaml   2021-08-31 15:37:18.000000000 +0900
@@ -2,14 +2,14 @@
 kind: Pod
 metadata:
   labels:
-    run: pod2
-  name: pod2
+    run: pod3
+  name: pod3
 spec: 
   nodeSelector:
     kubernetes.io/hostname: minikube-m02
   containers:
   - image: ubuntu:18.04
-    name: pod2
+    name: pod3
     command:
     - sleep
     - infinity

PodのManifestをデプロイします。

$ kubectl apply -f pod1.yaml -f pod2.yaml -f pod3.yaml 
pod/pod1 created
pod/pod2 created
pod/pod3 created

Pod,PVC,PVを確認します。

$ kubectl get pod,pvc,pv -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP           NODE           NOMINATED NODE   READINESS GATES
pod/pod1   1/1     Running   0          15s   10.244.0.4   minikube       <none>           <none>
pod/pod2   1/1     Running   0          15s   10.244.1.5   minikube-m02   <none>           <none>
pod/pod3   1/1     Running   0          15s   10.244.1.6   minikube-m02   <none>           <none>

NAME                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE   VOLUMEMODE
persistentvolumeclaim/pvc1   Bound    pvc-ffd4f1aa-eb86-4ac4-8355-56e57a74a43a   1Gi        RWO            standard       22m   Filesystem
persistentvolumeclaim/pvc2   Bound    pvc-a3bc28e8-752c-4457-b788-6a4f057f9fbe   1Gi        RWO            standard       22m   Filesystem

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM          STORAGECLASS   REASON   AGE   VOLUMEMODE
persistentvolume/pvc-a3bc28e8-752c-4457-b788-6a4f057f9fbe   1Gi        RWO            Delete           Bound    default/pvc2   standard                22m   Filesystem
persistentvolume/pvc-ffd4f1aa-eb86-4ac4-8355-56e57a74a43a   1Gi        RWO            Delete           Bound    default/pvc1   standard                22m   Filesystem

Node(minikube-m02)上に1つのPVC(pvc2)をマウントする2つのPod(pod2, pod3)が出来ています。
次に、pod2からマウントしたVolume上のファイルに書き込み(Write)をしてみます。

$ kubectl exec -ti pod2 -- sh -c 'echo "Hello" >/mnt/data/hoge'

続いて、pod3から同じファイル(/mnt/data/hoge)に対して追加で書き込みを行います。

$ kubectl exec -ti pod3 -- sh -c 'echo "Hoge" >>/mnt/data/hoge'

次に、pod2から、文字列を書き込んだファイル(/mnt/data/hoge)の中身を確認してみます。

$ kubectl exec -ti pod2 -- cat /mnt/data/hoge
Hello
Hoge

同じVolumeへ書き込みを行なっているため、pod2pod3から書き込んだ文字列が格納されています。
ストレージからすれば、Node上で動作するドライバ(SCSIなど)からWriteされた順でデータを格納するため、障害にはなりませんが、Pod上で動作するアプリからみたら、同一ファイルをロックなしにデータを書き込むため、データ破損のリスクがある危険な構成です。

Pod,PVC,PVを削除します。

$ kubectl delete pod --all
pod "pod1" deleted
pod "pod2" deleted
pod "pod3" deleted

$ kubectl delete pvc --all
persistentvolumeclaim "pvc1" deleted
persistentvolumeclaim "pvc2" deleted

ReadWriteOncePodの検証

続いて、Kubernetes v1.22で新しくアルファ機能として登場したReadWriteOncePodを試します。
まず、ReadWriteOncePodのAccessModeを指定したPVC(pvc3)を作成します。
Manifestは以下です。

  • RWOP-PVC3.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc3
spec:
  storageClassName: standard
  accessModes:
    - ReadWriteOncePod
  resources:
    requests:
      storage: 1Gi

Manifestをデプロイします。

$ kubectl apply -f RWOP-PVC3.yaml 
persistentvolumeclaim/pvc3 created

PVC/PVを確認します。

$ kubectl get pvc,pv
NAME                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/pvc3   Bound    pvc-611e7784-e5d7-4b20-b994-445fa1a22083   1Gi        RWOP           standard       7s

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM          STORAGECLASS   REASON   AGE
persistentvolume/pvc-611e7784-e5d7-4b20-b994-445fa1a22083   1Gi        RWOP           Delete           Bound    default/pvc3   standard                7s

ReadWriteOncePod (RWOP)でPVC/PVが作成されていることが確認できます。
次に、作成したPVC/PVをマウントするPod(pod4)を作成します。
Manifestは以下です。

  • pod4.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: pod4
  name: pod4
spec: 
  nodeSelector:
    kubernetes.io/hostname: minikube
  containers:
  - image: ubuntu:18.04
    name: pod4
    command:
    - sleep
    - infinity
    volumeMounts:
    - mountPath: /mnt/data
      name: myvolume
  volumes:
  - name: myvolume
    persistentVolumeClaim:
      claimName: pvc3

Manifestをデプロイし、Pod,PVC,PVを確認します。

$ kubectl get pod,pvc,pv -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
pod/pod4   1/1     Running   0          21s   10.244.0.6   minikube   <none>           <none>

NAME                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE    VOLUMEMODE
persistentvolumeclaim/pvc3   Bound    pvc-611e7784-e5d7-4b20-b994-445fa1a22083   1Gi        RWOP           standard       103s   Filesystem

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM          STORAGECLASS   REASON   AGE    VOLUMEMODE
persistentvolume/pvc-611e7784-e5d7-4b20-b994-445fa1a22083   1Gi        RWOP           Delete           Bound    default/pvc3   standard                103s   Filesystem

ReadWriteOncePod (RWOP)のAccessModeのPVCをマウントしたPod(pod4)が立ち上がりました。
続いて、同じNode(minikube)上に同じPVC(pvc3)をマウントするPod(pod5)を作成してみます。
Manifestは以下です。

  • pod5.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: pod5
  name: pod5
spec: 
  nodeSelector:
    kubernetes.io/hostname: minikube
  containers:
  - image: ubuntu:18.04
    name: pod5
    command:
    - sleep
    - infinity
    volumeMounts:
    - mountPath: /mnt/data
      name: myvolume
  volumes:
  - name: myvolume
    persistentVolumeClaim:
      claimName: pvc3

なお、先にデプロイしたpod4.yamlとpod5.yamlの差分は名前のみです。

$ diff -u pod4.yaml pod5.yaml 
--- pod4.yaml   2021-08-31 16:08:51.000000000 +0900
+++ pod5.yaml   2021-08-31 16:13:22.000000000 +0900
@@ -2,14 +2,14 @@
 kind: Pod
 metadata:
   labels:
-    run: pod4
-  name: pod4
+    run: pod5
+  name: pod5
 spec: 
   nodeSelector:
     kubernetes.io/hostname: minikube
   containers:
   - image: ubuntu:18.04
-    name: pod4
+    name: pod5
     command:
     - sleep
     - infinity

pod5.yamlのManifestをデプロイします。

$ kubectl apply -f pod5.yaml 
pod/pod5 created

Podの状態を確認します。

$ kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
pod4   1/1     Running   0          5m52s   10.244.0.6   minikube   <none>           <none>
pod5   0/1     Pending   0          30s     <none>       <none>     <none>           <none>

pod5はPendingとなっています。
詳細を確認します。

$ kubectl describe pod pod5
...
Events:
  Type     Reason            Age               From               Message
  ----     ------            ----              ----               -------
  Warning  FailedScheduling  3s (x2 over 87s)  default-scheduler  0/2 nodes are available: 2 node has pod using PersistentVolumeClaim with the same name and ReadWriteOncePod access mode.

同じPVCの名前を持つPodが既に存在するため、マウントできるNodeがなく、スケジューリングできずにPendingとなっていることが確認できます。
ReadWriteOncePodが正しく動作していることが確認できました。

感想

本レポートでは、Kubernetes v1.22で新たに登場したAccessMode(ReadWiteOncePod)を試しました。
これまで、ReadWriteOnceがNode単位の制御であったため、データ破損のリスクのある構成を取ることが出来てしまいました。
多くのユーザにとっては、望んでいない構成かと思います。
今回追加されたReadWiteOncePodにより、より安全に1Pod/PVC/PVを保証するAccessModeが出来たことで、ブロックストレージを利用する多くのPVC/PVは、今後ReadWiteOnceの代わりにReadWiteOncePodを利用するケースが増えてくるかと推察します。
ReadWriteOnceReadWriteOncePodの違いを抑え、安全にPVC/PVを利用すると良いでしょう。

27
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?