TT Lab
料金プラン はじめる
学ぶ 学習パス コース

壊すのは私 — 仮説を先に書くカオス実験室

破壊実験室 — 五回壊して一回直す

TT Labで続きを見る

目標

一時VMの中の本物のk3sの上で、5回のカオス実験を行います。正常なベースラインを測り、5つの実験の仮説を壊す前にまとめて書き、Podを殺し、CPUを絞り、メモリを干上がらせたあと、学んだことを設計に反映して、同じ攻撃をもう一度受けます。kubectlの基本的な参照とDeploymentの構造を知っている、中級の学習者向けの65分の実験です。

なぜ重要なのか

障害を注入すること自体は、コマンド1行です。難しいのは、その実験が翌週にも残る何かを作るようにすることです。そのため、このラボの成果物は、故障ではなくノートです。仮説は実験の前に書き、測定は実験ウィンドウの中で行い、結論は2つを突き合わせた結果として書きます。予想が外れることは、失敗ではありません。反証された仮説こそ、その実験が稼げる最も高価な情報であり、それをrefutedと書くのが、正解です。

判定表(結論で使う名前)

同じ数字を人によって違って読まないように、名前を付けるルールを先に決めておきます。

測定ツール

python3 /opt/fixtures/chaos_lab.pyは、3つのことだけを行います。障害を代わりに起こしてくれることも、直してくれることもありません。

安全範囲

一時VMの中のAPI https://127.0.0.1:6443とlabhub-chaosネームスペースだけを使います。本番クラスターや他人の環境では、絶対に実行しないでください。提供されるアプリは、原因を1つだけ残すためにRecreate戦略を使っています。無停止運用の推奨値という意味ではありません。アプリイメージはダイジェストで固定されています。

このラボは65分に設定されているので、基本セッション(60分)より長いです。開始するときに、あらかじめ時間を延長してください。セッションが終わると、VMと記録ファイルは消え、復元できません。記録ファイルは編集可能な実験ノートであり、偽造できない証拠ではありません。

ステップ

  1. /opt/fixtures/chaos-lab-app.jsonを適用してロールアウトが終わるのを待ったあと、何も触らない状態で20秒の負荷をかけて、ベースラインを記録してください。python3 /opt/fixtures/chaos_lab.py load 20 /root/chaos-lab/raw-01.jsonで測り、python3 /opt/fixtures/chaos_lab.py record steady /root/chaos-lab/raw-01.json /root/chaos-lab/01-steady.jsonで残します。
  2. ベースラインのファイルから成功率とp95を読み、steady_stateに定常状態を書き、5つの実験(kill-one・kill-ha・cpu-squeeze・mem-squeeze・hardened)の仮説を/root/chaos-lab/hypothesis.jsonに書いてください。実験ごとに、availability、latency、なぜそう予想するか(why)、いつ止めるか(abort_if)の4つが必要です。availability_ratio_minは0.95以上でベースラインの測定値以下、p95_ms_maxはベースラインp95以上でその3倍以下にします。
  3. レプリカが1の状態で負荷をバックグラウンドで起動し、測定ウィンドウの中で、その1つだけのPodを削除してください。ウィンドウが閉じたら、python3 /opt/fixtures/chaos_lab.py record kill-one /root/chaos-lab/raw-03.json /root/chaos-lab/03-kill-one.jsonで残します。リソースの上限とpreStopは、ベースラインのままにしてください。
  4. レプリカを3に増やして、3つすべてがReadyになったあと、同じ方法で負荷ウィンドウの中でPodをちょうど1つだけ削除してください。python3 /opt/fixtures/chaos_lab.py record kill-ha /root/chaos-lab/raw-04.json /root/chaos-lab/04-kill-ha.jsonで残します。レプリカ数以外は、何も変えません。
  5. shopコンテナのcpuの上限だけを100mに下げてください(memoryは96Mi、レプリカは3のまま)。ロールアウトが終わり、3つすべてがReadyになったあとで20秒の負荷をかけ、python3 /opt/fixtures/chaos_lab.py record cpu-squeeze /root/chaos-lab/raw-05.json /root/chaos-lab/05-cpu-squeeze.jsonで残します。
  6. cpuの上限を500mに戻しながら、memoryの上限を40Miに下げてください。アプリは起動時に48MiBを一度に確保するので、コンテナがOOMで死にます。再起動が捉えられたら、20秒の負荷をかけ、python3 /opt/fixtures/chaos_lab.py record mem-squeeze /root/chaos-lab/raw-06.json /root/chaos-lab/06-mem-squeeze.jsonで残します。
  7. リソースの上限を正常値(cpu 500m・memory 96Mi)に戻しながら、今回は終了処理を加えてください。preStopフックで最低3秒を稼ぎ、terminationGracePeriodSecondsを10以上に上げます。ロールアウトが終わったら、ステップ3・ステップ4と同じ攻撃をもう一度行い、python3 /opt/fixtures/chaos_lab.py record hardened /root/chaos-lab/raw-07.json /root/chaos-lab/07-hardened.jsonで残します。
  8. 5つの記録の数字に、指示文の判定表のとおりに名前を付けて、/root/chaos-lab/conclusion.jsonに書いてください。実験ごとに、observed_availability、observed_latency、仮説と突き合わせたverdict(confirmedまたはrefuted)、そしてこの結果で何を変えるか(action)が必要です。最後の採点は、前の記録と今のクラスターを一緒に見ます。直した設計が生きている必要があります。

参考

壊す前に「正常」を数字で残す

/opt/fixtures/chaos-lab-app.jsonを適用してロールアウトが終わるのを待ったあと、何も触らない状態で20秒の負荷をかけて、ベースラインを記録してください。python3 /opt/fixtures/chaos_lab.py load 20 /root/chaos-lab/raw-01.jsonで測り、python3 /opt/fixtures/chaos_lab.py record steady /root/chaos-lab/raw-01.json /root/chaos-lab/01-steady.jsonで残します。

Running・Ready・実際のリクエスト成功は、それぞれ別の事実です。Readyなendpointが1つになってから測ってください。測定ウィンドウの中で新しいPodが生まれたら、ベースラインとして認められません。

5つの仮説を先に書く

ベースラインのファイルから成功率とp95を読み、steady_stateに定常状態を書き、5つの実験(kill-one・kill-ha・cpu-squeeze・mem-squeeze・hardened)の仮説を/root/chaos-lab/hypothesis.jsonに書いてください。実験ごとに、availability、latency、なぜそう予想するか(why)、いつ止めるか(abort_if)の4つが必要です。availability_ratio_minは0.95以上でベースラインの測定値以下、p95_ms_maxはベースラインp95以上でその3倍以下にします。

予想が合っている必要はありません。外れた予想を、あとでrefutedと書くことが、このラボの正解です。ラベルの意味は、指示文の判定表にあります。このファイルは、ここから先は直さないでください。最後の採点が、最初の実験記録より先に書かれたかどうかを見ます。

レプリカ1つのサービスを殺してみる

レプリカが1の状態で負荷をバックグラウンドで起動し、測定ウィンドウの中で、その1つだけのPodを削除してください。ウィンドウが閉じたら、python3 /opt/fixtures/chaos_lab.py record kill-one /root/chaos-lab/raw-03.json /root/chaos-lab/03-kill-one.jsonで残します。リソースの上限とpreStopは、ベースラインのままにしてください。

負荷を先に起動して、数秒後に殺して初めて、影響がウィンドウの中に捉えられます。殺したあとで測り始めると、すでに代わりのPodが準備できたあとの数字を測ることになります。ウィンドウが閉じるときにPodが再びReadyである必要があり、そうでなければ記録されません。

レプリカを3つに増やして、同じ攻撃を繰り返す

レプリカを3に増やして、3つすべてがReadyになったあと、同じ方法で負荷ウィンドウの中でPodをちょうど1つだけ削除してください。python3 /opt/fixtures/chaos_lab.py record kill-ha /root/chaos-lab/raw-04.json /root/chaos-lab/04-kill-ha.jsonで残します。レプリカ数以外は、何も変えません。

変える変数は、一度に1つでなければ、結果を説明できません。3つすべてがReadyかを確認してから測ってください。成功率がベースラインほど回復しなくても、そのまま記録すればよいです。それがこの実験の結果です。

CPUを絞る: 死なずに遅くなる障害

shopコンテナのcpuの上限だけを100mに下げてください(memoryは96Mi、レプリカは3のまま)。ロールアウトが終わり、3つすべてがReadyになったあとで20秒の負荷をかけ、python3 /opt/fixtures/chaos_lab.py record cpu-squeeze /root/chaos-lab/raw-05.json /root/chaos-lab/05-cpu-squeeze.jsonで残します。

リソースの上限はPodテンプレートにあるので、変えれば新しいPodが起動します。ロールアウトの最中に測ると、デプロイの影響と上限の影響が混ざります。上限がリクエスト量より小さくできないことも、覚えておいてください。

メモリを干上がらせる: 今度は死ぬ

cpuの上限を500mに戻しながら、memoryの上限を40Miに下げてください。アプリは起動時に48MiBを一度に確保するので、コンテナがOOMで死にます。再起動が捉えられたら、20秒の負荷をかけ、python3 /opt/fixtures/chaos_lab.py record mem-squeeze /root/chaos-lab/raw-06.json /root/chaos-lab/06-mem-squeeze.jsonで残します。

今回のロールアウトは終わりません。rollout statusを待たず、再起動回数や終了理由を見てください。終了コードと理由がCPUのときとどう違うかが、この実験の核心です。

学んだことを設計に反映して同じ攻撃をもう一度受ける

リソースの上限を正常値(cpu 500m・memory 96Mi)に戻しながら、今回は終了処理を加えてください。preStopフックで最低3秒を稼ぎ、terminationGracePeriodSecondsを10以上に上げます。ロールアウトが終わったら、ステップ3・ステップ4と同じ攻撃をもう一度行い、python3 /opt/fixtures/chaos_lab.py record hardened /root/chaos-lab/raw-07.json /root/chaos-lab/07-hardened.jsonで残します。

preStopはTERMシグナルより先に実行され、フックが終わって初めてシグナルが出ます。その間に、エンドポイントの削除が広がります。猶予期間のカウントダウンはフックの前に始まるので、フックより余裕がある必要があります。プローブを消したり、レプリカをさらに増やしたりして数字を良くすることは、この実験の契約を回避する行為です。

仮説と測定を突き合わせて実験ノートを閉じる

5つの記録の数字に、指示文の判定表のとおりに名前を付けて、/root/chaos-lab/conclusion.jsonに書いてください。実験ごとに、observed_availability、observed_latency、仮説と突き合わせたverdict(confirmedまたはrefuted)、そしてこの結果で何を変えるか(action)が必要です。最後の採点は、前の記録と今のクラスターを一緒に見ます。直した設計が生きている必要があります。

反証された仮説は、失敗ではありません。予想と違えば、refutedと書くのが正しい答えです。数字だけをもっともらしく書いて、名前を適当に付けると、採点ツールが同じルールで再計算して、はじきます。actionの5つは、互いに違う必要があります。