はじめに
Kubernetesはコンテナの起動直後に任意のコマンドを実行する「postStart」と、コンテナの終了直前に任意のコマンドを実行する「preStop」ハンドラーがあります。
今回はこのpostStart/preStopの動作を確認します。
設定
以下のマニフェストで動作を確認します。
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
labels:
app: app1
spec:
containers:
- name: lifecycle-demo-container
image: nginx:latest
command: ["/bin/sh", "-c", "echo Hello from the Entrypoit >> /usr/share/message; sleep 999999"]
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", " echo Hello from the postStart handler >> /usr/share/message"]
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
postStartでは、/usr/share/messageファイルにメッセージを書き込むようにしています。
preStopでは、Podを削除する前にnginxを停止する処理を入れています。
また、postStart/preStopの他にspec.containers.commandによる通常のコマンド実行(Entrypoint)も設定しています。
動作確認
このマニフェストをapplyします。
$ kubectl apply -f postStart-preStop.yaml
pod/lifecycle-demo created
postStart
コンテナの/usr/share/messageの内容を確認します。
$ kubectl exec -it lifecycle-demo cat /usr/share/message
Hello from the Entrypoit
Hello from the postStart handler
Entrypoitの方が先に実行されていますね。マニュアルを確認すると以下の記載があります。
コンテナが作成された直後にKubernetesはpostStartイベントを送信します。 ただし、コンテナのエントリーポイントが呼び出される前にpostStartハンドラーが呼び出されるという保証はありません。postStartハンドラーはコンテナのコードに対して非同期的に実行されますが、postStartハンドラーが完了するまでコンテナのKubernetesによる管理はブロックされます。
コンテナライフサイクルイベントへのハンドラー紐付け
postStartの方が先に実行される保証はないと書かれていますが、私が何回か試した限りですと常にEntrypoitの方が先に実行されていました。(実行するコマンドの影響もあるかも知れません)
postStartとEntrypointを併用する場合には、実行される順番がランダムでもよい処理を設定する必要がありそうです。常に先に実行する必要がある場合には、Init Containersの利用も考えた方がよさそうです。
参考:[Kubernetes]Init Containersの動作を確認する
preStop
デプロイしたPodを削除します。
$ kubectl delete -f postStart-preStop.yaml
pod "lifecycle-demo" deleted
別ターミナルでPodの詳細をkubectl describeコマンドで確認します。
$ kubectl describe pod lifecycle-demo
Name: lifecycle-demo
・・・
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 15m default-scheduler Successfully assigned default/lifecycle-demo to k8s-worker01
Normal Pulling 15m kubelet, k8s-worker01 Pulling image "nginx:latest"
Normal Pulled 15m kubelet, k8s-worker01 Successfully pulled image "nginx:latest"
Normal Created 15m kubelet, k8s-worker01 Created container lifecycle-demo-container
Normal Started 15m kubelet, k8s-worker01 Started container lifecycle-demo-container
Normal Killing 15s kubelet, k8s-worker01 Stopping container lifecycle-demo-container
Warning FailedPreStopHook 15s kubelet, k8s-worker01 Exec lifecycle hook ([/usr/sbin/nginx -s quit]) for Container "lifecycle-demo-container" in Pod "lifecycle-demo_default(699078ca-3f29-440e-9f01-60eb9bfb9746)" failed - error: command '/usr/sbin/nginx -s quit' exited with 1: 2020/06/06 13:38:50 [notice] 17#17: signal process started
2020/06/06 13:38:50 [error] 17#17: open() "/var/run/nginx.pid" failed (2: No such file or directory)
nginx: [error] open() "/var/run/nginx.pid" failed (2: No such file or directory)
, message: "2020/06/06 13:38:50 [notice] 17#17: signal process started\n2020/06/06 13:38:50 [error] 17#17: open() \"/var/run/nginx.pid\" failed (2: No such file or directory)\nnginx: [error] open() \"/var/run/nginx.pid\" failed (2: No such file or directory)\n"
失敗してますね。/var/run/nginx.pidがないからエラーになっています。ただ、preStopとしては実行されていることがわかりますので、preStopの動作確認としては良しとします。
ちなみに、nginxの設定を修正して/var/run/nginx.pidがある状態でPodを削除するとエラーなく終了します。ただ、Eventsにメッセージが表示されないので、preStopが実行されたのかは分からなくなります。
今回はエラーが出たので、結果としてpreStopが動作できていることが確認できました。
まとめ
今回はpostStart/preStopの動作確認自体はあまり時間がかかりませんでしたが、nginxのエラーで時間を取られました。
また、postStartは実行される順番が保証されないので、使いどころが難しいですね。