Podを退勤させよう — 応答を返してから終了
目標
個人のk3s VMで、すでに処理中のHTTPリクエストを残したままPodを終了し、アプリの終了処理・EndpointSlice・終了猶予を結び付けて説明します。実際のプロセスを使い、kwokによる状態のシミュレーションではありません。
なぜ重要なのか
Deploymentの成功の表示は、処理中だったリクエストの完了を保証しません。ここでは、読み取り専用のコードで実行される小さなHTTPサーバーを置き、学生が作成したPodの宣言をヘルパーがそのまま生成します。まずService経路とリクエストの受け付けを確認し、Pod watchを開始した状態で削除するので、同じUIDのレスポンス・切断・終了コードを結び付けられます。
通常の終了と猶予不足を比較しますが、小数点以下の時間を当てるラボではありません。入力ファイルや観測ファイルをコピーして成功の文を作るのではなく、実際の実行とその解釈が合っているかを確認します。新しいリクエストの除外と既存の接続の終了は同じ出来事ではなく、この実験が外部ロードバランサーを含むサービス全体の無停止を保証するわけでもありません。
所要時間は約80分です。セッションの既定は60分なので、期限が切れる前に「+時間」で延長してください(最大180分)。セッションが終わるとVMとファイルは回収されます。必要な結果は、終了する前に別に保存してください。
ステップ
- 個人VMのnode_name、server_version、namespace_uid、imageを/root/ckad-inflight-termination/environment.jsonに記録し、run 1を実行してください。namespaceはckad-inflight-termination、imageは提供されたpod-template.jsonのサーバーイメージです。
- immediate.jsonを作成してください。Podの名前とcaseラベルはimmediate、MODEはimmediate、WORK_SECONDSは文字列の8、terminationGracePeriodSecondsは整数の15、lifecycleはなしです。run 2で、サーバーが受け付けたリクエストを、終了中に観測してください。
- immediate-analysis.jsonに、観測したpod_uid、client_error、exit_codeと、accepted_before_delete、readiness_alone_drains_requestsの判断を書き、run 3を実行してください。削除の前に受け付けられたかどうかと、readinessだけでリクエストを終わらせられるかどうかを区別します。
- graceful.jsonを作成してください。名前とcaseラベルはgraceful、MODEはgraceful、WORK_SECONDSは文字列の8、猶予は整数の15です。テンプレートのpreStopを維持して、/drainの呼び出しのあとに2秒待つようにし、run 4を実行してください。
- graceful-analysis.jsonに、pod_uidと、最初のterminating endpointのconditions全体をfirst_terminating_conditionsとして書いてください。hook_before_term、accepted_response_complete、endpoint_ready_false_means_connection_closedを観測と結び付けて判断し、run 5を実行してください。
- exhausted.jsonを作成してください。名前とcaseラベルはexhausted、MODEはgraceful、WORK_SECONDSは文字列の10、猶予は整数の5です。preStopの/drainのあとのsleepだけを3秒に変更し、run 6を実行してください。終了モードが同じでも、リクエストの結果が変わるかを確認します。
- repaired.jsonを作成してください。名前とcaseラベルはrepaired、MODEはgraceful、WORK_SECONDSは文字列の10、猶予は整数の15、preStopは/drainのあとに3秒です。run 7で、実際の完了レスポンスを確認してください。既存の観測ファイルは変更しません。
- comparison.jsonのoutcomesに、immediate・graceful・exhausted・repairedごとのpod_uid、client_completed、exit_codeを記録してください。grace_includes_prestop、api_delete_is_exact_deadline、service_zero_downtime_provenを判断し、run 8を実行してください。
参考
- 作業フォルダーは/root/ckad-inflight-terminationです。すべてのステップの実行は、python3 /opt/fixtures/ckad_termination_lab.py runにステップ番号を続けたコマンドです。採点は観測を再実行せず、保存されたファイルを読みます。
- pod-template.jsonをコピーして、必要なフィールドだけを変更してください。image・command・セキュリティ設定・runラベルは、実験の統制条件です。Podをkubectl applyで事前に作らないでください。runが学生の宣言で、作成・リクエスト・削除を1つの流れで観測します。
- JSONはKubernetesが受け取るマニフェスト形式です。環境変数のWORK_SECONDSは文字列、終了猶予は整数です。サンプルJSONでテンプレートの原本を上書きしないでください。
- preStopのexecコマンドはテンプレートにあります。/drainの呼び出しは維持し、最後のtime.sleepの数字をステップの要求どおりに変更します。即時終了のステップでは、lifecycle自体を削除します。
- 前のステップの準備は、prepareにステップ番号を続けたコマンドです。現在のステップの答えは作らず、すでに作成した学生のファイルも上書きしません。誤って作成した前のステップのファイルがあれば、先に直してください。
- 中断された観測の再実行では、以前のPodを復旧できません。エラーの履歴は保存し、ヘルパーが新しいラボを求めたら、目印を勝手に消さずに、環境を新しく開始してください。
- 終了コード17は、このサーバーが選んだ比較用の目印です。137だけでOOMや猶予不足と断定せず、watchのreason・実際の設定・hookとtermを一緒に見ます。
- 参考: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/ ・ https://kubernetes.io/docs/tutorials/services/pods-and-endpoint-termination-flow/
本物の厨房かを確認する
個人VMのnode_name、server_version、namespace_uid、imageを/root/ckad-inflight-termination/environment.jsonに記録し、run 1を実行してください。namespaceはckad-inflight-termination、imageは提供されたpod-template.jsonのサーバーイメージです。
kubectl get nodes、kubectl version -o json、kubectl get namespaceのmetadata.uidと、Podテンプレートを読みます。外部のクラスターには接続しません。
お客さんを残してすぐに帰る
immediate.jsonを作成してください。Podの名前とcaseラベルはimmediate、MODEはimmediate、WORK_SECONDSは文字列の8、terminationGracePeriodSecondsは整数の15、lifecycleはなしです。run 2で、サーバーが受け付けたリクエストを、終了中に観測してください。
pod-template.jsonから、名前・caseラベル・MODE・WORK_SECONDS・終了猶予・preStopだけを変更します。runがその宣言で、実際のリクエストと削除を観測します。
準備完了の表示では注文は終わらない
immediate-analysis.jsonに、観測したpod_uid、client_error、exit_codeと、accepted_before_delete、readiness_alone_drains_requestsの判断を書き、run 3を実行してください。削除の前に受け付けられたかどうかと、readinessだけでリクエストを終わらせられるかどうかを区別します。
observation-2.jsonのobservationから、initial.events、delete_at、client、pod_watchを結び付けてください。終了コードは、最後のコンテナのterminated状態にあります。
受けた注文まで仕上げてから帰る
graceful.jsonを作成してください。名前とcaseラベルはgraceful、MODEはgraceful、WORK_SECONDSは文字列の8、猶予は整数の15です。テンプレートのpreStopを維持して、/drainの呼び出しのあとに2秒待つようにし、run 4を実行してください。
pod-template.jsonから、名前・caseラベル・MODE・WORK_SECONDS・終了猶予・preStopだけを変更します。runがその宣言で、実際のリクエストと削除を観測します。
扉は閉まったのにレスポンスは届く
graceful-analysis.jsonに、pod_uidと、最初のterminating endpointのconditions全体をfirst_terminating_conditionsとして書いてください。hook_before_term、accepted_response_complete、endpoint_ready_false_means_connection_closedを観測と結び付けて判断し、run 5を実行してください。
observation-4.jsonのendpointsを時間順に読みます。server_eventsのhookとterm、client.completedを比較し、ready=falseを既存の接続の切断と同一視しないでください。
終了猶予を短くしすぎる
exhausted.jsonを作成してください。名前とcaseラベルはexhausted、MODEはgraceful、WORK_SECONDSは文字列の10、猶予は整数の5です。preStopの/drainのあとのsleepだけを3秒に変更し、run 6を実行してください。終了モードが同じでも、リクエストの結果が変わるかを確認します。
pod-template.jsonから、名前・caseラベル・MODE・WORK_SECONDS・終了猶予・preStopだけを変更します。runがその宣言で、実際のリクエストと削除を観測します。
猶予を補って、もう一度観測する
repaired.jsonを作成してください。名前とcaseラベルはrepaired、MODEはgraceful、WORK_SECONDSは文字列の10、猶予は整数の15、preStopは/drainのあとに3秒です。run 7で、実際の完了レスポンスを確認してください。既存の観測ファイルは変更しません。
pod-template.jsonから、名前・caseラベル・MODE・WORK_SECONDS・終了猶予・preStopだけを変更します。runがその宣言で、実際のリクエストと削除を観測します。
観測した分だけ結論を書く
comparison.jsonのoutcomesに、immediate・graceful・exhausted・repairedごとのpod_uid、client_completed、exit_codeを記録してください。grace_includes_prestop、api_delete_is_exact_deadline、service_zero_downtime_provenを判断し、run 8を実行してください。
ステップ2・4・6・7の観測を比較します。猶予バジェットにはフックも含まれますが、API削除の時刻が正確な終了時刻を保証するわけではなく、単一のリクエストがサービス全体の可用性を証明するわけでもありません。