深夜2時の電話が怖くなくなった。本番障害3回で学んだインシデント対応の本当のコツ
深夜の本番障害に何度も対応してわかったこと。英雄的対応から脱却し、自動復旧・SOP・チーム文化で障害を「パターン化」させた実話。
深夜2時の電話が怖くなくなった理由
先日も深夜1時に本番がぶっ壊れました。でも正直、前ほど怖くないんですよね。
3年前だったら、その夜は寝られず、朝まで不安でいっぱいでした。今は違う。電話が来ても「よし、パターン化されてるやつだな」って冷静に対応できる。むしろ「このインシデント、ドキュメント化しておくか」くらい思ってる。
何が変わったのか。別に僕のメンタルが強くなったわけじゃない。うちのチーム全体で「インシデント対応の仕組み」を3回ぶっ壊して、ようやく本当に動く体制ができたんです。
最初のやり方は「とにかく駆けつける」だった
うちのチームがインシデント対応で最初にハマったのは、いわゆる「英雄的対応」の文化。
深夜障害が発生すると、メンバーが競い合うように駆けつけて、誰かが3時間かけて手作業で復旧させる。みたいな。当時は「こういうものなんだ」って思ってました。
当時のフロー:
[障害発生]
↓
[全員起床・集結]
↓
[15分:状況把握会議]
↓
[2時間:手作業での復旧試行錯誤]
↓
[成功・失敗どちらでも朝日は見てる]
このパターンを半年繰り返してから気づいたんです。これ、絶対におかしい。毎回朝日を見るのってやっぱり不健康だし、何より同じ障害を繰り返してるのが無駄なんですよ。
自動復旧とSOP整備に本気で向き合った話
2023年の夏、僕らは本気でインシデント対応を変えることにしました。
1つ目:自動復旧の優先度付け
過去1年分のインシデントログを全部見直して、「本当に繰り返す障害」と「1回限りだったやつ」に分類しました。結果として、頻出する障害パターンは意外と少ない。
| 障害パターン | 発生頻度 | 割合 |
|---|---|---|
| Lambda Cold Startによる5分間のタイムアウト | 月1〜2回 | 30% |
| DynamoDBスロットリング | 月2〜3回 | 25% |
| RDS接続プール枯渇 | 月1回 | 15% |
| その他 | - | 30% |
この3つで全体の70%を占めてました。だったら、この3つには自動復旧を仕込もう。という判断になりました。
Lambda Cold Start対策として、実際に導入したコードがこれです:
# Lambda Cold Start対策:Provisioned Concurrencyの動的調整
# CloudWatch Metricsを監視して、レイテンシが閾値超えたら
# Lambda Concurrencyを+20%増やす(EventBridge + Lambda)
import json
import boto3
from datetime import datetime, timedelta
lambda_client = boto3.client('lambda')
cloudwatch = boto3.client('cloudwatch')
def auto_scale_provisioned_concurrency(event, context):
function_name = event['function_name']
# 現在のメトリクスを取得
response = cloudwatch.get_metric_statistics(
Namespace='AWS/Lambda',
MetricName='Duration',
Dimensions=[{'Name': 'FunctionName', 'Value': function_name}],
StartTime=datetime.now() - timedelta(minutes=5),
EndTime=datetime.now(),
Period=60,
Statistics=['Average']
)
avg_duration = sum([d['Average'] for d in response['Datapoints']]) / len(response['Datapoints'])
# 閾値チェック(例:800ms以上)
if avg_duration > 800:
# 現在のProvisioned Concurrencyを取得
config = lambda_client.get_provisioned_concurrency_config(
FunctionName=function_name,
Qualifier='LIVE'
)
current_capacity = config['ProvisionedConcurrentExecutions']
new_capacity = int(current_capacity * 1.2)
lambda_client.put_provisioned_concurrency_config(
FunctionName=function_name,
Qualifier='LIVE',
ProvisionedConcurrentExecutions=new_capacity
)
print(f"Scaled {function_name} from {current_capacity} to {new_capacity}")
return {'statusCode': 200}
2つ目:SOP(Standard Operating Procedure)の強制実行化
ここで大事な気づきがあったんですが、SOPって書くのは簡単なんです。Notionに手順書を作って、チェックリスト付けて。でも、実際に本番環境で守られることはほぼないんですよね。
重要な発見がありました:
SOP は「テキストのドキュメント」じゃなく「実行可能なコード」にしないと、本番では絶対に守られない。
だから、うちは runbook をすべて Lambda 関数として実装しました。DynamoDBスロットリング検出時の自動復旧ルールはこんな感じです:
# EventBridgeルール:DynamoDBスロットリング検出→自動復旧
Name: DynamoDBThrottleAutoRemediation
EventPattern:
source:
- aws.dynamodb
detail-type:
- AWS API Call via CloudTrail
detail:
eventName:
- ConsumedWriteCapacityUnits
Targets:
- Arn: arn:aws:lambda:ap-northeast-1:xxx:function:DynamoDBAutoScale
RoleArn: arn:aws:iam::xxx:role/EventBridgeInvokeLambda
こうすることで、「誰かが間違える」というリスクが減るんですよ。SOP はコードなので、バージョン管理もできるし、テストもできる。何より人間が判断を挟む余地がない。
チーム文化の改革:「責める」から「学ぶ」へ
ただ、技術的な自動化よりも重要だったのが、チーム内の文化を変えることでした。
昔は障害が起きたら、「誰がデプロイミスした」「どんなテストが抜けてた」という責任追及になってました。その結果、何が起きたか。
- メンバーが自分の業務中に「本番に何か入れるのが怖い」状態に
- 報告が遅れる(悪いニュースを隠したい心理)
- インシデント後のポストモーテムで本当の原因が出てこない
これは、ほぼ毎日起きてました。マジで悪循環ですよ。
2024年初頭、うちは徹底的に「ポストモーテム文化」に切り替えました。
新しいフロー:
障害が起きた
↓
[自動復旧で、15分以内に復旧]
↓
[24時間以内にポストモーテム実施]
↓
「なぜ本番で起きたのか」を技術的に掘る
「誰が悪いか」は絶対に言わない
↓
[再発防止策を実装:テスト追加・自動化・ドキュメント改善]
↓
[改善を実装するまでは、そのメンバーは関連業務から外さない]
この流れに変えてから、報告のスピードが劇的に変わりました。
実際、先月のとあるインシデントで、あるメンバーが「自分の実装で本番エラーが出た」って報告してくれたのは、障害発生後わずか3分でした。昔だったら、30分後に上司から連絡が来るまで黙ってたはずです。責める文化が消えたら、人間は本当に素早く動くんですね。
2026年時点のインシデント対応の「スタック」
うちが実装した、2026年時点での本当に動いてる構成を図で示します:
graph TB
subgraph Detection["検知層"]
CW[CloudWatch<br/>Alarms]
CWA[CloudWatch<br/>Anomaly Detection]
DD[Datadog<br/>APM]
end
subgraph Auto["自動復旧層"]
EB{EventBridge<br/>Rule}
L1["Lambda<br/>AutoRemediation<br/>Cold Start"]
L2["Lambda<br/>AutoRemediation<br/>DB Throttle"]
L3["Lambda<br/>AutoRemediation<br/>Connection Pool"]
end
subgraph Manual["手動対応層"]
SNS[SNS Notify<br/>Slack]
RUNBOOK["Runbook SOP<br/>Lambda実装"]
INCIDENT["PagerDuty<br/>Escalation"]
end
subgraph PostMortem["学習層"]
PM[ポストモーテム<br/>24h以内]
ACTION[改善施策実装]
TEST[テスト追加]
end
CW --> EB
CWA --> EB
DD --> EB
EB --> L1
EB --> L2
EB --> L3
L1 --> SNS
L2 --> SNS
L3 --> SNS
SNS --> RUNBOOK
RUNBOOK --> INCIDENT
INCIDENT --> PM
PM --> ACTION
ACTION --> TEST
TEST --> CW
リアルな失敗パターン:自動復旧が逆効果になった話
ここからは、ちょっと恥ずかしい話なんですけど…正直なところ、自動復旧をすべてに適用するのは危険だと気づきました。
去年、「データベース接続エラーが出たら自動でフェイルオーバーする」という自動復旧を実装したんです。
そしたら何が起きたか。実は根本原因は「SQLインジェクションによるDB枯渇」だったのに、自動復旧がそれを隠してしまって、攻撃が5時間続きました。人間が対応してたら、2回目のフェイルオーバーで「あ、これ何かおかしいぞ」ってわかったはず。
それ以来、うちのルールはこうになりました:
「自動復旧は『一時的な過負荷』には使う。根本原因が不明な場合は、自動復旧の前に人間に通知する」
# EventBridge Rule:フェイルオーバーする前に人間に通知
Name: DBFailoverWithNotification
Rules:
- Name: Notify-Before-Failover
EventPattern:
source: [aws.rds]
detail-type: [RDS DB Instance Event]
detail:
EventCategories: [availability]
Targets:
- Arn: arn:aws:sns:ap-northeast-1:xxx:IncidentResponse
RoleArn: arn:aws:iam::xxx:role/EventBridgePublishSNS
- Arn: arn:aws:lambda:ap-northeast-1:xxx:function:AnalyzeDBFailure
RoleArn: arn:aws:iam::xxx:role/EventBridgeInvokeLambda
DeadLetterConfig:
Arn: arn:aws:sqs:ap-northeast-1:xxx:IncidentDLQ
この失敗のおかげで、自動化の限界が見えました。100%自動化は夢物語で、人間の判断が必要な局面は確実に存在するんですよ。
SLO・SLIの設計が、実は一番重要だった話
インシデント対応で本当に大事なのは、実は「何が故障か」をきちんと定義することなんです。地味ですけど、これがすべての根底にある。
うちのチームが去年、SLI(Service Level Indicator)を重新定義しました。
| 定義 | 内容 | 問題点 |
|---|---|---|
| Old Definition | レスポンスタイム200ms以内 | バックエンド単体の指標。ユーザー体験を反映しない |
| New Definition | ユーザージャーニーの95%が、3秒以内に完了 | ビジネス視点。実際のユーザー体験を測定 |
何が違うか。古い定義だと、バックエンド単体のレスポンスは200msでも、フロントエンドのJavaScript処理が1秒かかってたら、ユーザーには3秒かかってるんです。見えない部分で品質が落ちてるんですよ。
そういう定義のズレを直したら、インシデント対応の優先度が劇的に変わりました。何に時間をかけるべきかが、ぐっと明確になったんです。
インシデント対応の現在地:2026年時点での課題
正直、すべてが完璧になったわけじゃないです。むしろ、新しい課題が見えてきました。
残ってる課題1:AI異常検知の誤検知
CloudWatch Anomaly Detection を導入して3ヶ月経ちますが、まだアラートの30%は誤検知です。学習期間不足の可能性もありますが、本番環境は「パターン変化」が多いので、機械学習が効きにくい側面があります。結局、本当の異常とノイズの境界線を引くのは難しいんですね。
残ってる課題2:ポストモーテムから改善までのギャップ
毎週のポストモーテムで「改善施策」を決めるんですが、実装まで行く率は正直60%くらいです。優先度付けや人員配置の課題が残ってます。これは技術的な問題というより、組織的な問題かもしれない。
残ってる課題3:複数障害の同時対応
自動復旧が入ると、1つの根本原因で複数の自動対応が走ることがあります。その結果、意図しない状態になることが時々ある。状態管理が難しいんですよ。例えば、スケーリング中にフェイルオーバーが走るみたいな。
この辺は、うちのSRE チームが今年の重点テーマになってます。
次のステップ:カオスエンジニアリングの本気導入
実は、今月から本格的にカオスエンジニアリングを導入しています。つまり「本当に自動復旧が動くのか」「本当にSOP が機能するのか」を、本番で定期的に検証するやつです。
毎週金曜の14時に「意図的に障害を起こして」本当に復旧するかテストしてます。最初は怖かったんですけど、やってみると本当に役に立つ。本番環境でテストされてない自動復旧の問題が、事前に見つかるんですから。
さらに言うと、SRE の深夜対応を減らすために、2026年後半には より包括的なアプローチを全部実装予定です。
まとめ
深夜2時の電話が怖くなくなった理由は、簡単です。
- 自動復旧を優先度付けして実装する:すべてではなく、繰り返す障害パターンだけに絞る
- SOP をコード化する:テキストドキュメントではなく Lambda 関数として実装
- 責任追及ではなくポストモーテム文化:「誰が悪いか」より「なぜ起きたか」「二度と起きない仕組み」を重視
- SLI は技術的ではなくビジネス的に定義:ユーザージャーニーベースの設計を心がける
- 自動復旧も定期検証する:カオスエンジニアリングで本当に動くかテスト
インシデント対応は「起きてからの対応」じゃなく、「起きにくく、起きても復旧が速い仕組み」を継続的に作る作業です。3年かけてようやくそこに辿り着きました。
いま、うちのチームの深夜障害は「対応が30分かかる」から「15分で自動復旧」になりました。まだ完璧じゃないし、課題もある。でも、確実に前に進んでます。
皆さんのチームはどうですか?深夜の電話に怯えてるなら、今がチャンスです。自動復旧の仕組みから、始めてみてください。