一覧CL-12-3 · Kubernetesの接続と保存

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/null

store-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

1
kubectl apply -f headless.json -f statefulset.json kubectl wait --for=condition=Ready pod/store-0 --timeout=90s > /dev/null
service/store created
statefulset.apps/store created
2
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
3
kubectl exec store-0 -- sh -c 'printf "counter=7\n" > /data/state.txt; cat /data/state.txt' python3 inspect.py save
counter=7
saved=Pod UID, PVC UID, PV name
4
kubectl delete pod store-0 --wait=true > /dev/null kubectl wait --for=condition=Ready pod/store-0 --timeout=90s > /dev/null python3 inspect.py identity
pod_name=store-0
pod_uid_changed=True
pvc_uid_same=True
volume_name_same=True
5
kubectl exec store-0 -- cat /data/state.txt
counter=7
6
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
7
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 identity
statefulset.apps/store scaled
counter=7
pod_name=store-0
pod_uid_changed=True
pvc_uid_same=True
volume_name_same=True

演習完了