Chaos Engineering Runbook

Parky スタックが想定外障害に耐えられるか、また検出・復旧フローが 実戦で機能するかを四半期ごとの game day で検証する。 本ドキュメントはシナリオ集、実施手順、記録テンプレートを 1 ファイルに集約した SSoT。

1. 目的

  • 耐性検証 — Supabase / Cloudflare / 外部 API の単一障害点で、 Parky のユーザー体験がどこまで劣化するか (graceful degrade or hard fail) を実測。
  • 検出フローの実地練習 — Sentry / synthetic-healthcheck / Discord P0/P1 alert / SLO burn auto-rollback が想定通り動くかを、実際に 壊して確認する (= ペーパーテストでは見えない gap を炙り出す)。
  • 復旧時間 (MTTR) の計測 — 障害誘発から通知着弾、 rollback 完了までの実測時間を蓄積。SLO Phase 2 移行の判断材料に使う。
  • 1 人開発期の "監視疲れ防止" — false-positive を避けつつ 本物の障害を見逃さない閾値を、game day での誤発火 / 見逃しから calibrate する。

2. 想定シナリオ

Parky の依存マップ (Cloudflare Workers BFF → Supabase / R2 / 外部 API) を踏まえ、 影響面が異なる 6 シナリオを最小セットとする。各 game day ではこの中から 3〜4 シナリオを選択して実施する (全部やると 1 日では終わらない)。

# シナリオ 影響面 誘発手段 推奨環境
S1 Supabase DB 一時停止 authenticated 全 path Supabase Dashboard で project pause dev
S2 Hyperdrive 接続切断 BFF DB 経由 path wrangler secret rotate (一時的に invalid token) dev
S3 R2 障害 画像 / asset 配信 R2 binding を invalid bucket に向ける dev
S4 CF Workers リージョン障害 (global 自動 failover 確認) 誘発不可、CF status を観測のみ prod (受動)
S5 外部 API 障害 (Stripe / Resend / Mapbox) 決済 / 通知 / 地図 MSW で 5xx / timeout を mock dev
S6 Supabase Auth 障害 session 維持 / 新規 login Auth project 個別 pause dev

3. 各シナリオの実施手順

S1. Supabase DB 一時停止 P0 想定

事前準備

  • 実施環境: dev または stgprod では絶対に実施しない
  • on-call (= 全員) に Discord #p2-deploys で 30 分前に告知。
  • metric snapshot を取る:
    • Sentry の error rate / p99 を直近 1 時間 screenshot。
    • synthetic-healthcheck の最新成功 run を確認。
    • auto-rollback-dev の最新 burn=healthy を確認。
  • scripts/deploy/rollback.sh api dev --dry-run で rollback ターゲットを事前確認。

障害誘発手順

  1. Supabase Dashboard → parkydev project → Settings → General → Pause project
  2. 誘発時刻を YYYY-MM-DD HH:MM JST で記録 (秒単位)。
  3. 並行して cd parky/api && npx wrangler tail --config wrangler.public.toml --env dev でログ流し見 (4 split worker のうち public がメイン HTTP route 担当)。

期待される挙動

  • /healthz/ready が 503 + { ok: false, checks: { db: 'fail' } } を返す。
  • synthetic-healthcheck workflow が連続 2 回 fail → Discord #p1-ops に P1 通知。
  • Sentry に HyperdriveConnectionError 系 event が発火 (DSN 設定済の場合)。
  • auto-rollback-dev が次サイクルで burn=fast を返す可能性あり (synthetic fallback mode)。
    この場合 rollback されるが rollback しても Supabase 自体が止まっているので解消しない。 これが想定通り (= rollback は app バグの是正であり、infra 障害には効かない) の確認。

復旧手順

  1. Supabase Dashboard → Resume project
  2. /healthz/ready が 200 OK に戻ることを確認 (5〜10 秒)。
  3. synthetic-healthcheck の次 run で recovery 通知が #p1-ops に出るか確認。
  4. auto-rollback-dev で誤発火していた場合は scripts/deploy/rollback.sh api dev --version <original> で前進 deploy へ復帰。

S2. Hyperdrive 接続切断 P1 想定

事前準備

  • 実施環境: dev
  • 現在の Hyperdrive binding 値を wrangler secret list --env dev で控える (復旧用)。

障害誘発手順

  1. echo "postgres://invalid:invalid@127.0.0.1:5432/none" | wrangler secret put HYPERDRIVE_URL --env dev で invalid な接続文字列を投入。
  2. または Cloudflare Dashboard → Hyperdrive → 該当 config を 一時 disable

期待される挙動

  • BFF の DB クエリが timeout / connection refused で落ちる。
  • postgres.js v3 の自動再接続が走るがリトライ上限で諦める (Sentry に event)。
  • /healthz/ready が 503 を返す。

復旧手順

  1. 正しい HYPERDRIVE_URL を再投入 (op-cache から取得 → wrangler secret put)。
  2. Cloudflare Hyperdrive config を re-enable。
  3. 5 分以内に healthz が回復することを確認。

S3. R2 障害 P2 想定

事前準備

  • 実施環境: dev
  • web/home / portal で画像が出ているページを 1 つ事前に開いておく (比較用)。

障害誘発手順

  1. wrangler.toml の R2 binding を bucket_name = "parky-nonexistent-test" に書き換えて dev へ deploy。
  2. または既存 R2 bucket の API token を一時的に revoke。

期待される挙動

  • 画像 endpoint が 404 / 502 を返す。
  • BFF 本体は動き続ける (DB 系 endpoint は影響なし)。
  • Astro home / portal admin の画像が壊れた alt text 表示になる (graceful degrade の検証)。
  • Sentry に R2NotFoundError 系 event。/healthz/ready は 200 を返し続けるはず (= R2 は readiness に含めない設計確認)。

復旧手順

  1. wrangler.toml を git revert して再 deploy。
  2. 画像が再度表示されることを確認。

S4. Cloudflare Workers リージョン障害 受動観測

事前準備

  • 誘発不可 (= CF 側でしか起こせない)。
  • Cloudflare Status で Tokyo / Asia リージョンに incident が出ているタイミングを観察用に記録しておく。

観測ポイント

  • CF Workers は global 自動 failover するので、特定リージョン障害で Parky 利用者には 遅延増加 として現れる (完全 outage にはならない)。
  • Sentry で p99 latency が普段の 2 倍以上に振れるか観測。
  • SLO burn check (auto-rollback-dev) が latency burn を返すかを確認。 ここで誤発火すると prod 自動 rollback の信頼性に直結するので 本シナリオは特に重要な calibration

復旧手順

  • 受動。CF 側の incident 解消を待つ。Parky 側は何もしない。

S5. 外部 API 障害 (Stripe / Resend / Mapbox) P1 想定

事前準備

  • 実施環境: dev + MSW (Mock Service Worker) で fetch を intercept。
  • 対象を 1 つに絞る (例: api.stripe.com)。

障害誘発手順

  1. BFF の fetch を MSW で https://api.stripe.com/* → 503 / 30s timeout に書き換え。
  2. または Cloudflare Worker の routes で該当ホストへの outbound を一時 reject。
  3. workflow_dispatch で chaos-fault-inject.yml を起動して Discord に開始通知。

期待される挙動

  • 該当 endpoint (/v1/billing/...) が 502 / 504 を返す。
  • BFF が circuit breaker / retry-with-backoff をしているか確認 (現状は素通し想定)。
  • Sentry に StripeAPIError event が出る。
  • SLO burn (error rate × 3) が発火するか観測。

復旧手順

  1. MSW handler を解除して fetch を実 API に戻す。
  2. chaos-fault-inject.yml の duration 終了通知を待つ。

S6. Supabase Auth 障害 P0 想定

事前準備

  • 実施環境: dev
  • 新規 login と既存 session 維持の 2 種を分けて検証する。
  • テスト用ブラウザを 2 つ用意 (既存 session / 未 login)。

障害誘発手順

  1. Supabase Dashboard → Auth → Policies で一時的に signin endpoint を block (= 大量 error 状態)。
  2. または Auth project だけ pause (DB と独立に止められる場合のみ)。

期待される挙動

  • 新規 login: 5xx で失敗。
  • 既存 session: BFF の session refresh が走るまでは動作。refresh で 5xx → silent logout される。
  • Sentry に AuthApiError 多発。

復旧手順

  1. Auth policy を revert / Auth resume。
  2. 新規 login が回復することを確認。既存 session の自動再認証も観察。

4. 記録テンプレート

各 game day で以下の表を埋め、.work/parky/chaos/ 配下に YYYY-MM-DD_chaos_game_day.md として保存する。

項目
game day 日付YYYY-MM-DD HH:MM JST
シナリオ IDS1 / S2 / S3 / S4 / S5 / S6
実施環境dev / stg
実施者(name)
誘発時刻HH:MM:SS
1 次検出時刻 (Discord 着弾 or 手動気づき)HH:MM:SS
検出までの時間 (TTD)N 分 N 秒
復旧開始時刻HH:MM:SS
復旧完了時刻 (healthz 200 確認)HH:MM:SS
復旧までの時間 (TTR / MTTR)N 分 N 秒
期待挙動との差分(自由記述)
誤発火の有無あり / なし — 内容
auto-rollback 動作(burn 種別 / rollback 実行可否 / 想定通りか)
改善 action1) ... 2) ... (Issue 化必須)
関連 PR / Issue#xxxx

5. 次回 game day 候補日

  • 初回 (Phase 1 リリース直後 + 1 ヶ月) — Phase 1 ローンチ (2026-06 想定) の 1 ヶ月後。最初は S1 (DB 停止) と S5 (外部 API) の 2 シナリオに絞る。
  • quarterly (四半期ごと) — 上記初回以降は 3 ヶ月に 1 回。 シナリオはローテーションで全 6 種を 1 年で回す。
  • 非定期 — 大きな architecture 変更後 (例: postgres.js v4 移行, Cloudflare Pro upgrade, Phase 2 SLO 移行) は次回定期を待たず追加実施。

6. 整備すべき仕組み

game day を継続的に行うために以下を整備する。優先度順:

  1. chaos-fault-inject workflow (skeleton).github/workflows/chaos-fault-inject.yml として配置済。 現状は Discord に開始 / 終了通知を送るだけ。実際の障害誘発は manual。 将来 MSW / wrangler tail 連携で自動化する。
  2. MSW (Mock Service Worker) — 外部 API mock の標準化。 BFF に MSW を組み込み、env PARKY_CHAOS_MOCKS で有効化する仕組み。 (未実装 / S5 の game day 前に着手)
  3. circuit breaker / retry-with-backoff — Stripe / Resend / Mapbox の outbound に共通 wrapper を入れて、 外部 API 障害時の cascading failure を防ぐ。 (未実装 / S5 の結果次第で着手判定)
  4. game day 記録の自動収集 — Discord channel / GitHub Actions の log から TTD / TTR を機械的に集計する script。 (Phase 2 入り後に着手)

7. 関連

  • インシデント対応 — game day で発火した alert は 実 incident と同じトリアージ手順で扱う。
  • Postmortem — 想定外の差分が出たら blameless postmortem を作成 (game day でも例外なく)。
  • SLO / Error budget — burn rate 閾値の calibration source。
  • Rollback runbook — auto-rollback と manual rollback の境界線。
  • DR Drill (markdown) — 災害復旧訓練 (本書とは別レベル)。