本番障害の対応手順: 止血→切り分け→復旧→ポストモーテムを崩さない
本番が落ちた瞬間、焦って直すと被害が広がる。検知→止血→原因切り分け→復旧→振り返りの順番を固定し、Claude Codeをログ調査と仮説出しに使う実務手順を、僕のやらかし込みで紹介。
金曜の夜、決済が通らないという連絡が来ました。
僕は反射的にターミナルを開いて、本番のコードを直し始めました。「たぶんここだ」と当たりをつけて、その場でサーバーに入って書き換えた。直った気がした。でも30分後、別の画面が真っ白になりました。さっきの“修正”が、関係ない機能まで巻き込んでいたんです。
落ち着いて考えれば分かります。障害対応でいちばん被害を広げるのは、原因調査を焦って本番をいじることです。 火事の最中に、火元を探しながら家具を動かすようなものなんですね。
この記事は、僕がClaudeCodeLabの運用でやっている障害演習と、実際にやらかした失敗をもとにした合成ケースです。金額や時刻は説明用ですが、事故の型はかなり現実的です。検知から振り返りまでの順番を固定し、その各段階でClaude Codeをどう“使い、どう使わないか”をまとめます。
この記事の要点
- 障害対応は**「止めてから調べる」**が原則。検知→止血(封じ込め)→原因切り分け→復旧→ポストモーテムの順番を崩さない。
- Claude Codeはログ要約・仮説出し・連絡文の下書きに使う。本番コマンドを勝手に実行させない。
- 焦って本番で直接いじる、
git checkout .で戻せると思い込む、リトライを無制限に足す——この3つが事故を倍にする。 permissions.denyとhookで、削除・force push・本番DBを物理的に止めておく。- 振り返りは犯人探しでなく仕組みの修正。小さな表でいいから期限つき対策を残す。
まず順番を固定する:5段階の対応フロー
障害が起きると、人は一番派手な「原因を直す」に飛びつきます。でも本当に最初にやるべきは、被害を止めることです。順番を体に入れておくと、夜中に呼び出されても判断がぶれません。
| 段階 | やること | Claude Codeに頼むこと | Claude Codeに頼まないこと |
|---|---|---|---|
| 1. 検知 | 何が、いつから壊れたか確認 | アラート・ログ・差分を要約 | アラートの真偽の最終判断 |
| 2. 止血(封じ込め) | 被害が広がるのを止める | 危険ジョブ停止案、機能フラグOFF案を出す | 本番への停止操作の実行 |
| 3. 原因切り分け | 直接の原因を絞る | 直近デプロイ・DB変更・権限変更を並べる | 「これが原因」と断定すること |
| 4. 復旧 | 安全な状態に戻す | 戻す対象・戻せないデータ・確認コマンドを明記 | 本番ロールバックの実行 |
| 5. ポストモーテム | 再発防止に変える | RCA・検知漏れ・期限つき対策を表にする | 個人の責任認定 |
ポイントは段階2と3の順番です。普通の感覚だと「原因が分かってから止める」ですが、本番では逆。先に止血してから、ゆっくり原因を切り分ける。 API課金、秘密情報、個人情報、DB書き込みが絡むときは、数分の遅れで被害範囲が変わります。止めても困らないものは、迷ったら止めてしまっていい。
Claude Code自体の設定は公式のClaude Code settingsとhooks guideに従います。この記事は、その公式仕様の上で対応フローを組む話です。
検知:赤いログを全部読まない
第一報はだいたい曖昧です。「サイトが重い」「決済が変」「管理画面が見えない」。ここで全ログを上から読み始めると、それだけで10分溶けます。
僕がやるのは、まず「いつから」「どこが」の2点だけ確定させること。デプロイ履歴、監視グラフ、エラー件数の急増ポイントを照らし合わせます。直近に何かをリリースしていれば、それが最有力です。
ログをClaude Codeに渡すときは、生ログを丸ごと貼らずに、時間帯で切ってから渡すと精度が上がります。
# 直近30分のエラーだけ抜き出して件数の多い順に
journalctl -u myapp --since "30 min ago" -p err --no-pager \
| sed -E 's/[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9:]+//' \
| sort | uniq -c | sort -rn | head -20
この「件数の多い順」が効きます。エラーは1種類が大量に出ていることが多く、その筆頭が震源地のことが多いからです。スタックトレースの読み方そのものはエラーメッセージの読み方の記事に詳しく書いたので、原因の型(undefined・型・非同期・import)を素早く分けたいときはそちらを併読してください。
検知の精度は、平時の仕込みで決まります。アラートやログが整っていないと、そもそも「いつから壊れたか」が分かりません。監視と構造化ログの設計はログ監視・可観測性の設計記事にまとめています。障害の日に慌てて入れるものではなく、平時に入れておくものです。
止血:本番に手を入れず、流れを止める
ここが今日いちばん伝えたいところです。原因が分かっていなくても、被害は止められます。
止血の選択肢は、だいたいこの順で軽いものから試します。
- 機能フラグをOFFにする(壊れた機能だけ切り離す。いちばん影響が小さい)
- 該当ジョブ・バッチを停止する(暴走しているプロセスを止める)
- メンテナンス表示に切り替える(全体に影響が出ているとき)
- DBを一時的に読み取り専用へ寄せる(データ破壊を止める最終手段)
このとき、Claude Codeには「止める操作の案」を出させます。実行は人間がやる。なぜなら、止血操作そのものが新しい事故になり得るからです。「メンテナンスにして」と頼んだAIが、設定ファイルの別の行まで触る——これは僕が実際にヒヤッとした場面です。
止血の判断で迷ったら、こう自問します。「これを今止めて、最悪何が困る?」止めて困るのが「一部の便利機能が使えない」程度なら、迷わず止める。データが消え続ける、課金が増え続けるほうが、はるかに高くつきます。
API無限リトライのような“静かな出血”には、コマンドの試行回数と簡易コスト上限で強制停止するラッパーが効きます。incident-budget-runner.mjs として保存し、node incident-budget-runner.mjs node batch.js のように既存の処理をくるんで使えます。
#!/usr/bin/env node
import { spawn } from "node:child_process";
const command = process.argv.slice(2);
const maxAttempts = Number(process.env.MAX_ATTEMPTS || 3);
const maxCostCents = Number(process.env.MAX_COST_CENTS || 200);
const costPerAttempt = Number(process.env.COST_PER_ATTEMPT_CENTS || 0);
if (command.length === 0) {
console.error("使い方: node incident-budget-runner.mjs <command> [...args]");
process.exit(2);
}
let estimatedCost = 0;
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
const child = spawn(command[0], command.slice(1), {
stdio: "inherit",
shell: process.platform === "win32",
});
const exitCode = await new Promise((resolve) => {
child.on("exit", (code) => resolve(code ?? 1));
});
estimatedCost += costPerAttempt;
if (exitCode === 0) process.exit(0);
// 試行のたびに概算コストを積み、上限を超えたら止める(暴走の出血点)
if (estimatedCost >= maxCostCents) {
console.error(`停止: 概算コストが ${estimatedCost} セントに達しました`);
process.exit(1);
}
// 指数バックオフ。失敗しても“呼び続けない”のが肝
const delayMs = Math.min(1000 * 2 ** (attempt - 1), 10_000);
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
console.error(`${maxAttempts} 回試して失敗しました`);
process.exit(1);
「リトライを足したら無限ループになっていた」は、コスト系の事故で本当に多いパターンです。回数と予算の天井を先に決めておくと、夜中に課金が膨らみ続ける事故が物理的に起きません。トークン課金の仕組みと予算の止め方はAPIコストの記事にまとめています。
原因切り分け:Claude Codeに仮説を“並べさせる”
止血が済んで、ようやく落ち着いて原因を探せます。ここでのコツは、Claude Codeに「直して」と頼まないこと。**「原因の候補を、可能性の高い順に並べて」**と頼みます。
断定させると、AIは最初の仮説に飛びついて、見当違いの修正を自信満々で出してきます。実際、AIは失敗ログの“最後の行”だけ見て直そうとする癖がある。だから、候補を複数出させて、人間が消し込んでいく形にします。
切り分けで最初に見るのは「直近に変わったもの」です。デプロイ、依存更新、DBマイグレーション、権限変更、環境変数。この5つを時系列で並べるだけで、容疑者がぐっと絞れます。
# 直近のコミットとデプロイ前後の差分を時系列で
git log --oneline --since "6 hours ago"
git diff HEAD~1 HEAD --stat
# DBのスキーマが変わっていないか(破壊する前に schema-only でバックアップ)
pg_dump "$DATABASE_URL" --schema-only > schema_before_repair.sql
psql "$DATABASE_URL" -c "\d users"
この出力をClaude Codeに渡して、「この変更のどれが、今の症状(決済が通らない)を説明できるか、可能性の高い順に理由つきで」と聞く。すると、人間が見落としていた“地味な依存更新”が浮かぶことがあります。npm update が意図せずメジャーバージョンを上げていた、というのは典型例です。
切り分けの段階で、まだ本番は触りません。仮説が立ったら、検証はステージングか手元でやる。「本番で試せば早い」は、金曜の夜の僕が通った道で、二次災害の入口です。
復旧:戻せるコードと、戻らないデータを分ける
原因の見当がついたら復旧です。ここで絶対に分けるべきなのが、「コード」と「データ」。
コードは前回の安全なデプロイに戻せます。これは比較的安全。問題はデータです。削除・更新してしまったデータは、コードを戻しても返ってきません。バックアップ、WAL(書き込みログ)、監査ログ、外部システムからの再同期——どれかで取り戻すしかない。
だから復旧前に、Claude Codeにこの4点を必ず書き出させます。
- 戻す対象(どのデプロイ/どのコミットへ)
- 戻らないもの(この時間帯に消えた・上書きされたデータ)
- 復元元(バックアップの場所、取得時刻)
- 確認コマンド(戻したあと、何を見れば復旧と判断できるか)
SQLを伴う復旧はとくに慎重に。DELETE FROM users; のような WHERE なしSQL、DROP COLUMN を含む down migration、巨大テーブルへの同期インデックス作成。この3つは、実行前に必ず人間が承認します。Claude Codeにも、実行ではなく「対象テーブル・影響行数・ロック時間・復元元・検証クエリ」を書かせてから、人間が読みます。
ここで多くの人がハマる落とし穴があります。git checkout . や git checkout -- . で全部戻せると思い込むことです。これは追跡済みファイルしか戻しません。 新しく作ったファイル(未追跡)は消えるどころか残り、消したつもりのものが消えていない、ということが起きます。force push のときも --force と --force-with-lease を混同しがちです。戻す操作こそ、慌てずにコマンドの意味を確認してください。
復旧が確認できたら、止血で切った機能フラグを戻し、メンテナンス表示を解除します。この“戻す”を忘れて、復旧済みなのにサービスが止まったまま、というのも演習でよく出る抜けです。
やってはいけないこと、3つ
正直に書きます。冒頭の決済事故のとき、僕はこの3つを全部やりました。
ひとつ目、焦って本番で直接いじる。 当たりをつけて本番サーバーに入り、その場で書き換えた。検証していないから、関係ない機能を巻き込んだ。本番の修正は、手元かステージングで再現・検証してから、正規のデプロイで入れる。遠回りに見えて、これがいちばん速い。
ふたつ目、Claude Codeに「直して」と全権委任する。 障害の興奮状態でAIに丸投げすると、AIは最初の仮説で本番を触ろうとします。AIは調査・要約・仮説出し・連絡文の下書きに使う。実行する手は人間が握る。役割を分けるだけで事故が減ります。
みっつ目、振り返りで犯人を探す。 「誰のせいか」を詰めると、次から誰も正直に報告しなくなり、検知がさらに遅れます。見るのは人ではなく仕組み。「なぜこの操作が本番で通ったのか」「なぜ検知が遅れたのか」を直す。これはGoogle SREのPostmortem Cultureの考え方そのものです。
連絡とポストモーテムのテンプレート
障害中の連絡は、短く、時刻入りで。原因が未確定なら「未確定」と正直に書きます。沈黙がいちばん不安を煽ります。
## 障害連絡
- 状態: 調査中 / 封じ込め済み / 復旧確認中
- 影響: 対象機能、対象ユーザー、開始時刻
- 現在の対応: 停止した処理、戻したデプロイ、確認中のログ
- 次の更新: YYYY-MM-DD HH:mm
- 問い合わせ窓口:
落ち着いたら、振り返り(ポストモーテム)を書きます。立派な文書はいりません。小さな表で十分です。
# ポストモーテム: [障害名]
## 概要
- 発生 / 検知 / 復旧 の各時刻:
- 影響:
- 重大度: P0/P1/P2/P3
## タイムライン
| 時刻 | 出来事 |
| --- | --- |
| HH:mm | |
## 原因
- 直接原因:
- 根本原因:
- 検知が遅れた理由:
## 再発防止
| 対策 | 担当 | 期限 |
| --- | --- | --- |
| | | |
ポストモーテムを書くときも、Claude Codeにタイムラインの素材(コミット履歴、デプロイ時刻、アラート発報時刻)を時系列に整理させると下書きが速いです。ただし「根本原因」の確定だけは人間がやる。AIの推定をそのまま結論にしないことです。
平時にやっておく:物理的に止める設定
対応フローを覚えても、危険操作が技術的に通ってしまえば、いつか疲れている日に踏みます。だから平時に、本番事故につながる操作を ask(人間に確認)か deny(拒否)へ寄せておきます。
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Bash(git push --force *main*)",
"Bash(git push -f *main*)",
"Bash(rm -rf /*)",
"Bash(rm -rf ~*)"
],
"ask": [
"Bash(git push*)",
"Bash(rm*)",
"Bash(npm install*)",
"Bash(*migrate*)",
"Bash(*deploy*)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-danger.sh"
}
]
}
]
}
}
hookは、危険コマンドを exit code 2 で止める形にします。設定の deny をすり抜けるパターンも、ここで二重に止められます。
#!/usr/bin/env bash
set -euo pipefail
payload="$(cat)"
command="$(node -e 'const fs = require("fs"); const raw = fs.readFileSync(0, "utf8") || "{}"; const json = JSON.parse(raw); console.log(json.tool_input?.command || "");' <<< "$payload")"
blocked='(rm[[:space:]]+-rf[[:space:]]+(/|~)|git[[:space:]]+push[[:space:]].*(-f|--force)([[:space:]]|$)|DROP[[:space:]]+TABLE|TRUNCATE[[:space:]])'
if [[ "$command" =~ $blocked ]]; then
echo "危険なコマンドをブロックしました: $command" >&2
exit 2
fi
exit 0
権限設定の早見表は権限設定リファレンスに、初心者がまず守る安全対策はセキュリティの記事にまとめています。GitHubの秘密情報検知については公式のsecret scanning documentationも合わせて見ておくと、APIキー漏えいの検知が一段早くなります。
よくある質問
Q. 障害中、原因が分からないまま止めていいんですか? A. はい。止めて困るのが「一部機能が使えない」程度なら、迷わず止めてください。原因究明は止血のあとでゆっくりやれます。「止めてから調べる」が本番の鉄則です。
Q. Claude Codeに本番のロールバックを任せてもいい? A. やめたほうがいいです。Claude Codeは「戻す対象・戻らないデータ・確認コマンド」を書き出させるまでにとどめ、実行コマンドを叩く手は人間が持ってください。実行を分けるだけで二次災害がかなり減ります。
Q. git checkout . で変更を全部戻せますか?
A. 戻せません。追跡済みファイルしか戻らず、未追跡の新規ファイルは残ります。復旧操作こそコマンドの効果を確認してから。--force と --force-with-lease の混同も事故のもとです。
Q. ポストモーテムって、個人ブログや小規模でも必要? A. 必要です。立派な文書でなく、原因と期限つき対策を書いた小さな表で十分。次に同じ事故が起きたとき、判断が一気に速くなります。犯人探しにせず、仕組みの修正だけ書くのがコツです。
Q. 障害の練習はどうやればいい? A. 開発環境でダミー障害を1つ作り、実際に時刻を入れて回します。「APIキーがPRに出た」「DBマイグレーションが失敗した」「バッチがAPIを呼び続けた」あたりが定番。詰まった場所が、そのまま本番前に直すべき運用の弱点です。
実際に試した結果
あの金曜以来、僕は「AIが直してくれるか」で悩むのをやめました。代わりに固定したのが、この記事の順番です。検知→止血→切り分け→復旧→振り返り。とくに**「止めてから調べる」**を体に入れてから、二次災害がゼロになりました。
Claude Codeの使いどころも変わりました。障害の最中に「直して」と頼むのではなく、ログを時間帯で切って渡して要約させ、原因候補を可能性順に並べさせ、連絡文の下書きを作らせる。実行する手は自分が握る。この線引きだけで、依頼が曖昧にならなくなりました。
一度、開発環境で20分のダミー障害をやってみてください。検知メモ、止血案、復旧案、連絡文、ポストモーテムの空欄を埋めるだけでいい。たいてい、足りないアラート、見つからないバックアップ、決まっていない担当が一気に見えます。そこが、あなたのリポジトリの“次に落ちる場所”です。
テンプレ化してチームで運用するならClaudeCodeLabの教材一覧を、CLAUDE.md・権限・hooks・障害訓練までまとめて整えるなら研修・導入相談が現実的です。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
まず無料PDFで基本を固め、繰り返し使う作業はGumroad教材へ、チーム導入や権限設計は導入相談へ進めます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
Claude Codeに1ファイルだけ直させる指示文のつくり方
「もっと良くして」で40行も変えられた失敗から学んだ、触る範囲・検証・戻し方をセットにしたClaude Code用の依頼文テンプレートを紹介します。
Claude Code の権限拒否から復旧する: 止まった理由を次の安全手順に変える
Claude Code のコマンドが拒否されたとき、焦って許可を広げずに、拒否理由、代替手順、証拠コマンド、再試行条件へ分解する方法。
Claude Codeにビルド→スモークテスト→自動修正を回させる足場の作り方
最小スモークテストの選び方、失敗ログを食わせて直させるループ、回数上限と確認ゲートで暴走を止める方法を、コピペで動くコード付きで紹介します。