はじめに #
KubernetesのpostStartフックについて調べていたところ、公式ドキュメントに終了コードの詳細な記載がないことに気づきました。
実際にexit 0とexit 1を返した場合の挙動がどう違うのか、minikube環境で検証してみました。
postStartフックとは? #
postStartは、KubernetesのContainer Lifecycle Hookの一つで、コンテナが起動した直後に実行される処理を定義できます。
基本的な仕組み #
- コンテナのENTRYPOINT/CMDプロセスが開始された直後に実行される
- 同期的に処理されるため、フックが終了するまでコンテナは「Running」状態にならない
- 終了コードによってコンテナの運命が決まる:
- exit 0(成功) → コンテナは正常に起動を続行
- exit 1(失敗) → コンテナは即座に終了し、再起動ポリシーに従って再起動
検証環境 #
minikube version: v1.36.0
Kubernetes version: Server Version: v1.33.1
実際の検証 #
成功パターン(exit 0) #
apiVersion: v1
kind: Pod
metadata:
name: poststart-exit0
spec:
containers:
- name: test
image: busybox
command: ["sh", "-c", "sleep 3600"]
lifecycle:
postStart:
exec:
command: ["sh", "-c", "echo 'postStart success'; exit 0"]
結果:
kubectl apply で成功パターンのYAMLをデプロイすると以下の結果になります。
コンテナは正常にRunning状態になり、postStartフックが成功したことが確認できます。
$ k get po poststart-exit0
NAME READY STATUS RESTARTS AGE
poststart-exit0 1/1 Running 0 11s
失敗パターン(exit 1) #
apiVersion: v1
kind: Pod
metadata:
name: poststart-exit1
spec:
containers:
- name: test
image: busybox
command: ["sh", "-c", "sleep 3600"]
lifecycle:
postStart:
exec:
command: ["sh", "-c", "echo 'postStart will fail'; exit 1"]
結果:
kubectl apply で失敗パターンのYAMLをデプロイすると以下の結果になります。
コンテナはPostStartHookError状態になり、再起動を繰り返します。
tech-0222@MSI:root$ k get po poststart-exit1 -w
NAME READY STATUS RESTARTS AGE
poststart-exit1 0/1 ContainerCreating 0 2s
poststart-exit1 0/1 PostStartHookError 0 32s
poststart-exit1 0/1 PostStartHookError 1 (0s ago) 64s
poststart-exit1 0/1 CrashLoopBackOff 1 (1s ago) 65s
poststart-exit1 0/1 PostStartHookError 2 (0s ago) 112s
poststart-exit1 0/1 CrashLoopBackOff 2 (1s ago) 113s
イベントログの確認 #
失敗時のイベントログを見るとFailedPostStartHookとKillingイベントが発生し、コンテナが強制終了されていることがわかります。
# poststart-exit1はPOD名
$ kubectl get events --field-selector involvedObject.name=poststart-exit1 --sort-by=.metadata.creationTimestamp
LAST SEEN TYPE REASON OBJECT MESSAGE
91s Normal Scheduled pod/poststart-exit1 Successfully assigned default/poststart-exit1 to k8s-test
11s Normal Pulling pod/poststart-exit1 Pulling image "busybox"
90s Normal Pulled pod/poststart-exit1 Successfully pulled image "busybox" in 1.368s (1.368s including waiting). Image size: 2223685 bytes.
10s Normal Created pod/poststart-exit1 Created container: test
10s Normal Started pod/poststart-exit1 Started container test
10s Warning FailedPostStartHook pod/poststart-exit1 PostStartHook failed
10s Normal Killing pod/poststart-exit1 FailedPostStartHook
58s Normal Pulled pod/poststart-exit1 Successfully pulled image "busybox" in 1.397s (1.397s including waiting). Image size: 2223685 bytes.
26s Warning BackOff pod/poststart-exit1 Back-off restarting failed container test in pod poststart-exit1_default(2d08490a-d216-4995-856c-6f4617835f72)
10s Normal Pulled pod/poststart-exit1 Successfully pulled image "busybox" in 1.342s (1.342s including waiting). Image size: 2223685 bytes.
終了コード exit code 137 #
Podの詳細を確認すると、終了コードが137になっています。
$ kubectl get pod poststart-exit1 -o yaml | grep -A5 "lastState:"
lastState:
terminated:
containerID: containerd://19b93fec260434f98f9dfbfe6f2f4518bef7507e5438115cc474f701f2654029
exitCode: 137
reason: Error
なぜ137なのか? #
137 = 128 + 9 (SIGKILL)→ プロセスがkill -9された時の終了コードpostStartが失敗すると、Kubernetesがコンテナ全体を強制終了するため- 実際の
postStartフック自体はexit 1で失敗しているが、最終的にコンテナはSIGKILLで終了される
終了コードの仕組み #
一般的に終了コードは以下のように分類されてるみたいです。
0: 正常終了
1-127: プログラムが自分で設定したエラーコード
128-255: シグナルによる終了(`終了コード = 128 + シグナル番号`)
主要なシグナル終了コード:
130 = 128 + 2→ SIGINT(Ctrl+C)137 = 128 + 9→ SIGKILL(強制終了)143 = 128 + 15→ SIGTERM(終了要求)
この仕組みにより、終了コードを見るだけで「プログラムのエラー」「シグナルによる終了」なのかを判断できるようです。
まとめ #
今回の検証で以下のことが確認できました。
- exit 0 → コンテナは正常に起動し続ける
- exit 1 → コンテナは即座に終了し、再起動を繰り返す
- 失敗時は
PostStartHookErrorステータスになる - 最終的な終了コードは
137(SIGKILL)になる
公式ドキュメントには明記されていませんが、実際の挙動は明確に終了コードによって制御されていることがわかりました。
postStartフックを実装する際は、必ずexit 0で終了するように注意しましょう。