pod(Kubernetes)のlifecycle.prestopの挙動
コンテナ削除時すぐにコンテナをSTOPされると困ることありません?
例えば….?
ApacheやNginxなどのWebサーバはSIGTERMが送られても、処理中のコネクションがCloseされないまま終了してしまう。 理想としては、残っている接続済みのコネクションを終了してからコンテナがSTOPして欲しいですよね。
Prestopを設定する
SIGTERMでの挙動は以下のようなもの(https://httpd.apache.org/docs/2.2/ja/stopping.htmlより引用)
TERM あるいは stop シグナルを親プロセスに送ると、即座に子プロセス全てを kill しようとします。 子プロセスを完全に kill し終わるまでに数秒かかるかもしれません。 その後、親プロセス自身が終了します。 処理中のリクエストは全て停止され、もはやリクエストに対する 応答はされません。
これでは、まずいのでPrestopフックを入れます。PrestopフックはPodのシャットダウンプロセスの最初に実行されるものです。Deploymentの.spec.template.spec.containers.lifecycle.preStop
に処理を記載していきます。httpGet
等でhttpリクエストを引っ掛けることもできますが、基本的にはexec
で記述することが多いように思えます。exec
は記述したコマンドの同一のコンテナで実行します。
Apacheのwebサーバの場合だと、httpd -k graceful-stop
をDeploymentの.spec.template.spec.containers.lifecycle.preStop.exec.command
に書いていくような形となるかと思います。Pod preStop フックの実行と Service エンドポイントからの Pod IP アドレス削除は並列して実行されるため、preStop フックの実行が先に実行されてしまいアクセスがまだ来る状態でコネクション作成をやめてしまうことになります。
なので、httpd -k graceful-stop
の前に数秒置く必要があります。また、指定したコマンドが非同期の場合には、勝手にpreStopフックが完了されたとみなし、SIGTERMがPodのコンテナに送られてしまいます。そのため、非同期のコマンドのあとにはそのコマンドでかかる時間sleepしておくなどするべきです。
もしapacheのリクエストのタイムアウト時間を30秒としているならば、以下のような設定となるかと思います。
terminationGracePeriodSeconds
はpodのシャットダウンプロセスが始まってからSIGKILLが送られるまでの時間です。preStopの設定を入れている場合は、preStop処理が終了後にSIGTERMが送られ、シャットダウンプロセスが始まってからterminationGracePeriodSeconds
で指定した秒数が経過してもプロセスが完了していない場合にはSIGLKILLが送られます。デフォルトは30秒となっているはずです。
apiVersion: v1 kind: Pod metadata: name: lifecycle-demo spec: containers: - name: lifecycle-demo-container image: httpd lifecycle: preStop: exec: command: ["sh", "-c", "sleep 2; httpd -k graceful-stop; sleep 30"] terminationGracePeriodSeconds: 40
kubernetesのソースコード上では以下のように、preStopが実装されてます。 preStopの定義がDeploymentにない場合は、すぐにStopContainerが送られてしまうのがわかると思います。
// Run the pre-stop lifecycle hooks if applicable and if there is enough time to run it if containerSpec.Lifecycle != nil && containerSpec.Lifecycle.PreStop != nil && gracePeriod > 0 { gracePeriod = gracePeriod - m.executePreStopHook(pod, containerID, containerSpec, gracePeriod) } // always give containers a minimal shutdown window to avoid unnecessary SIGKILLs if gracePeriod < minimumGracePeriodInSeconds { gracePeriod = minimumGracePeriodInSeconds } if gracePeriodOverride != nil { gracePeriod = *gracePeriodOverride glog.V(3).Infof("Killing container %q, but using %d second grace period override", containerID, gracePeriod) } err := m.runtimeService.StopContainer(containerID.ID, gracePeriod) if err != nil { glog.Errorf("Container %q termination failed with gracePeriod %d: %v", containerID.String(), gracePeriod, err) } else { glog.V(3).Infof("Container %q exited normally", containerID.String()) }
Prestopフックですが、標準出力などに吐き出してもStackdriverなどでは拾えないので注意です。なお失敗した場合には、kubectl describe <pod>
のイベントのところに表示されます。