破壊実験室 — 五回壊して一回直す
目標
一時VMの中の本物のk3sの上で、5回のカオス実験を行います。正常なベースラインを測り、5つの実験の仮説を壊す前にまとめて書き、Podを殺し、CPUを絞り、メモリを干上がらせたあと、学んだことを設計に反映して、同じ攻撃をもう一度受けます。kubectlの基本的な参照とDeploymentの構造を知っている、中級の学習者向けの65分の実験です。
なぜ重要なのか
障害を注入すること自体は、コマンド1行です。難しいのは、その実験が翌週にも残る何かを作るようにすることです。そのため、このラボの成果物は、故障ではなくノートです。仮説は実験の前に書き、測定は実験ウィンドウの中で行い、結論は2つを突き合わせた結果として書きます。予想が外れることは、失敗ではありません。反証された仮説こそ、その実験が稼げる最も高価な情報であり、それをrefutedと書くのが、正解です。
判定表(結論で使う名前)
同じ数字を人によって違って読まないように、名前を付けるルールを先に決めておきます。
- 可用性: 成功率が0.98以上なら
ok、0.5以上ならdegraded、それ未満ならdown - レイテンシ: 成功したレイテンシのサンプルが5件未満なら
unmeasured、p95がベースラインp95の2倍以上ならslower、そうでなければsame - 判定: 仮説の2つの値が、観測した2つの値とどちらも同じなら
confirmed、1つでも違えばrefuted
測定ツール
python3 /opt/fixtures/chaos_lab.pyは、3つのことだけを行います。障害を代わりに起こしてくれることも、直してくれることもありません。
load <초> <원시.json>(プレースホルダーは順に、秒と生データJSONです): Service経由で2系統の負荷を送ります。可用性は静的パス/に毎秒10回、レイテンシはCPUを使う/cgi-bin/workに毎秒2回。バックグラウンドで起動しておき、そのウィンドウの中で障害を起こしてください。record <국면> <원시.json> <기록.json>(プレースホルダーは順に、フェーズ、生データJSON、記録JSONです): 負荷の結果と、その瞬間のクラスターをまとめて残します。要求したフェーズの構造条件が実際に成り立っていなければ、記録を拒否します。grade: 採点ツールが使います。
安全範囲
一時VMの中のAPI https://127.0.0.1:6443とlabhub-chaosネームスペースだけを使います。本番クラスターや他人の環境では、絶対に実行しないでください。提供されるアプリは、原因を1つだけ残すためにRecreate戦略を使っています。無停止運用の推奨値という意味ではありません。アプリイメージはダイジェストで固定されています。
このラボは65分に設定されているので、基本セッション(60分)より長いです。開始するときに、あらかじめ時間を延長してください。セッションが終わると、VMと記録ファイルは消え、復元できません。記録ファイルは編集可能な実験ノートであり、偽造できない証拠ではありません。
ステップ
/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で残します。- ベースラインのファイルから成功率と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倍以下にします。 - レプリカが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は、ベースラインのままにしてください。 - レプリカを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で残します。レプリカ数以外は、何も変えません。 - 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で残します。 - 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で残します。 - リソースの上限を正常値(cpu
500m・memory96Mi)に戻しながら、今回は終了処理を加えてください。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で残します。 - 5つの記録の数字に、指示文の判定表のとおりに名前を付けて、
/root/chaos-lab/conclusion.jsonに書いてください。実験ごとに、observed_availability、observed_latency、仮説と突き合わせたverdict(confirmedまたはrefuted)、そしてこの結果で何を変えるか(action)が必要です。最後の採点は、前の記録と今のクラスターを一緒に見ます。直した設計が生きている必要があります。
参考
kubectl -n labhub-chaos get pods,svc,endpointslices -o wideで、対象と状態を分けて見てください。kubectl -n labhub-chaos get rsは、過ぎたデプロイの痕跡です。最後の採点がこれを見ます。kubectl -n labhub-chaos describe pod <이름>(プレースホルダーは名前です)の終了理由と終了コードを確認してください。- 負荷はバックグラウンド(
&)で起動し、障害を起こしたあと、waitでウィンドウが閉じるのを待ってから記録します。 - ベースラインを測り直す必要があるなら、前のステップに戻ってもかまいません。ただし、
hypothesis.jsonは直さないでください。 - 上限の値は、
limitsだけでなくrequestsとの関係も見ます。上限がリクエスト量より小さいと、拒否されます。
壊す前に「正常」を数字で残す
/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つは、互いに違う必要があります。