ブレームレスなポストモーテム — 今は失敗し、直せば通る対策
目標
決済インシデントの記録から、トリガーとなったデプロイ・根本原因・寄与要因を実際の行とともに分け、対策ごとに「今は失敗し、直せば通る」 検証コマンドを書きます。人の名前のないポストモーテムを書き、本番設定を実際に直し、検証が嘘をついていた前四半期の対策を正します。
なぜ重要か
ポストモーテムが約束するのは「二度と起きない」ことで、その約束を守るのは文章ではなく対策です。対策が「完了」で閉じられても、 検証が欠陥を捕まえられなければ一度も完了していないのと同じです。このラボの採点ツールは、皆さんの検証コマンドをインシデント当時のコピー、直したコピー二つ、 要因を一つだけ残したコピーに実際に実行してみます。現場で検証が嘘をつくあらゆるやり方(キーだけを見る、終了コードを覆う、古いコピー・別のキーを 見る、値一つを決め打ちする)がここで引っかかります。このラボは60分に設定されていて基本のセッション時間と同じなので、開始時にあらかじめ時間を延長しておいてください。
ステップ
python3 /opt/fixtures/ic/ickit.py open postmortemで素材を展開し、/root/ic/postmortem/README.txtを読んでください。app.logの最初の ERROR の直前に入ったデプロイのコミット sha(先頭7文字)と、そのデプロイが変えた設定ファイルを/root/ic/postmortem/answers/trigger.txtにdeploy=<sha>、changed=<etc/...file>の二行で書きます。/root/ic/postmortem/answers/evidence.txtに三行を書きます:trigger: <trigger-line>(きっかけとなったデプロイを指す deploys.log の commit の行かタイトルの行)、first_error: <first-error-line>(app.log の最初の ERROR 行)、recovery: <resolved-line>(このインシデントのアラートが解除された alerts.csv の行)。各行は素材にあるとおりでなければなりません。- README のチームの基準と
/srv/shop/etc/の設定、デプロイ記録を突き合わせ、根本原因一つ(トリガーとなったデプロイが基準外に変えたもの)と寄与要因二つ(もともと基準外だったもの)を/root/ic/postmortem/answers/factors.txtに書きます。原因の行(root_cause: <code>またはcontributing: <code>)のすぐ下にevidence: <line>を付けます。コード:alert-threshold-high(アラートしきい値が高い)、cache-ttl-zero(キャッシュ TTL が0)、deploy-no-healthgate(デプロイのヘルスチェックが無効)、pool-too-small(コネクションプールが小さい)、retry-no-backoff(リトライに待機がない)、timeout-too-long(ゲートウェイのタイムアウトが長い)。 - 根本原因と寄与要因ごとにアクションアイテムを一つずつ
/root/ic/postmortem/answers/actions.txtに書きます。対策ごとにaction: <what>、owner: <team>、factor: <factor-code>、verify: <shell-command>の四行です。verify は設定ツリーを環境変数$ROOTで受け取り(例:"$ROOT/etc/pool.conf")、その要因がチームの基準外なら0以外のコードで、基準内なら0で終わらなければなりません。採点ツールはインシデント当時のコピー(失敗すべき)、直したコピー二つ(値が違っても通るべき)、その要因だけを残したコピー(失敗すべき)に実際に実行してみます。 /root/ic/postmortem/answers/postmortem.mdにポストモーテムを書きます。## Summary(要約)、## Impact(影響)、## Root cause(原因)、## Actions(対策)の四つの節が必要で(節の名前は韓国語の## 요약・## 영향・## 원인・## 조치も受け付けます)、影響の節にはduration: <N>m(最初の ERROR からアラート解除までの分、切り捨て)とfailed_requests: <M>(app.log の ERROR 行数)、原因の節には根本原因・寄与要因のコード、対策の節には対策ごとにverify:の行を書きます。どの行にも人の名前(チャンネルハンドル)を書きません。素材を引用する行だけはevidence:で始めれば例外です。- 本番設定
/srv/shop/etc/を直して、三つの欠陥をチームの基準内に収めてください(基準内だった設定は壊しません)。その後、ステップ04の verify がROOT=/srv/shopですべて通るか確認します。 incident ic-unverifiableを実行するとインシデントが発生します。/srv/billing/postmortems/2026-q3.mdの対策 A1・A2・A3 はすべて done で、/srv/billing/bin/verify-actionsも三つとも PASS を出しますが、/srv/billing/etc/の設定は三つともチームの基準(/srv/billing/README.txt)の外です。(1) 対策ごとに verify を直し、欠陥が残っていれば失敗し、直れば通るようにし(factor と対策の順序はそのまま)、(2) 設定を実際に直して verify-actions が正直に PASS を出すようにしてください。採点ツールは直した verify を、インシデント当時のコピー(古いコピーetc/old/を含む)・直したコピー二つ・その要因だけを残したコピーに実行してみます。/root/incident/ic-unverifiable/report.txtにA1: <code>、A2: <code>、A3: <code>の三行とevidence: <original-verify-line>を書きます。コードは対策ごとに次のいずれかです:always-passes(値を見ずにキーの存在だけを見る)、exit-masked(失敗した終了コードを後ろで覆い隠す)、flaky-timing(時刻によって結果が変わる)、wrong-file(サービスが読まないファイルを見る)、wrong-key(名前の似た別のキーを見る)。
参考
- 素材ツール:
python3 /opt/fixtures/ic/ickit.py open <name>が素材を/root/ic/<name>/に展開します。素材は人ごとに異なるランダムシードで作られ、採点ツールは同じシードで正解を計算し直します。他人の数値は合いません。展開し直しても同じ素材で、--freshを付けると新しい素材で最初からになります。 - 答えは
/root/ic/<name>/answers/の下に書きます。セッションが終わると消えるので、必要なら別に保管してください。 - インシデントツール:
incident listで発生させられるインシデントと一行の症状を、incident show <name>でチケットを見直します。同じインシデントをもう一度発生させると、新しいランダムな素材で最初の故障状態になります(先に書いた答えは合わなくなります)。 - レポートは
/root/incident/<name>/report.txtです。対象ごとに<target>: <cause-code>を一行ずつ、最後にevidence: <line>をそのまま付けます。証拠は素材や観察記録にそのまま存在する一行(20文字以上)でなければなりません。採点ツールは直した後の実際の状態をまず見て、レポートは判断を確認するのに使います。 - verify は
bash -c '<command>'で、環境変数ROOTに設定ツリーのパスを入れ、そのツリーをカレントディレクトリにして実行されます。自分で試すにはROOT=/srv/shop bash -c '<command>'; echo $?のように実行して終了コードを見てください。 - 設定ファイルは
key=valueが一行ずつで、同じキーが何度も出てくると下の行が勝ちます。awk -F= '$1=="key" {v=$2} END {exit !(<condition>)}' <file>の形がそのルールに合っています。 - よくあるミス二つ: 検証の末尾に
|| echo …を付けて失敗を消してしまうこと、そして本番のパス(/srv/shop)を検証に埋め込んで$ROOTを無視することです。
素材の展開 — トリガーとなったデプロイを探す
python3 /opt/fixtures/ic/ickit.py open postmortem で素材を展開し、/root/ic/postmortem/README.txt を読んでください。app.log の最初の ERROR の直前に入ったデプロイのコミット sha(先頭7文字)と、そのデプロイが変えた設定ファイルを /root/ic/postmortem/answers/trigger.txt に deploy=<sha>、changed=<etc/...file> の二行で書きます。
app.log は UTC、git の Date はコミットした人のタイムゾーンです。「最新のデプロイ」ではなく「最初のエラーの直前のデプロイ」です。デプロイのコミット本文の change: 行が、変更したファイルを教えてくれます。
タイムラインの三本柱を実際の行で
/root/ic/postmortem/answers/evidence.txt に三行を書きます: trigger: <trigger-line>(きっかけとなったデプロイを指す deploys.log の commit の行かタイトルの行)、first_error: <first-error-line>(app.log の最初の ERROR 行)、recovery: <resolved-line>(このインシデントのアラートが解除された alerts.csv の行)。各行は素材にあるとおりでなければなりません。
grep で見つけた行をそのまま貼ってください(grep -n の行番号の接頭辞は付いていても構いません)。似た別の行や手で直した行は不合格になります。
根本原因と寄与要因
README のチームの基準と /srv/shop/etc/ の設定、デプロイ記録を突き合わせ、根本原因一つ(トリガーとなったデプロイが基準外に変えたもの)と寄与要因二つ(もともと基準外だったもの)を /root/ic/postmortem/answers/factors.txt に書きます。原因の行(root_cause: <code> または contributing: <code>)のすぐ下に evidence: <line> を付けます。コード: alert-threshold-high(アラートしきい値が高い)、cache-ttl-zero(キャッシュ TTL が0)、deploy-no-healthgate(デプロイのヘルスチェックが無効)、pool-too-small(コネクションプールが小さい)、retry-no-backoff(リトライに待機がない)、timeout-too-long(ゲートウェイのタイムアウトが長い)。
基準内にある設定は欠陥ではありません(おとり)。証拠はその要因を示すログ・デプロイ記録・アラートの行から選んでください。設定ファイルの一行は短すぎて証拠になりません。
検証できる対策
根本原因と寄与要因ごとにアクションアイテムを一つずつ /root/ic/postmortem/answers/actions.txt に書きます。対策ごとに action: <what>、owner: <team>、factor: <factor-code>、verify: <shell-command> の四行です。verify は設定ツリーを環境変数 $ROOT で受け取り(例: "$ROOT/etc/pool.conf")、その要因がチームの基準外なら0以外のコードで、基準内なら0で終わらなければなりません。採点ツールはインシデント当時のコピー(失敗すべき)、直したコピー二つ(値が違っても通るべき)、その要因だけを残したコピー(失敗すべき)に実際に実行してみます。
設定は key=value で、同じキーがまた出てくると下の行が勝ちます。キーの存在だけを見たり、値一つを文字どおり決め打ちしたり、終了コードを || で覆ったりすると不合格になります。awk -F= で最後の値を保持しておき、END で基準と比較して exit する形が便利です。自分で ROOT=/srv/shop bash -c '<command>'; echo $? で実行してみてください。
ブレームレスなポストモーテムを書く
/root/ic/postmortem/answers/postmortem.md にポストモーテムを書きます。## Summary(要約)、## Impact(影響)、## Root cause(原因)、## Actions(対策)の四つの節が必要で(節の名前は韓国語の ## 요약・## 영향・## 원인・## 조치 も受け付けます)、影響の節には duration: <N>m(最初の ERROR からアラート解除までの分、切り捨て)と failed_requests: <M>(app.log の ERROR 行数)、原因の節には根本原因・寄与要因のコード、対策の節には対策ごとに verify: の行を書きます。どの行にも人の名前(チャンネルハンドル)を書きません。素材を引用する行だけは evidence: で始めれば例外です。
原因は「誰が」ではなく「何が」(設定・手順・検査の不在)で書きます。数値は素材から数え直します。オーナーはチーム名で書きます。
対策を実際に行う
本番設定 /srv/shop/etc/ を直して、三つの欠陥をチームの基準内に収めてください(基準内だった設定は壊しません)。その後、ステップ04の verify が ROOT=/srv/shop ですべて通るか確認します。
サービスは同じキーが何度も出てくると下の行を使います。行を直しても末尾に追記しても、最後の値が基準内であれば構いません。直した後、verify を自分で実行して終了コードを見てください。
インシデント — 「検証通過」なのに同じインシデントがまた起きた
incident ic-unverifiable を実行するとインシデントが発生します。/srv/billing/postmortems/2026-q3.md の対策 A1・A2・A3 はすべて done で、/srv/billing/bin/verify-actions も三つとも PASS を出しますが、/srv/billing/etc/ の設定は三つともチームの基準(/srv/billing/README.txt)の外です。(1) 対策ごとに verify を直し、欠陥が残っていれば失敗し、直れば通るようにし(factor と対策の順序はそのまま)、(2) 設定を実際に直して verify-actions が正直に PASS を出すようにしてください。採点ツールは直した verify を、インシデント当時のコピー(古いコピー etc/old/ を含む)・直したコピー二つ・その要因だけを残したコピーに実行してみます。/root/incident/ic-unverifiable/report.txt に A1: <code>、A2: <code>、A3: <code> の三行と evidence: <original-verify-line> を書きます。コードは対策ごとに次のいずれかです: always-passes(値を見ずにキーの存在だけを見る)、exit-masked(失敗した終了コードを後ろで覆い隠す)、flaky-timing(時刻によって結果が変わる)、wrong-file(サービスが読まないファイルを見る)、wrong-key(名前の似た別のキーを見る)。
方向: 検証が嘘をつく道はいくつもあります。何を読むか(ファイル・キー)、何と比較するか(値を見てはいるか)、終了コードが最後まで生き残るか。対策ごとに三つのうちどこがずれているかを見ます。 ツール: 元の verify を ROOT=/srv/billing bash -c '<command>'; echo $? で自分で実行する、README の基準、grep -n。 具体: まず verify を直して今の設定で失敗するのを目で確かめ、その次に設定を直して通るようにしてください。順序を逆にすると、検証が欠陥を捕まえるかどうかがわかりません。