手元のクラスタにReadWriteOnce
のPVがあったので、これをReadWriteOncePod
に変更したので、その時に調査した内容を紹介します。
ReadWriteOncePod
はKubernetes1.22でAlpha機能ととして登場し、1.27からBetaとなり、最新の1.29でGAした機能です。
基本的な動作については以下の記事が詳しいです。
さて、今回はすでにデプロイしてしまっているPVについて、1つのPodからしか利用していないのでReadWriteOncePod
に変更することをやってみました。
なぜ実施するのか?
ReadWriteOnce
に設定したVolumeは同じNodeに配置された他のPodからもマウントすることが出来ますが、多くの場合そのようなマウントができることは不要です。
セキュリティ文脈における最小権限の原則からも、不要なことは出来ないようにしておくことが望ましいです。
PVをeditする
まず変更実施すべきはPVです。
実体として1つのPodしかVolumeをマウントしていないケースにおいては単純にPVをeditしてAccessMode
をReadWriteOncePod
に変更することで、これを実現できます。
PVCはどうなる?
PVをeditしたときPVCはどうなるのかが気になって調べてみました。
結果としてはPVCのspecのaccessModes
はそのままReadWriteOnce
で、statusのaccessMode
がReadWriteOncePod
となりました。
apiVersion: v1
kind: PersistentVolumeClaim
...
spec:
accessModes:
- ReadWriteOnce # ← ここと
...
status:
accessModes:
- ReadWriteOncePod # ← ここ
phase: Bound
...
この状態で、PVはReadWriteOncePod
となっているので、他のPodからはマウントできない状態となっており、機能としては意図した状態になっているといえばそうです。
また、kubectl get pvc
の結果はstatusを表示しているので、一見すると問題はなさそうに見えます。
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc Bound pvc-xxxxxx 3Gi RWOP sc-xxxxxxxx XdYh
しかし、このようなちょっとイレギュラーな状態は、Kubernetesのコーナーケースに当たるため、なにか今後の機能変更時にバグを引き当てたりしそうなので、なるべく避けたいという気持ちもあります。
PVCも修正したい
PVCのspecのaccessModes
は後から変更することが出来ないので、ここを変更するためには、PVCを一度削除して作り直す必要があります。
しかし、PVのReclaimPolicy
がDelete
になっている場合は、PVCを削除することで対応するPVが一緒に削除されてしまうので作業には注意が必要です。
前節で説明したようにPVをeditしてReadWriteOncePod
にしておけば、PVCのspecがReadWriteOnce
でも、別のPodからはマウントできなくなるので、PVCはそのままにしておく、というのも一つの手かなと思いました。
一方でこの方法は、Kubernetesのマニフェストをリポジトリなどで管理している場合、そのマニフェストを流用して別のシステムを構築する際に、PVがReadWriteOnce
で作られてしまうという問題があります。
かといって、PVCのマニフェストだけを修正すると、今度は構成変更を反映するためにそのマニフェストをapplyする際に変更できないエラーとなってしまい、これも不便です。
ということで、以降はPVCを修正する方法を見ていきます。
StatefulSetを使いPVCを作っている場合
StatefulSetのvolumeClaimTemplates
を使ってPVCを作っている場合は、管理しているマニフェストはStatefulSetのものなので、まず修正すべきはStatefulSetです。
StatefulSetのvolumeClaimTemplates
は後から変更できないので、StatefulSetの作り直しが必要です。
StatefulSetを消してもPVCは残るので、StatefulSetを消して、accessModes
をReadWriteOncePod
に変更したStatefulSetをデプロイすることで、管理しているマニフェストと実環境の矛盾を解消できます。
さて、StatefulSetのみを変更してデプロイし直しができればこれで十分でしょうか?
この際、PVCは以前のStatefulSetの作ったものが引き続き利用されるので、PVCのspecのaccessModes
はReadWriteOnce
のままという状況になります。
実体としてのPVはReadWriteOncePodに変更済みなので、実質的な問題はなさそうです。
しかし、やはりPVとPVC、StatefulSetのvolumeClaimTemplatesのaccessModeが異なることは、ややこしいので、後に紹介する方法でPVCも作り直すのが良いと考えます。
謎のPending
この作業をやっている時に、StatefulSetのYAMLに妙なフィールドがあることに気づきました。
.spec.volumeClaimTemplates.statusというフィールドです。
以下のように何故か、specの中なのにstatusというフィールドが有り、ずっとPendingという値になっています。
この件は一応 https://github.com/kubernetes/kubernetes/issues/65706 でバグ報告されていたようですが、特に対応されずCloseされています。
他のリソースの様子をみても問題はないので、Pendingであることを気にする必要はなさそうです。
$ kubectl get statefulset -n hedgedoc -o yaml
apiVersion: v1
items:
- apiVersion: apps/v1
kind: StatefulSet
...
spec:
...
volumeClaimTemplates:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
name: test-ss
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
storageClassName: test-ss
volumeMode: Filesystem
status:
phase: Pending # <---- これ
...
status:
PVCを作り直す
PVの内容が消えて良い場合は単純にPVCを消せばOKですが、一般的にPVの内容は保持したいと思うので、その前提で手順を紹介します。
該当PVのpersistentVolumeReclaimPolicy
を変更して、PVCと連動して削除されないようにする。
kubectl patch pv <PVの名前> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
この手順では事前にPVCを利用しているPodはすべて削除する必要があります。
PVCを削除
kubectl delete pvc <PVCの名前>
PVがRelease状態になるので、再び利用できるようにする
kubectl patch pv <PVの名前> -p '{"spec":{"claimRef": null}}'
(これ以降に次のPVC作成の手順を実施するまでの間に、何処か別の仕組みでPVCが作られると、このPVがBindされてしまう事があるので注意)
accessModes
をReadWriteOncePod
に変更したPVCを作成
StatefulSetでPVCを作る場合は、volumeClaimTemplates
を変更すると読み替えてください。
この際.spec.volumeName
で紐づけしたいPVの名前を指定することで、意図通りのPVに紐付けることが出来ます。
(一方で.spec.volumeName
はあとから編集できないフィールドなので、これをマニフェストファイルに記載して、リポジトリで管理するような場合は、このフィールドの記載がこのクラスタに特化した内容になってしまう点に注意が必要です。)
# 一例です
kubectl apply -f pvc.yaml
これで、PVもPVCもReadWriteOncePod
を設定できました。
PVのpersistentVolumeReclaimPolicy
をもとに戻す
kubectl patch pv <PVの名前> -p '{"spec":{"persistentVolumeReclaimPolicy":"Delete"}}
まとめ
新しく使えるようになったReadWriteOncePod
を既存のVolumeに適用する方法を紹介しました。
PV周りは、ちょっと手順を間違えるとデータが消えてしまったりするので、慎重な作業が必要です。
不要なPodからのマウントを防ぐことは、特にKubernetesクラスタを他のチームと共用している場合などに非常に有効な設定です。手順はちょっと面倒ですが、早めに対応するのが良いでしょう。
参考