- 未知の環境の障害はなぜ違うのか
- ステップ1 — アクセスと権限を確認する
- ステップ2 — 症状を再現する
- ステップ3 — レイヤーを切り分ける
- ステップ4 — 原因仮説を立てる
- ステップ5 — 仮説を検証する
- ステップ6 — 報告書を書く
- 手を動かして練習する
未知の環境の障害はなぜ違うのか
自分のチームのサービスに障害が起きたら、どこを見るべきか体が知っています。ダッシュボードのURL、ログの場所、直近のデプロイ履歴が頭に入っている。顧客先の障害は、その前提が全部消えた状態から始まります。モニタリングが何か、ログがどこに溜まるか、昨日何が変わったか、何も知らないまま、隣では顧客がいつ直るのかと尋ねてきます。
この条件で必要なのは、優れた直感ではなく固定された順序です。この記事はその順序を6ステップで書き下ろしたプレイブックです。貫く事例は一つに絞ります。午後から決済APIが断続的に504を返すという報告 — 実際の事件ではなくこの記事のために構成した例で、登場するコマンドはkubectl、curl、grepのような公開された汎用ツールだけを使います。
ステップ1 — アクセスと権限を確認する
本能はまずログを開けと言いますが、未知の環境での最初のステップはアクセス確認です。どの環境にどの経路で入れるのか、いま持っている権限は読み取りか書き込みか、本番かステージングか。理由は二つ。第一に、診断の途中で権限がないと気づくと、その申請と承認にかかる時間が丸ごと無駄になります。第二に、権限の外の行動はそれ自体が事故です。顧客の本番環境で権限を越えるコマンド一つは、障害そのものより大きな信頼の損失を生みます。
確認事項は短い。アクセス経路は生きているか。アカウントの権限範囲はどこまでか。読み取り専用で診断を終えられるか。書き込みが必要になったら誰に頼むのか。この4行を障害チケットの最上部に書いてから始めます。
ステップ2 — 症状を再現する
「ときどき遅いんです」は診断できません。診断できるのは測定値だけです。だから二番目のステップは、報告された症状をコマンド一つに固定することです。
# 症状をコマンド一つに固定する(構成した例)
curl -sS -o /dev/null -w "%{http_code} %{time_total}s\n" \
https://api.customer.example/v1/payments/health
# 20回繰り返して失敗率を測る
for i in $(seq 1 20); do
curl -s -o /dev/null -w "%{http_code}\n" https://api.customer.example/v1/payments/health
done | sort | uniq -c
この繰り返しで20回中3回が504なら、障害は伝聞から数値に変わります。再現しないなら、それも情報です。特定のユーザー、特定の時間帯、特定の経路でだけ起きる問題だという意味で、問いはその分だけ狭まります。再現コマンドは以後のすべてのステップで、修理できたかを判定する物差しとして再利用されます。
ステップ3 — レイヤーを切り分ける
原因を探す前に、原因が住んでいる町内を探します。ネットワーク、認証、アプリケーション、データの4層に、それぞれ質問を一つずつだけ投げます。DNSとTLSは正常か。401や403が混ざっていないか。Podは再起動なしに生きているか。データ層の遅延がアプリに伝播していないか。
# レイヤーごとに質問を一つずつ(構成した例)
kubectl -n payments get pods -o wide # アプリ層: 状態と再起動回数
kubectl -n payments describe deploy/api | grep -A3 Limits # リソース上限
kubectl -n payments get endpoints api # ServiceとPodの接続
kubectl -n payments logs deploy/api --since=30m | grep -ciE "timeout|refused|pool"
構成した例では、Podの再起動はなく、401もなく、ログにはタイムアウト系の文字列が固まっています。すると、ネットワーク層と認証層は容疑者リストから外れ、アプリとデータの間のどこかに絞られます。レイヤー切り分けの目的は正解を当てることではなく、もう見なくていい場所を確定することです。
ステップ4 — 原因仮説を立てる
ここからは、ログをやみくもに漁りたい誘惑と戦う区間です。未知の環境のログは海で、仮説なしに入ると時間だけが消えます。残った容疑区域で仮説を二つ三つ、明示します。構成した例ならこうなります。第一、DBコネクションプールの枯渇 — ログのpool文字列と断続性が合致します。第二、特定クエリの遅延 — データが溜まって実行計画が変わった可能性。第三、午後のデプロイや設定変更 — 時刻が重なるかの確認が必要です。
良い仮説の条件は一つです。何を見れば棄却されるかが明確であること。反証の方法が思いつかない仮説は、診断ではなく当て推量です。仮説リストはチケットにそのまま書きます。あとで報告書の半分になります。
ステップ5 — 仮説を検証する
仮説ごとに、棄却または確証に必要な最小限の証拠だけを取りに行きます。
# 仮説1: プール枯渇 — 発生頻度と時間分布(構成した例)
kubectl -n payments logs deploy/api --since=2h \
| grep -c "connection pool exhausted"
kubectl -n payments logs deploy/api --since=2h \
| grep "connection pool exhausted" | cut -c1-16 | sort | uniq -c
# 14:05以降にだけ固まっているなら → その時刻の変更履歴を顧客に尋ねる
構成した例の結末はこうです。エラーは14時5分以降にだけ現れ、顧客に尋ねるとその時刻に設定デプロイがあり、デプロイのdiffでプールサイズが縮んでいました。設定を戻した後、ステップ2の再現コマンドをもう一度回して20回中0回の失敗を確認します。検証の核心は二方向です。原因を確証する証拠、そして修理後に同じ物差しで測り直した正常値。後者がなければ、直したとは言えません。
ステップ6 — 報告書を書く
FDEの障害対応はシステムではなく報告書で終わります。技術的に完璧に直しても、報告が遅かったり曖昧だったりすれば、顧客の記憶に残るのは不安だけです。最初の段落には影響と現在の状態が来ます。原因分析はその後です。
[障害報告 — 構成した例]
影響 : 決済API 5xx率 平時0.1% → 最大7%(14:10–15:40)
現在の状態: 緩和策の適用完了、エラー率が平時の範囲に戻ったことを確認
暫定原因 : 14:05の設定デプロイでDBコネクションプールのサイズが縮小
次の一手 : 差し戻し完了。再発防止としてデプロイ前の設定diff点検手順を提案予定
人を名指ししないという原則は、顧客先では特に重い。「顧客側の担当者が設定を誤って変えたため」と書いた瞬間、次の障害でその担当者は情報を隠します。システムがどう失敗したかだけを書きます。非難のない報告書は道徳ではなく、次の診断の速度を買う投資です。
手を動かして練習する
このプレイブックは、読むより体験する方が百倍速く身につきます。
- FDEエンジニア育成RPG — 「遅いんです」「つながらないんです」「モニタリングが先に死にました」のような31のミッションは、すべてこの6ステップの変奏です。時間と信頼度が削られる圧力の中で順序を守る練習ができます。
- FDEカリキュラム ロードマップ — レイヤー切り分けに必要なドメイン別スキルをチェックリストで点検します。
FDE完全ガイドシリーズ
현재 단락 (1/38)
自分のチームのサービスに障害が起きたら、どこを見るべきか体が知っています。ダッシュボードのURL、ログの場所、直近のデプロイ履歴が頭に入っている。顧客先の障害は、その前提が全部消えた状態から始まります...