StatefulSetと永続ボリューム
名前が同じでもPodは別の実体であることと、同じPVCを再利用することでデータを引き継げることを説明します。
説明
StatefulSetはPodに順序付きの安定した名前を付け、volumeClaimTemplatesからPodごとのPVCを作れます。PVCは永続領域を要求する資源で、PVはその要求に割り当てられた保存領域です。Podの再作成と、保存領域の削除は別の操作です。
1個のstore-0とdata-store-0というPVCを作り、/dataへ値を書きます。Podを削除して再作成させ、名前・UID・PVC・保存値を比較します。最後に台数を0へ減らしてから戻します。
名前が同じでもPodは別の実体であることと、同じPVCを再利用することでデータを引き継げることを説明します。
1保存領域付きのPodを作る
headless ServiceとStatefulSetを適用します。PVCの要求は16Mi、アクセスモードはReadWriteOnceです。この環境ではローカルの動的プロビジョナーが領域を用意します。
kubectl applyServiceとStatefulSetを作る -f headless.json -f statefulset.json
kubectl wait保存領域が割り当てられたPodのReadyを待つ --for=condition=Ready pod/store-0 --timeout=90s > /dev/nullstore-0が起動しました。要求した16MiはPVCの宣言値であり、使用量を測定した数字ではありません。
2PodからPVCへの参照
PodのvolumeとPVCの状態を読みます。
kubectl get pod store-0Podが参照するPVC名を読む -o jsonpath='{.spec.volumes[0].persistentVolumeClaim.claimName}{"\n"}'
kubectl get pvc data-store-0PVCの割当状態とアクセスモードを読む -o jsonpath='{.status.phase}{" "}{.spec.accessModes[0]}{"\n"}'data-store-0を参照し、PVCはBoundです。ReadWriteOnceは単一ノードへの読書きマウントを表し、単一プロセスの排他ロックを保証する指定ではありません。
3再作成前の値と識別子
/dataへcounter=7を書き、PodとPVCのUID、割当先PV名を保存します。
kubectl exec store-0マウントした領域に書いて読む -- sh -c 'printf "counter=7\n" > /data/state.txt; cat /data/state.txt'
python3 inspect.py savePod・PVC・PVの対応を保存する保存値はcounter=7です。次は値だけでなく、どの資源が入れ替わったかも比較します。
4Podだけを削除する
StatefulSetを残してstore-0を削除すると、必要数1へ戻すためPodが再作成されます。
kubectl delete pod store-0Podを削除する --wait=true > /dev/null
kubectl wait補充されたPodを待つ --for=condition=Ready pod/store-0 --timeout=90s > /dev/null
python3 inspect.py identityPodと保存領域の識別子を照合する名前はstore-0のままですがPodのUIDは変わります。PVCのUIDとPV名は同じです。名前が同じことだけを根拠に同一プロセスだと判断できません。
5保存値が残ることを確認
新しいPodから同じパスを読みます。
kubectl exec store-0 -- cat /data/state.txt新しいPodから永続データを読むcounter=7が残っています。新しいコンテナの書込み層に移したのではなく、同じPVCの領域を再びマウントしています。
6台数を0へ減らす
replicasを0へ変更し、Podがなくなるまで待ちます。PVCの保持方針は既定のRetainです。
kubectl scale statefulset store --replicas=0必要なPod数を0へ変える
kubectl waitPodの削除完了を待つ --for=delete pod/store-0 --timeout=60s > /dev/null
kubectl get pvc data-store-0PVCの保持を確認する -o jsonpath='{.status.phase}{"\n"}'PodがなくてもPVCはBoundで残ります。台数を減らすことがデータの削除と同義ではありません。
7台数を戻して再利用する
必要数を1へ戻します。
kubectl scale statefulset store --replicas=1必要数を1へ戻す
kubectl waitPodの起動を待つ --for=condition=Ready pod/store-0 --timeout=90s > /dev/null
kubectl exec store-0 -- cat /data/state.txt保存値を読み直す
python3 inspect.py identity同じPVCとPVを確認するcounter=7と同じPVCが再び使われます。Podの寿命、PVCの保持方針、PVのreclaimPolicyを別々に確認する必要があります。
まとめ
StatefulSetの安定した名前とPodのUIDは別の識別子です。
Podを作り直しても、同じPVCの領域を使えばデータを引き継げます。
台数変更時の保持とPVC削除後のPV回収は別の方針です。
操作履歴と解説を振り返れます。
Kubernetes 1.37.0 / kind 0.33.0 / Linux arm64
kubectl apply -f headless.json -f statefulset.json
kubectl wait --for=condition=Ready pod/store-0 --timeout=90s > /dev/nullservice/store created statefulset.apps/store created
kubectl get pod store-0 -o jsonpath='{.spec.volumes[0].persistentVolumeClaim.claimName}{"\n"}'
kubectl get pvc data-store-0 -o jsonpath='{.status.phase}{" "}{.spec.accessModes[0]}{"\n"}'data-store-0 Bound ReadWriteOnce
kubectl exec store-0 -- sh -c 'printf "counter=7\n" > /data/state.txt; cat /data/state.txt'
python3 inspect.py savecounter=7 saved=Pod UID, PVC UID, PV name
kubectl delete pod store-0 --wait=true > /dev/null
kubectl wait --for=condition=Ready pod/store-0 --timeout=90s > /dev/null
python3 inspect.py identitypod_name=store-0 pod_uid_changed=True pvc_uid_same=True volume_name_same=True
kubectl exec store-0 -- cat /data/state.txtcounter=7
kubectl scale statefulset store --replicas=0
kubectl wait --for=delete pod/store-0 --timeout=60s > /dev/null
kubectl get pvc data-store-0 -o jsonpath='{.status.phase}{"\n"}'statefulset.apps/store scaled Bound
kubectl scale statefulset store --replicas=1
kubectl wait --for=condition=Ready pod/store-0 --timeout=90s > /dev/null
kubectl exec store-0 -- cat /data/state.txt
python3 inspect.py identitystatefulset.apps/store scaled counter=7 pod_name=store-0 pod_uid_changed=True pvc_uid_same=True volume_name_same=True
演習完了