TT Lab
はじめる
学ぶ 学習パス コース

CKAD — Kubernetesアプリケーション開発者

二重発送事件 — Jobの成功と業務の成功は違う

TT Labで続きを見る

目標

1つの注文を処理するJobが成功したのに、配送が2回作られるという出来事を再現し、再試行・冪等キー・実行期限を根拠にして説明します。

なぜ重要なのか

completionsとparallelismが1でも、外部の業務が1回だけ処理されるという保証にはなりません。失敗したプロセスが、すでに保存を終えている可能性があるからです。Kubernetesが管理する実行の寿命と、元帳が管理する業務の寿命を分けて観測してこそ、再実行するかどうかを判断できます。

個人VMの本物のk3sと、HTTP・SQLiteの擬似元帳を使います。実際の配送・決済・個人情報はありません。元帳のsafeモードは、サーバーの書き込みトランザクションとビジネスキーの一意性で、重複した行をまとめます。このラボはその契約の適用と検証を扱うもので、分散システム全体で正確に1回の処理を保証するものではありません。元帳のPodを削除すると、データも消えます。

最初の準備には最大6分が許容されます。すべてのファイルは/root/ckad-job-effectsの下に置きます。セッションが終了すると作業内容は消えるため、必要な記録はあらかじめダウンロードしてください。ラボは55分が目安で、さらに必要なら期限が切れる前に時間を延長してください。

使用するテンプレートと実行ヘルパー

job-template.jsonは、実際のworkerコマンドと固定イメージ、権限制限を含むJobのJSONです。ステップごとにコピーして、metadata.name、spec.backoffLimit、コンテナenvのCASE_ID・MODE・FAULTの値を変更します。Podテンプレートのspec.template.metadata.labelsにあるlabhub.io/caseも、そのCASE_IDと同じ値に変更します。WORK_IDはorder-001、POD_UIDはDownward APIのまま維持します。parallelism・completionsは1、restartPolicyはNeverを維持します。他のセキュリティ・アプリの設定も変更しません。期限が必要なステップにだけ、spec.activeDeadlineSecondsを追加します。

直接kubectl createで実行せず、ファイルを作成してからpython3 /opt/fixtures/ckad_job_effects_lab.py run <단계>(プレースホルダーはステップ番号です)を実行します。ヘルパーは学生のファイルをそのままAPIに送信し、実行中のPodの観測を保存します。すでに完了した同じステップをもう一度実行しても、新しいJobは作らず、採点だけを行います。観測の待機が中断された場合は、同じコマンドで再開し、Jobの削除・再作成は行いません。作成のレスポンス自体を失った場合は、重複作成の代わりに、新しいラボの開始を求めます。

ステップ

  1. baseline.json: name=baseline、CASE_ID=baseline、MODE=unsafe、FAULT=none、backoffLimit=0。run 1で、正常な比較群を観測します。
  2. unsafe-retry.json: name=unsafe-retry、CASE_ID=unsafe-retry、MODE=unsafe、FAULT=after-commit、backoffLimit=1。run 2で、保存後の失敗と代替Podを観測します。
  3. observation-2.jsonを読み、retry-analysis.jsonにjob_uid、failed_pod_uid、succeeded_pod_uid、request_count、shipment_count、retry_layerを書きます。retry_layerは、job-controllerまたはkubelet-containerのうち、観測に合うほうの値です。run 3で、分析を記録します。
  4. safe-retry.json: name=safe-retry、CASE_ID=safe-retry、MODE=safe、FAULT=after-commit、backoffLimit=1。run 4で、同じ失敗でも冪等な結果になることを確認します。
  5. safe-replay.json: name=safe-replay、CASE_ID=safe-retry、MODE=safe、FAULT=none、backoffLimit=0。run 5で、新しいJobでも同じビジネスキーが引き継がれるかを確認します。
  6. no-retry.json: name=no-retry、CASE_ID=no-retry、MODE=unsafe、FAULT=after-commit、backoffLimit=0。run 6で、失敗しても配送が残るかを確認します。
  7. deadline.json: name=deadline、CASE_ID=deadline、MODE=safe、FAULT=deadline、backoffLimit=0、activeDeadlineSeconds=20。run 7で、期限超過と元帳を観測します。現在のPod一覧が空でも、observed_podsにある過去の観測は消しません。
  8. 6つの実行を比較して、final-analysis.jsonを書きます。request_count・shipment_countは元帳全体の合計で、safe_replay_shipment_idは再実行で維持された配送番号です。timeout_cancels_effectとledger_survives_ledger_pod_replacementは、それぞれ、期限超過が業務を取り消すか、元帳のPodを入れ替えてもデータが保存されるか、のブール値です。idempotency_scopeは、実際の重複判定に使うフィールド名を+でつないで書きます。outcomesには、6つのJob名をキーとして置き、condition(Complete/Failed)とshipments(各実行の直後の、該当する業務の配送数)を書きます。run 8で、総合分析を記録します。

参考

正常な配送の比較群

/root/ckad-job-effects/baseline.jsonをテンプレートから作成し、ステップ1の案内にある名前・ビジネスキー・モード・失敗条件・リトライバジェットで、run 1を実行してください。

正常な実行でも、Jobと元帳の観測を別々に残します。

成功したのに2件ある

/root/ckad-job-effects/unsafe-retry.jsonに、保存後の失敗と1回の再試行を宣言し、run 2を実行してください。

Neverは、同じPodのコンテナの再起動ポリシーです。代替PodのUIDを見てください。

犯人はどの再試行か

observation-2.jsonからJob・失敗Pod・成功PodのUIDと、リクエスト・配送の数を読み取り、retry-analysis.jsonに記録して、run 3を実行してください。

同じownerReferencesと、異なるPod UID、restartCountを一緒に比較します。

同じ失敗、1件の配送

/root/ckad-job-effects/safe-retry.jsonに、safeモードと安定したビジネスキーを宣言し、run 4を実行してください。

リクエストが繰り返されても、同じ配送番号につながるかを確認してください。

別のJobで再実行する

/root/ckad-job-effects/safe-replay.jsonを別のJob名で作成し、CASE_ID=safe-retryは維持したまま、run 5を実行してください。

実行IDは変わる必要があり、ビジネスIDは維持される必要があります。

再試行を止めても残るもの

/root/ckad-job-effects/no-retry.jsonに、backoffLimit=0と保存後の失敗を宣言し、run 6を実行してください。

Failed条件は、保存のロールバックを意味しません。

時間切れでも残るもの

/root/ckad-job-effects/deadline.jsonに、activeDeadlineSeconds=20と保存後の待機を宣言し、run 7を実行してください。

実行の終了と業務の取り消しを区別し、削除前のPodの観測を保存してください。

配送事故の総合レポート

ステップ1・2・4・5・6・7の観測から、final-analysis.jsonの合計・再利用された配送番号・取り消しと保存の判断・ビジネスキーの範囲・6つのoutcomesを作成し、run 8を実行してください。

全体のリクエスト数、業務ごとの配送数、実行の成功条件は、別々の指標です。