Article

夜間・休日の緊急対応体制の構築方法

高田晃太郎
夜間・休日の緊急対応体制の構築方法

業界調査では、重大インシデントの約半数が営業時間外に発生し、平均復旧時間(MTTR: Mean Time To Recovery)は通常時間帯より長くなる傾向があると報告されています。夜間は人のパフォーマンスが落ちやすく、対応品質やスピードに影響が出ることは多くの現場で共有される実感です²,³。通知のノイズ、引き継ぎの欠落、ツール間の断絶が重なると、深夜の一報が二報三報へと連鎖し、翌日の開発生産性まで侵食します²。GoogleのSRE実践でも「睡眠を妨げるページ(オンコール通知)は例外的であるべき」と繰り返し示唆され¹、夜間のページ件数を構造的に減らす設計こそが本質だと理解できます。公開されている事例やガイドでは、カバレッジ設計と通知ルーティングの整流化、さらに自動化を組み合わせることで、MTTA(平均応答時間)やMTTRの短縮が期待できるとされています²。ここでは、夜間・休日の緊急対応体制を、設計原則からIaC(Infrastructure as Code: 構成のコード化)、通知制御、SLO(サービス目標)、演習、制度・ROIまで、CTO視点で一気通貫に組み立てます。

設計原則とカバレッジ戦略を固める

最初に決めるべきは体制の輪郭です。地理的に分散しているならフォロー・ザ・サン(各タイムゾーンでの持ち回りによる24時間体制)を、単一タイムゾーン中心ならプライマリとセカンダリの二重待機にマネージャー・オンコールを加えた三層構造を基本に据えます。どちらの方式でも、睡眠を妨げる夜間ページは最後の手段に位置付け、適切な閾値・抑制・メンテナンスウィンドウを前提にします¹。シフトの長さは人の限界を基準にし、夜間帯の連続待機は一週間を超えない設計が望ましいと考えます¹。交代タイミングには明確なハンドオフ手順を設け、営業終了直前のデプロイを避ける変更凍結ポリシーとセットで管理します。法務・労務の観点では、深夜・休日労働の割増やオンコール手当の定義、在宅待機の労働時間算定、36協定や就業規則への明記、勤怠とアラートログの突合といった基礎を整え、ブラックボックス化を避けます⁵。セキュリティ面では、夜間対応における権限昇格の一時化、踏み台/監査ログの記録、個人端末アクセスの禁止をルールとして予め固定し、深夜ゆえのヒューマンエラーを構造で抑え込みます。

エスカレーションと当番設計は運用の背骨です。プライマリはアクティブなトラブルシューティングを担い、セカンダリはプライマリ不在や複合障害時の支援に限定します。マネージャー・オンコールは調整と優先順位の決定、コミュニケーションの外向き窓口に専念し、深夜に余計な手を出さない役割を明確にします。これにより、技術的解決と関係者調整が競合しない流れが生まれます。交代時のハンドオフには、未解決アラート、進行中の緩和策、ロールバック可否、関係システムのリスクを一続きの文にまとめ、確認を返すリバース・アック(受領の逆確認)を義務にします。通知ポリシーは重大度の定義(SEV: Severity)から逆算し、SEV1のみコール、SEV2はプッシュ通知と即時ステータス可視化、SEV3はチケット化と翌営業日の対応へと落とし込みます。何を夜間にやらないかを先に決めることで、人的な限界と事業継続性の均衡点を探ります¹。

持続可能性は数で管理します。アラート1件あたりの平均対応時間とページ頻度を可視化し、個人あたりの夜間ページが月間で一定のしきい値を超えたら改善アクションを自動で起票する仕組みを作ります。不可逆な疲弊が溜まる前に、アラートの発火条件、SLOの設定、そしてシステム側のセルフヒーリングを見直し続けます。重要なのは「ページを減らすための投資」を経営テーマに昇格させることです。単に頑張りで耐える体制は長続きしません。

通知の整流化と自動化を実装する

ツールが分断されていると、夜間の数分がそのままMTTRに乗ります。監視からルーティング、コミュニケーション、チケット、ステータス公開までを直線で結び、手作業を最小化します²。アラート抑制の中心には通知ハブを置き、重大度・時間帯・メンテナンスウィンドウ・ビジネスカレンダーを基準に動的ルーティングを行います²。メトリクスはメッセージングに同期させ、インシデント開始と同時に専用チャンネルを生成し、テンプレートの状況報告を自動投稿、タイムラインはイベントから自動合成するのが理想です。さらに、スケジュールとエスカレーションはコード化し、変更のたびに差分がレビューされる状態を維持します。

Alertmanagerで夜間抑制と重大度ルーティングを設計する

監視基盤にPrometheus Alertmanagerを採用しているなら、時間帯と重大度で通知先を切り替え、ノイズを夜間に流さない構成が有効です。以下はメンテナンスウィンドウとビジネス時間帯を考慮したルーティング例です。

route:
  receiver: ops-business-hours
  group_by: ['alertname','service']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 2h
  routes:
    - match:
        severity: critical
      receiver: ops-24x7-pager
    - match:
        severity: warning
      receiver: ops-business-hours
    - match_re:
        service: 'batch|etl'
      receiver: ops-suppress-night
receivers:
  - name: ops-24x7-pager
    pagerduty_configs:
      - routing_key: PAGERDUTY_INTEGRATION_KEY
  - name: ops-business-hours
    slack_configs:
      - channel: '#ops'
        send_resolved: true
  - name: ops-suppress-night
    slack_configs:
      - channel: '#ops-etl'
        send_resolved: true
inhibit_rules:
  - source_match:
      severity: critical
    target_match:
      severity: warning
    equal: ['alertname','service']

# time intervals (Alertmanager v0.27+)
time_intervals:
  - name: business-hours
    time_intervals:
      - weekdays: ['monday:friday']
        times:
          - start_time: '09:00'
            end_time: '19:00'
  - name: night
    time_intervals:
      - weekdays: ['monday:sunday']
        times:
          - start_time: '19:00'
            end_time: '09:00'

この設定は重大度criticalのみを24x7ページし、warningは業務時間帯に集約します。クリティカルに従属するワーニングは抑制され、同一原因での重複通知を避けます。夜間のバッチ系は専用チャンネルへ落ち、翌朝に拾い上げられるように流れを変えています。

Slackチャンネルの自動生成と初動テンプレート

ページと同時にインシデント用のSlackチャンネルを切り出し、役割と状況テンプレートを自動投稿しておくと初動が加速します。以下はPythonによる簡易実装例です。

import os
import time
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

SLACK_BOT_TOKEN = os.environ.get('SLACK_BOT_TOKEN')
client = WebClient(token=SLACK_BOT_TOKEN)

def create_incident_channel(incident_key: str, severity: str) -> str:
    name = f"inc-{severity.lower()}-{int(time.time())}-{incident_key[:6]}"
    try:
        resp = client.conversations_create(name=name, is_private=True)
        channel_id = resp['channel']['id']
        header = (
            f"[INCIDENT] key={incident_key} sev={severity}\n"
            "Roles: IC=?, Comms=?, Ops=?\n"
            "Status: investigating | mitigating | monitoring\n"
            "Next update: +15m\n"
        )
        client.chat_postMessage(channel=channel_id, text=header)
        return channel_id
    except SlackApiError as e:
        raise RuntimeError(f"Slack error: {e.response['error']}")

実運用では、ここにPagerDutyやJira/ServiceNowのイベントペイロードを受け取り、ステータスページのドラフト生成やタイムライン自動更新を追加します。チャンネル名規約と役割の即時宣言により、深夜帯でも迷いを減らせます。

スケジュールとエスカレーションをコード化する

待機スケジュールとエスカレーションはTerraformでコード化しておくと、属人化を防ぎ、変更履歴とレビューを担保できます。PagerDutyを例にします。

terraform {
  required_providers {
    pagerduty = {
      source  = "PagerDuty/pagerduty"
      version = "~> 2.16"
    }
  }
}

provider "pagerduty" {
  token = var.pagerduty_token
}

resource "pagerduty_schedule" "primary" {
  name      = "Primary On-call"
  time_zone = "Asia/Tokyo"
  layer {
    name                         = "Weekday Nights"
    start                        = "2025-01-01T00:00:00+09:00"
    rotation_virtual_start       = "2025-01-01T00:00:00+09:00"
    rotation_turn_length_seconds = 604800
    users = var.primary_user_ids
    restriction {
      type              = "daily_restriction"
      start_time_of_day = "19:00:00"
      duration_seconds  = 12 * 3600
    }
  }
}

resource "pagerduty_schedule" "secondary" {
  name      = "Secondary On-call"
  time_zone = "Asia/Tokyo"
  layer {
    name                         = "24x7 Secondary"
    start                        = "2025-01-01T00:00:00+09:00"
    rotation_virtual_start       = "2025-01-01T00:00:00+09:00"
    rotation_turn_length_seconds = 604800
    users = var.secondary_user_ids
  }
}

resource "pagerduty_escalation_policy" "policy" {
  name = "Ops Escalation"
  rule {
    escalation_delay_in_minutes = 10
    target {
      type = "schedule_reference"
      id   = pagerduty_schedule.primary.id
    }
  }
  rule {
    escalation_delay_in_minutes = 10
    target {
      type = "schedule_reference"
      id   = pagerduty_schedule.secondary.id
    }
  }
}

この構成では夜間帯のみプライマリを当て、プライマリが反応しない場合にセカンダリへ10分で昇格します。昼間帯の一次対応は別スケジュールに切り分け、開発と運用の両立を図れます。

SLOと演習で品質を維持する

体制を実装したら、品質を数値で管理します。ユーザー体験に直結するSLO(Service Level Objective: サービス目標)を定義し、違反の予兆をバーンレート(エラーバジェットの消費速度)で検知して夜間のページ条件とつなぐのが有効です。SLOは「夜間にページすべきか」を判断する境界線にもなります⁴。例えばAPIの可用性を対象に、月間99.9%を目標に置いた場合、短時間の断続的失敗は日中にまとめて対処する選択が合理的になることもあります⁴。

SlothでSLOとバーンレートアラートを生成する

Slothを使うとSLOからPrometheusの録画ルールとAlertmanagerのアラートを自動生成できます。夜間に限りページする閾値を盛り込み、残りエラーバジェットを使い切る速度に応じて通知レベルを変えます。バーンレートのしきい値はSRE実践で普及しているガイドラインに基づき設定します¹。

apiVersion: sloth.slok.dev/v1
kind: PrometheusServiceLevel
metadata:
  name: api-availability
spec:
  service: api
  slos:
    - name: availability
      objective: 99.9
      description: "API availability"
      sli:
        events:
          errorQuery: sum(rate(http_requests_total{job="api",code=~"5.."}[5m]))
          totalQuery: sum(rate(http_requests_total{job="api"}[5m]))
      alerting:
        pageAlert:
          labels:
            severity: critical
          annotations:
            description: "High burn rate"
          expr: |
            # 2h window burn > 14.4 (fast burn)
            (api_availability_error:ratio_rate2h > (1-0.999)*14.4)
        ticketAlert:
          labels:
            severity: warning
          annotations:
            description: "Slow burn"

閾値は目標とリスク許容度から逆算し、バーンが遅い場合は夜間ページを避け、翌営業日までチケットで保留します。逆に高速バーンは夜間でも即ページします。これにより、ページはビジネスに対する実害の近似に収束していきます¹,⁴。

シミュレーションと交代運用で体制を鍛える

机上のルールは夜間の現場で初めて試されます。四半期に一度はゲームデーを設け、深夜帯と同じ制約を模擬して、役割の呼吸と手順書の鮮度を確かめます。新任のプライマリは最初のローテーションでセカンダリとペアを組み、心理的安全性と学習速度を確保します。レビューは非難を排し、タイムラインを基に事実を整理し、構造的原因に手を入れます。ポストモーテムは30日以内の完了を目標にし、改善策に期限と責任者を伴わせて、次のハンドオフまでに反映します。これらの営みを通じて、夜間ページの削減、初動時間の短縮、解決までの時間の標準偏差の縮小といった、持続可能性を示す指標が安定していきます¹。

コスト設計とROIで持続性を証明する

経営に「なぜ夜間・休日体制に投資するのか」を説明するには、ダウンタイムの機会損失と人件費・手当・ツール費用を同じ土俵に載せます。まずは売上またはトランザクションの時間当たりの価値、SLA違反ペナルティ、ブランド損失の推定、そして人的コストを並べ、体制導入前後でのMTTR短縮やインシデント頻度低減の仮説を置いて差分を算出します。夜間ページの削減は、当事者の睡眠負債と離職リスクの低下、日中の開発生産性の回復として跳ね返ります³。制度設計は公平性と予見可能性が鍵で、オンコール手当、代休・振替、連続休暇ルールを明文化し、勤怠と連動させます⁵。予算編成では、当番人数と頻度、手当単価、ツールのシート課金、オートメーション開発の初期投資を年次で見積もり、ダウンタイム削減の保守的な前提でROI(投資対効果)を示します。数式化しておけば、投資規模と効果の会話が建設的になります。

簡易な評価はスクリプト化できます。以下は入力から年間の損益差分を推計するPythonの例です。

from dataclasses import dataclass

@dataclass
class Inputs:
    hourly_revenue: float      # 1時間あたりの売上/付加価値
    outage_hours_before: float # 年間ダウンタイム(導入前)
    outage_hours_after: float  # 年間ダウンタイム(導入後)
    penalty_per_hour: float    # SLA違反等の時間当たりペナルティ
    oncall_cost_annual: float  # 手当・ツール・運用の年間コスト
    productivity_gain_rate: float # 日中生産性の回復率(0-1)
    engineering_payroll: float # エンジニア人件費の年間合計

def estimate_roi(i: Inputs) -> dict:
    if i.outage_hours_after > i.outage_hours_before:
        raise ValueError("改善後のダウンタイムが増えています")
    saved_outage = i.outage_hours_before - i.outage_hours_after
    revenue_protect = saved_outage * i.hourly_revenue
    penalty_saved = saved_outage * i.penalty_per_hour
    prod_gain = i.engineering_payroll * i.productivity_gain_rate
    gross_benefit = revenue_protect + penalty_saved + prod_gain
    net_benefit = gross_benefit - i.oncall_cost_annual
    roi = net_benefit / i.oncall_cost_annual if i.oncall_cost_annual > 0 else float('inf')
    return {
        "saved_outage_hours": saved_outage,
        "gross_benefit": gross_benefit,
        "net_benefit": net_benefit,
        "roi": roi,
    }

この試算は意思決定の速度を上げ、議論を感情からデータに移します。加えて、年二回の前提見直しを定例化し、SLO・通知ポリシー・当番設計と連動して更新すれば、体制そのものが学習する装置になります。制度・技術・データの三位一体で、夜間・休日の緊急対応は、疲弊の源泉から競争力に転じます。

まとめ:眠れるオンコールは設計できる

夜間・休日の緊急対応体制は、気合ではなく設計の問題です。重大度の定義とSLOで「夜に起こすべき事象」を厳密に絞り、通知を整流化し、自動化で初動を短縮し、スケジュールとエスカレーションをコード化して透明化すれば、ページは減らせ、MTTRは縮む傾向が期待できます。演習とポストモーテムで学習を継続し、制度とROIで持続可能性を裏付ければ、オンコールは人と事業の双方に優しくなります。今日、どのアラートを夜間から外しますか。明日、どの自動化を一本入れますか。小さな一手が、眠れるオンコールへの最短距離になります。

参考文献

  1. Google SRE Workbook: On-Call. https://sre.google/workbook/on-call/
  2. PagerDuty Blog. Optimizing Alert Management. https://www.pagerduty.com/blog/incident-management-response/optimizing-alert-management/
  3. InfoQ. An Engineer’s Guide to Sleep: Making On-Call Less Tiring. https://www.infoq.com/articles/engineers-guide-to-sleep/
  4. Atlassian. SLO vs SLA vs SLI: What’s the difference? https://www.atlassian.com/incident-management/kpis/sla-vs-slo-vs-sli
  5. Y-K Law Office. 労働問題FAQ:時間外・休日・深夜割増賃金の考え方(労基法関連)。https://www.y-klaw.com/faq/faq345.html