深夜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時の電話が怖くなくなった理由は、簡単です。

  1. 自動復旧を優先度付けして実装する:すべてではなく、繰り返す障害パターンだけに絞る
  2. SOP をコード化する:テキストドキュメントではなく Lambda 関数として実装
  3. 責任追及ではなくポストモーテム文化:「誰が悪いか」より「なぜ起きたか」「二度と起きない仕組み」を重視
  4. SLI は技術的ではなくビジネス的に定義:ユーザージャーニーベースの設計を心がける
  5. 自動復旧も定期検証する:カオスエンジニアリングで本当に動くかテスト

インシデント対応は「起きてからの対応」じゃなく、「起きにくく、起きても復旧が速い仕組み」を継続的に作る作業です。3年かけてようやくそこに辿り着きました。

いま、うちのチームの深夜障害は「対応が30分かかる」から「15分で自動復旧」になりました。まだ完璧じゃないし、課題もある。でも、確実に前に進んでます。

皆さんのチームはどうですか?深夜の電話に怯えてるなら、今がチャンスです。自動復旧の仕組みから、始めてみてください。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事