#はじめに
今回はPersistentVolume(PV)のアクセスモードの動作を確認したいと思います。
アクセスモードには以下の3つがあります。
アクセスモード | 概要 |
---|---|
ReadWriteOnce | 単一のNodeで読み取り/書き込みとしてマウントできます |
ReadOnlyMany | 多数のNodeで読み取り専用としてマウントできます |
ReadWriteMany | 多数のNodeで読み取り/書き込みとしてマウントできます |
ReadWriteManyはこれまで確認してますので、「ReadWriteOnce」「ReadOnlyMany」について確認します。
ReadWriteManyの動作はこちらをご参照ください。
#ReadWriteOnce
##環境準備
以下のマニフェストのPV/PVC/Podを作成します。
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
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
storageClassName: slow
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からのアクセスを制御するものではないようです。
また、GCPのマニュアルですが、ReadWriteOnceはDeployment(ReplicaSet)では使用しないで、StatefulSetで使用するものだと書かれています。
Deployment はステートレス アプリケーション用に設計されているため、Deployment のすべてのレプリカで同じ PersistentVolumeClaim が共有されます。作成されたレプリカ Pod はそれぞれ同一であるため、この設定では ReadOnlyMany モードと ReadWriteMany モードのボリュームだけが動作します。
・・・中略・・・
この場合は ReadWriteOnce ボリュームで StatefulSet を使用します。
###StatefulSetの作成
DeploymentからStatefulSetに変えて、再度確認します。
まず、Deploymentは削除しておきます。
$ kubectl delete -f nginx-dep-rwo.yaml
deployment.apps "nginx-dep" deleted
以下のマニフェストのStatefulSetをapplyします。
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を作成します。
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
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadOnlyMany
resources:
requests:
storage: 500Mi
storageClassName: slow
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の動作も確認できました。