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

K8sを壊してみよう — 犯人は自分のYAML

犯人は自分のYAML — 三つの障害ノート

TT Labで続きを見る

目標

実際のk3sの中で、セレクターの不一致・誤ったreadinessのパス・プロセスの終了を起こし、観測の根拠を保存したうえで、最小限の変更で復旧します。kubectl get/applyとYAMLの基本構造を知っている中級の学習者のための、50分の実験です。

なぜ重要なのか

Runningと、Readyと、ユーザーリクエストの成功は、それぞれ別の事実です。正常なPodがあっても、Serviceのセレクターやポートが間違っていれば、顧客は失敗を経験します。逆に、プロセスは生きていても、準備の条件を満たせず、転送先から除外されることもあります。1回に1つのフィールドだけを変えて前後を比較すれば、再起動を繰り返す代わりに、原因を説明できます。3つの事件を区別したうえで、確認した正常なリビジョンに戻ることが、このラボの範囲です。

安全の範囲

一時VM内のAPI(https://127.0.0.1:6443과)とlabhub-mysteryネームスペースだけを使います。本番クラスターや、ほかの受講生の環境では実行しないでください。提供されるアプリは、単一のレプリカとRecreateを使って、以前の正常なPodが障害を覆い隠さないようにしています。無停止運用の推奨設定という意味ではありません。アプリはイメージのdigestで固定されています。セッションが終了すると、VMと観測ファイルは消えます。必要なら、期限が切れる前に時間を延長し、観測を別に保管してください。観測ファイルは、編集可能な実験ノートであり、偽造できない証拠ではありません。

ステップ

  1. 提供された/opt/fixtures/k8s-mystery-app.jsonを、kubectl applyで適用してください。labhub-mysteryネームスペースのparcel DeploymentとServiceを取得し、PodとServiceの両方で、ポート8080の/パスが本文labhub-mystery-v1を返す正常な状態を、baselineとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe baseline /root/k8s-mystery/01-baseline.json。
  2. parcel Serviceのselectorだけを、app=missingに変更してください。Podのラベルと、Deploymentは維持します。PodはReadyで直接応答するが、Serviceの選択されたendpointが0で、リクエストは失敗する状態を、selector-brokenとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe selector-broken /root/k8s-mystery/02-selector.json。
  3. Serviceのselectorを、app=parcelに復旧してください。選択されたReadyなendpointが1個で、PodとServiceの両方で正常なHTTPであることを確認し、selector-restoredとして記録してください。Deploymentを削除して作り直さないでください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe selector-restored /root/k8s-mystery/03-selector-fixed.json。
  4. Deploymentのparcelコンテナの、readinessProbe.httpGet.pathだけを、/missingに変更してください。新しいPodはRunningで、/に直接応答するが、Ready=false、再起動0、現在のPodの404イベント、Readyなendpointが0である状態を、readiness-brokenとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe readiness-broken /root/k8s-mystery/04-readiness.json。
  5. 同じreadinessのパスを、/に復旧してください。プローブを削除したり、常に成功するexecに置き換えたりしないでください。PodとServiceの正常なHTTPと、Readyなendpoint1個を、readiness-restoredとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe readiness-restored /root/k8s-mystery/05-readiness-fixed.json。
  6. 正常なDeploymentのリビジョンを取得して、Deploymentのannotation labhub.io/known-good-revisionに保存してください。そのあと、parcelのcommandを["sh","-ec","echo intentional-exit-17 >&2; exit 17"]に置き換えます。CrashLoopBackOff・直前の終了コード17・再起動1以上・直前のログのintentional-exit-17を確認し、crashとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe crash /root/k8s-mystery/06-crash.json。
  7. rollout historyと、保存したlabhub.io/known-good-revisionを確認し、そのリビジョンにrollout undoしてください。既存のDeploymentを維持したまま、正常なコマンド・プローブ/・Serviceのselector app=parcel・targetPort 8080、および両方のHTTPが復旧した状態を、rollbackとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe rollback /root/k8s-mystery/07-rollback.json。
  8. 前の7つの観測ファイルを維持したまま、/root/k8s-mystery/report.jsonに、selector・readiness・crashの事件ごとに、cause・prevention・evidenceを結び付けてください。原因/予防のコードは下の参考にあり、evidenceは障害発生時のファイル名です。現在のServiceも正常である必要があります。最後に、「全体採点」を押して、過去の観測と現在の復旧を一緒に確認してください。

参考

観測ツールのobserveは、取得とファイルの保存だけを行います。要求された障害を代わりに注入したり、復旧したりしません。45秒以内に条件が見られなければ、値とイベントを確認して、もう一度観測してください。前の7つのステップの採点は、保存した記録を読み取るので、復旧後も過去のステップが取り消されません。最後の採点は、同じDeploymentの記録の結び付きと、現在のPodとServiceのHTTPを、2回確認します。

宅配便が正常に届く基準線

提供された/opt/fixtures/k8s-mystery-app.jsonを、kubectl applyで適用してください。labhub-mysteryネームスペースのparcel DeploymentとServiceを取得し、PodとServiceの両方で、ポート8080の/パスが本文labhub-mystery-v1を返す正常な状態を、baselineとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe baseline /root/k8s-mystery/01-baseline.json。

Runningの表示だけを見ずに、Ready、EndpointSlice、実際のHTTPを区別してください。観測ツールは、望ましい状態を作りません。

配送車は生きているのに、配送先がない

parcel Serviceのselectorだけを、app=missingに変更してください。Podのラベルと、Deploymentは維持します。PodはReadyで直接応答するが、Serviceの選択されたendpointが0で、リクエストは失敗する状態を、selector-brokenとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe selector-broken /root/k8s-mystery/02-selector.json。

Serviceのselectorと、Podのlabelsを比較してください。選択条件を変えても、既存のPodのラベルが一緒に変わるわけではありません。

配送リストだけを正す

Serviceのselectorを、app=parcelに復旧してください。選択されたReadyなendpointが1個で、PodとServiceの両方で正常なHTTPであることを確認し、selector-restoredとして記録してください。Deploymentを削除して作り直さないでください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe selector-restored /root/k8s-mystery/03-selector-fixed.json。

アプリを再起動する根拠はありますか。誤って変更したオブジェクトのフィールドだけを元に戻して、実際のリクエストで効果を確認してください。

存在しない健康診断室のせいで、配達が止まる

Deploymentのparcelコンテナの、readinessProbe.httpGet.pathだけを、/missingに変更してください。新しいPodはRunningで、/に直接応答するが、Ready=false、再起動0、現在のPodの404イベント、Readyなendpointが0である状態を、readiness-brokenとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe readiness-broken /root/k8s-mystery/04-readiness.json。

livenessではなく、readinessのパスを変えます。イベントの対象UIDが現在のPodと同じかどうかも見てください。

健康診断の契約を、実際のパスに合わせる

同じreadinessのパスを、/に復旧してください。プローブを削除したり、常に成功するexecに置き換えたりしないでください。PodとServiceの正常なHTTPと、Readyなendpoint1個を、readiness-restoredとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe readiness-restored /root/k8s-mystery/05-readiness-fixed.json。

実際のリクエストとプローブが、同じ健全性を表現するようにします。このケースはパスのタイプミスであり、実際の依存関係の障害を無視するケースではありません。

宅配ドライバーがメモ17を残して退勤する

正常なDeploymentのリビジョンを取得して、Deploymentのannotation labhub.io/known-good-revisionに保存してください。そのあと、parcelのcommandを["sh","-ec","echo intentional-exit-17 >&2; exit 17"]に置き換えます。CrashLoopBackOff・直前の終了コード17・再起動1以上・直前のログのintentional-exit-17を確認し、crashとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe crash /root/k8s-mystery/06-crash.json。

リビジョンを1のような数字で暗記しないでください。正常だと確認した直後に保存し、logs --previousで、直前の終了の根拠を読みます。

記憶ではなく、確認したリビジョンに戻る

rollout historyと、保存したlabhub.io/known-good-revisionを確認し、そのリビジョンにrollout undoしてください。既存のDeploymentを維持したまま、正常なコマンド・プローブ/・Serviceのselector app=parcel・targetPort 8080、および両方のHTTPが復旧した状態を、rollbackとして記録してください。記録コマンド: python3 /opt/fixtures/k8s_mystery.py observe rollback /root/k8s-mystery/07-rollback.json。

Deploymentのロールバックは、Serviceを修正しません。履歴の番号と実際のリクエストを、一緒に確認してください。

緑のランプではなく根拠で、事件を終結させる

前の7つの観測ファイルを維持したまま、/root/k8s-mystery/report.jsonに、selector・readiness・crashの事件ごとに、cause・prevention・evidenceを結び付けてください。原因/予防のコードは下の参考にあり、evidenceは障害発生時のファイル名です。現在のServiceも正常である必要があります。最後に、「全体採点」を押して、過去の観測と現在の復旧を一緒に確認してください。

セレクター・プローブのパス・プロセスのコマンドを、互いに区別してください。現在のサービスが失敗していると、JSONだけを書き換えても通過しません。