目次
StatefulSetとは
DBなどのステートフルなアプリケーションのためのリソース。
つまり、Podを再度作成しても、同じPod名、host名で作成される。
Pod起動、停止、削除時の挙動
- 新たにPodが起動されるたびに、以下のように末尾に連番で番号が付与されていく。
{Pod name}-0, {Pod name}-1, {Pod name}-2・・・
- Pod起動時は、ナンバリングが小さいものから起動する
- Pod削除時は、ナンバリングが大きいものから停止、削除される
Podのスケーリング時
Podのスケーリング時には、StatefulSetのPodが全てReadyになっている必要がある。
$ sudo kubectl get sts
NAME READY AGE
sample-statefuleset 2/2 26m
Podのローリングアップデート
Podの停止、削除と同様、番号の大きなPodから実施される。
Volumeについて
- Podが起動すると、前回と同じPersistentVolumeClaimsが使用される
- Podが削除されても、使用していたPersistentVolumeClaimsは削除されない
担保されること
- Stable, unique network identifiers.(固定のネットワーク識別子)
- Stable, persistent storage.(固定の永続ストレージ)
- Ordered, graceful deployment and scaling.(順番を担保したデプロイとスケーリング)
- Ordered, automated rolling updates.(順番を担保した削除と終了)
Node、Podの障害時の挙動について
StatefulSetではPodのSTATUSがUnknownになってもPodを自動起動しない。
どういうことなのか、まずはkubleteによるPodの監視方法について理解する必要がある。
kubeletによるPodの監視方法について
各Nodeのkubeletが、masterに定期的にPodの状態を報告することでPodの状況を監視している。
つまり、Nodeの障害などでkubeletが落ちると、masterはPodの状態が確認できなくなる。
Deploymentなどでは、このようなNodeの障害が起きた場合、別のNodeに新たなPodを自動起動することでリカバリをする。
しかし、kubeletが落ちているが、Podは落ちていないという状況もあり得る。
この状況で別Nodeに新たにPodを起動すると、Podの2重起動という状況になる。
Volumeをマウントしている場合、複数のPodからの書き込みが発生することになる。
この状況は、アプリケーションによってはデータが壊れる可能性がある。
StatefulSetでkubeletに障害が起きた場合の挙動
StatefulSetではこれを防ぐために、kubeletが落ちても新たにPodの自動起動をしない。
言い換えると、Podを手動で削除しない限り、新たなPodは起動されない。
Podを自動で起動してほしい場合
Deploymentなど、StatefulSet以外のリソースを使う必要がある。
以下の記事がわかりやすい。
参考:KubernetesのStatefulSetを疑ってみたが濡れ衣だった
ハンズオン
StatefulSetの定義
以下の例ではNFSにmountしている。NFSにmountする方法は以下を参照。
参考:【KubernetesのPersistentVolume】その2- Dynamic Provisionerを使ってNFSサーバーをPodにmountする
kind: StatefulSet
apiVersion: apps/v1beta1
metadata:
name: sample-statefuleset
spec:
serviceName: statefuleset-service
replicas: 2
# これを定義することでRollingUpdateされる
updateStrategy:
# RollingUpdate or OnDelete。Default: RollingUpdate
# OnDeleteだとPodは自動的に更新されず、手動でPodを削除すると、自動的に新しいPodが起動される。
type: RollingUpdate
##############################################################
# partitionを設定すると、指定した番号以上のPodのみが更新される。
# 以下の例だと、{Pod名}-1以上のPodが更新され、{Pod名}-0は更新されない。
##############################################################
#rollingUpdate:
# partition: 1
# Podの定義
template:
metadata:
labels:
app: sample-statefuleset
spec:
containers:
- name: sample-container
image: gcr.io/google-samples/hello-app:1.0
volumeMounts:
- name: sample-pvc
mountPath: /var/data
# 以下のTemplateでPersistentVolumeClaimを作成する
volumeClaimTemplates:
- metadata:
name: sample-pvc
spec:
resources:
requests:
storage: 1Gi
accessModes:
- ReadWriteOnce
# storageClassNameを省略するとHostPathでホストOS上にmountされる
storageClassName: nfs
作成
$ sudo kubectl apply -f statefulset.yaml
確認
Podが2つ起動している。
$ sudo kubectl get pod
sample-statefuleset-0 1/1 Running 0 25s
sample-statefuleset-1 1/1 Running 0 16s
statefulsetが作成されている。
$ sudo kubectl get statefulset
NAME READY AGE
sample-statefuleset 2/2 96s
PersistentVolumeClaimが作成されている。
$ sudo kubectl get pvc
sample-pvc-sample-statefuleset-0 Bound pvc-092e4166-cc92-460b-b9b7-5361dffdebe5 1Gi RWO nfs 4m
sample-pvc-sample-statefuleset-1 Bound pvc-6ae2f4ca-ce4d-4a67-8c0b-a9673d63002f 1Gi RWO nfs 3m55s
NFSのPersistentVolumeが作成されている。
$ sudo kubectl get pv
pvc-092e4166-cc92-460b-b9b7-5361dffdebe5 1Gi RWO Retain Bound default/sample-pvc-sample-statefuleset-0 nfs 4m12s
pvc-6ae2f4ca-ce4d-4a67-8c0b-a9673d63002f 1Gi RWO Retain Bound default/sample-pvc-sample-statefuleset-1 nfs 4m7s
Podを削除してもmount領域が変わっていないか確認
Podを削除して再度Podを起動しても、同じ領域がmountされることを確認する。
Pod削除前に確認
NFSサーバーのmount領域を確認。以下のディレクトリが作成されていることを確認。
$ ls -1 /var/share/nfs
default-sample-pvc-sample-statefuleset-0-pvc-092e4166-cc92-460b-b9b7-5361dffdebe5
default-sample-pvc-sample-statefuleset-1-pvc-6ae2f4ca-ce4d-4a67-8c0b-a9673d63002f
sample-statefuleset-0用のテストファイルを作成しておく。
$ touch /var/share/nfs/default-sample-pvc-sample-statefuleset-0-pvc-092e4166-cc92-460b-b9b7-5361dffdebe5/sample001.txt
sample-statefuleset-1用のテストファイルを作成しておく。
$ touch /var/share/nfs/default-sample-pvc-sample-statefuleset-1-pvc-6ae2f4ca-ce4d-4a67-8c0b-a9673d63002f/sample002.txt
作業前にPodから参照できることを確認
sample-statefuleset-0用のテストファイルが見えることを確認。
$ sudo kubectl exec -it sample-statefuleset-0 ls /var/data
sample001.txt
sample-statefuleset-1用のテストファイルが見えることを確認。
$ sudo kubectl exec -it sample-statefuleset-1 ls /var/data
sample002.txt
Podを削除
$ sudo kubectl delete -f statefulset.yaml
statefulset.apps "sample-statefuleset" deleted
Podを再作成
$ sudo kubectl apply -f statefulset.yaml
statefulset.apps/sample-statefuleset created
作業後のNFSのmount領域を確認
NFSサーバーのmount領域を確認。Pod削除前のディレクトリが残っていることを確認。
新たにディレクトリが生成されていないことを確認。
[NFSサーバー]$ ls -1 /var/share/nfs
default-sample-pvc-sample-statefuleset-0-pvc-092e4166-cc92-460b-b9b7-5361dffdebe5
default-sample-pvc-sample-statefuleset-1-pvc-6ae2f4ca-ce4d-4a67-8c0b-a9673d63002f
sample-statefuleset-0用のテストファイルがあることを確認
[NFSサーバー]$ ls -1 /var/share/nfs/default-sample-pvc-sample-statefuleset-0-pvc-092e4166-cc92-460b-b9b7-5361dffdebe5
sample001.txt
sample-statefuleset-1用のテストファイルがあることを確認
[NFSサーバー]$ ls -1 /var/share/nfs/default-sample-pvc-sample-statefuleset-1-pvc-6ae2f4ca-ce4d-4a67-8c0b-a9673d63002f
sample002.txt
作業後もPodから参照できることを確認
sample-statefuleset-0用のテストファイルが見えることを確認。
$ sudo kubectl exec -it sample-statefuleset-0 ls /var/data
sample001.txt
sample-statefuleset-1用のテストファイルが見えることを確認。
$ sudo kubectl exec -it sample-statefuleset-1 ls /var/data
sample002.txt
Rolling Update
Rolling Updateをしてみる。
Podのイメージを更新
Podのコンテナイメージのversionを変更してapplyする。
参考:KubernetesでPodをローリングアップデートをする
kind: StatefulSet
apiVersion: apps/v1beta1
metadata:
name: sample-statefuleset
spec:
# 省略
template:
# 省略
spec:
containers:
- name: sample-container
- image: gcr.io/google-samples/hello-app:1.0
+ image: gcr.io/google-samples/hello-app:2.0
volumeMounts:
- name: sample-pvc
mountPath: /var/data
# 省略
Rolling Update実行
$ sudo kubectl apply -f statefulset.yaml
statefulset.apps/sample-statefuleset configured
Rolling Update開始
状況を確認。
ナンバリングが大きいものからアップデートされていくことが分かる。
$ sudo kubectl get pod -w
NAME READY STATUS RESTARTS AGE
# 旧Pod
sample-statefuleset-0 1/1 Running 0 73m
sample-statefuleset-1 1/1 Running 0 73m
sample-statefuleset-2 1/1 Running 0 61m
# 旧Pod2が破棄される。(ナンバリングが大きいものから破棄される)
sample-statefuleset-2 1/1 Terminating 0 78m
sample-statefuleset-2 0/1 Terminating 0 78m
sample-statefuleset-2 0/1 Terminating 0 78m
sample-statefuleset-2 0/1 Terminating 0 78m
sample-statefuleset-2 0/1 Pending 0 0s
sample-statefuleset-2 0/1 Pending 0 0s
sample-statefuleset-2 0/1 ContainerCreating 0 0s
# 新Pod2が旧Podと同じ名前で起動した。
sample-statefuleset-2 1/1 Running 0 4s
# 旧Pod1が破棄される。
sample-statefuleset-1 1/1 Terminating 0 90m
sample-statefuleset-1 0/1 Terminating 0 90m
sample-statefuleset-1 0/1 Terminating 0 90m
sample-statefuleset-1 0/1 Terminating 0 90m
sample-statefuleset-1 0/1 Pending 0 0s
sample-statefuleset-1 0/1 Pending 0 0s
sample-statefuleset-1 0/1 ContainerCreating 0 0s
# 新Pod1が旧Podと同じ名前で起動した。
sample-statefuleset-1 1/1 Running 0 4s
# 旧Pod0が破棄される。
sample-statefuleset-0 1/1 Terminating 0 91m
sample-statefuleset-0 0/1 Terminating 0 91m
sample-statefuleset-0 0/1 Terminating 0 91m
sample-statefuleset-0 0/1 Terminating 0 91m
sample-statefuleset-0 0/1 Pending 0 0s
sample-statefuleset-0 0/1 Pending 0 0s
sample-statefuleset-0 0/1 ContainerCreating 0 0s
# 新Pod0が旧Podと同じ名前で起動した。
sample-statefuleset-0 1/1 Running 0 5s