LESSON 40分

ストーリー

佐藤CTO
オンコール体制ができた。次はランブック — アラートを受けたエンジニアが迷わず対応できる手順書を整備する
あなた
今は障害対応が属人的で、経験のあるエンジニアしか対応できない状況です
佐藤CTO
それは知識の属人化だ。障害対応の知識をランブックとして体系化すれば、新人エンジニアでもオンコール対応ができるようになる。さらに、ランブックの一部は自動化できる

ランブックとは

ランブックの定義と目的

要素説明
定義システム障害や運用タスクに対する標準化された対応手順書
目的誰でも一貫した品質で障害対応ができるようにする
対象者オンコールエンジニア、SREチーム、開発チーム
形式Markdown/Wiki、コマンド付き手順、判断ツリー
更新頻度インシデント対応後、ポストモーテム後に随時更新

ランブックが必要な理由

graph TD
    subgraph "ランブックがない場合"
        A1["障害発生"] --> A2["誰か知ってる人いない?<br/>(Slackで呼びかけ)"]
        A2 --> A3["詳しい人が捕まらない<br/>(深夜・休暇中)"]
        A3 --> A4["試行錯誤で復旧を試みる"]
        A4 --> A5["MTTR: 60分以上"]
    end

    subgraph "ランブックがある場合"
        B1["障害発生"] --> B2["アラートにランブックURLが含まれている"]
        B2 --> B3["手順に従い診断・復旧を実行"]
        B3 --> B4["MTTR: 15分以内"]
    end

    style A5 fill:#ffebee,stroke:#c62828
    style B4 fill:#e8f5e9,stroke:#2e7d32

ランブックテンプレート

基本テンプレート

# ランブックテンプレート
runbook:
  metadata:
    title: "[アラート名] 対応手順"
    alert_name: "AlertName"
    severity: "SEV1/SEV2/SEV3"
    service: "対象サービス名"
    last_updated: "2026-02-14"
    author: "SRE Team"
    review_date: "2026-05-14"  # 3ヶ月ごとにレビュー

  overview:
    description: "このアラートが示す状況の説明"
    impact: "ユーザーへの影響"
    urgency: "対応の緊急度"

  diagnosis:
    steps:
      - order: 1
        action: "ダッシュボードで現在の状況を確認"
        command: "https://grafana.example.com/d/xxx"
        expected: "エラー率のスパイクまたはレイテンシ増加"
      - order: 2
        action: "関連サービスのログを確認"
        command: |
          kubectl logs -l app=payment-service --tail=100 -n production
        expected: "エラーメッセージから原因を特定"

  decision_tree:
    question: "エラーの種類は?"
    options:
      - condition: "DB接続エラー"
        goto: "remediation_db"
      - condition: "外部APIタイムアウト"
        goto: "remediation_external"
      - condition: "メモリ不足(OOM)"
        goto: "remediation_oom"

  remediation:
    remediation_db:
      steps:
        - action: "DB接続数を確認"
          command: "kubectl exec -it postgres-0 -- psql -c 'SELECT count(*) FROM pg_stat_activity;'"
        - action: "接続プールの再起動"
          command: "kubectl rollout restart deployment/payment-service -n production"
    remediation_external:
      steps:
        - action: "外部APIのステータスページを確認"
          command: "curl -s https://status.stripe.com/api/v2/status.json | jq '.status'"
        - action: "サーキットブレーカーの状態を確認"
          command: "curl http://payment-service:8080/actuator/health"
    remediation_oom:
      steps:
        - action: "メモリ使用量の確認"
          command: "kubectl top pods -n production -l app=payment-service"
        - action: "Podの再起動"
          command: "kubectl rollout restart deployment/payment-service -n production"

  escalation:
    when: "上記の手順で解決しない場合"
    contact: "SREリード → エンジニアリングマネージャー"

  post_resolution:
    - "Slackの #incidents チャンネルに解決報告を投稿"
    - "PagerDutyのインシデントをResolvedにする"
    - "ポストモーテムの要否を判断(SEV1/SEV2は必須)"

決定木(Decision Tree)の設計

障害診断の決定木

graph TD
    A["アラート: API応答時間がSLO超過"] --> B{"エラー率も<br/>上昇している?"}
    B -->|"はい"| C{"エラーの種類は?"}
    C -->|"5xx"| D{"特定エンドポイント<br/>のみ?"}
    D -->|"はい"| E["依存サービスを確認"]
    D -->|"いいえ"| F["アプリケーション全体の問題<br/>→ 最新デプロイを確認<br/>→ ロールバック検討"]
    C -->|"5xx + DB接続エラー"| G{"DB接続エラーが<br/>含まれる?"}
    G -->|"はい"| H["DB接続プール/DB負荷を確認"]
    G -->|"いいえ"| I["アプリケーションログを確認"]
    C -->|"429"| J["外部APIのレート制限<br/>→ バックオフ設定を確認"]

    B -->|"いいえ"| K["レイテンシのみ上昇"]
    K --> L{"全エンドポイント<br/>で上昇?"}
    L -->|"はい"| M["インフラ(CPU/メモリ/ネットワーク)を確認"]
    L -->|"いいえ"| N["スロークエリログ/トレースを確認"]
    K --> O{"特定の時間帯のみ?"}
    O -->|"はい"| P["トラフィックパターン確認<br/>→ スケーリング検討"]

    A --> Q{"最近のデプロイ<br/>がある?"}
    Q -->|"はい"| R["デプロイ前後のメトリクスを比較<br/>→ ロールバック実行"]
    Q -->|"いいえ"| S["インフラ変更・外部要因を調査"]

TypeScriptでの決定木表現

interface DecisionNode {
  question: string;
  options: DecisionOption[];
}

interface DecisionOption {
  condition: string;
  action?: string;          // 末端ノードの場合
  command?: string;         // 実行コマンド
  nextNode?: DecisionNode;  // 次の判断ノード
}

const apiLatencyRunbook: DecisionNode = {
  question: 'エラー率も上昇しているか?',
  options: [
    {
      condition: 'はい',
      nextNode: {
        question: 'エラーの種類は?',
        options: [
          {
            condition: '5xx サーバーエラー',
            nextNode: {
              question: '最新デプロイから30分以内か?',
              options: [
                {
                  condition: 'はい',
                  action: 'ロールバックを実行',
                  command: 'kubectl rollout undo deployment/api-server -n production',
                },
                {
                  condition: 'いいえ',
                  action: 'アプリケーションログを確認し根本原因を特定',
                  command: 'kubectl logs -l app=api-server --tail=200 -n production | grep ERROR',
                },
              ],
            },
          },
          {
            condition: 'DB接続エラー',
            action: 'DB接続プールの状態を確認し、必要に応じて再起動',
            command: 'kubectl rollout restart deployment/api-server -n production',
          },
        ],
      },
    },
    {
      condition: 'いいえ(レイテンシのみ上昇)',
      action: 'CPU/メモリ使用率とスロークエリを確認',
      command: 'kubectl top pods -n production && kubectl logs -l app=api-server --tail=50 | grep "slow query"',
    },
  ],
};

自動化ランブック

手動ランブックから自動化ランブックへ

段階内容
Level 0ドキュメントなし属人的対応
Level 1手動ランブックMarkdownの手順書
Level 2半自動ランブック診断コマンドのスクリプト化
Level 3自動ランブックアラートトリガーで自動診断・復旧
Level 4自己修復システムが自動的に問題を検知し修復

自動診断スクリプトの例

// automated-runbook/diagnose-api-latency.ts
import { execSync } from 'child_process';

interface DiagnosisResult {
  timestamp: Date;
  checks: CheckResult[];
  recommendation: string;
  autoRemediationPossible: boolean;
}

interface CheckResult {
  name: string;
  status: 'ok' | 'warning' | 'critical';
  value: string;
  threshold: string;
}

async function diagnoseApiLatency(): Promise<DiagnosisResult> {
  const checks: CheckResult[] = [];

  // 1. Pod のリソース使用量チェック
  const podMetrics = execSync(
    'kubectl top pods -l app=api-server -n production --no-headers'
  ).toString();
  const cpuUsage = parseCpuUsage(podMetrics);
  checks.push({
    name: 'CPU使用率',
    status: cpuUsage > 80 ? 'critical' : cpuUsage > 60 ? 'warning' : 'ok',
    value: `${cpuUsage}%`,
    threshold: '80%',
  });

  // 2. DB接続数チェック
  const dbConnections = execSync(
    "kubectl exec postgres-0 -n production -- psql -t -c \"SELECT count(*) FROM pg_stat_activity WHERE state = 'active';\""
  ).toString().trim();
  const connCount = parseInt(dbConnections, 10);
  checks.push({
    name: 'DB Active Connections',
    status: connCount > 80 ? 'critical' : connCount > 50 ? 'warning' : 'ok',
    value: `${connCount}`,
    threshold: '80',
  });

  // 3. 最新デプロイの確認
  const lastDeploy = execSync(
    'kubectl rollout history deployment/api-server -n production --revision=0'
  ).toString();
  const isRecentDeploy = checkRecentDeploy(lastDeploy, 30); // 30分以内
  checks.push({
    name: '直近デプロイ(30分以内)',
    status: isRecentDeploy ? 'warning' : 'ok',
    value: isRecentDeploy ? 'あり' : 'なし',
    threshold: '-',
  });

  // 推奨アクションの決定
  const criticalChecks = checks.filter(c => c.status === 'critical');
  let recommendation: string;
  let autoRemediationPossible: boolean;

  if (isRecentDeploy && criticalChecks.length > 0) {
    recommendation = 'デプロイのロールバックを推奨';
    autoRemediationPossible = true;
  } else if (connCount > 80) {
    recommendation = 'DB接続プールの再起動を推奨';
    autoRemediationPossible = true;
  } else {
    recommendation = '詳細なログ調査が必要';
    autoRemediationPossible = false;
  }

  return {
    timestamp: new Date(),
    checks,
    recommendation,
    autoRemediationPossible,
  };
}
ランブックの品質チェックリスト

良いランブックの条件:

チェック項目基準
前提知識システム専門家でなくても理解できるか
コマンドの正確性コピペで実行できるか(パスや変数が正確か)
判断基準「正常」と「異常」の判断基準が数値で示されているか
分岐状況に応じた分岐が網羅されているか
エスカレーション手順で解決しない場合の次のアクションが明記されているか
最終更新日3ヶ月以内に更新されているか
テスト済み実際のインシデントまたは訓練で検証済みか

ランブックが陳腐化する原因と対策:

  1. システム構成の変更 → デプロイパイプラインにランブックレビューを組み込む
  2. コマンドの変更 → CI/CDでランブック内のコマンドを自動テスト
  3. 新しい障害パターン → ポストモーテムのアクションアイテムに「ランブック更新」を含める

まとめ

ポイント内容
ランブックの目的障害対応の標準化、属人性の排除、MTTRの短縮
テンプレート概要→診断→判断ツリー→対処→エスカレーション→事後対応
決定木状況に応じた分岐で適切な対処を誘導する
自動化手動→半自動→全自動→自己修復の段階的進化
継続的更新ポストモーテム後のランブック更新を必須プロセスにする

チェックリスト

  • ランブックの構成要素と目的を説明できる
  • テンプレートに従ったランブックを作成できる
  • 決定木を使った障害診断フローを設計できる
  • ランブック自動化の段階を理解した
  • ランブックの品質維持プロセスを把握した

次のステップへ

次は「カオスエンジニアリング入門」を学びます。ランブックが実際に機能するか、意図的に障害を注入して検証する手法 — カオスエンジニアリングの基礎を学びましょう。


推定読了時間: 40分