LESSON 40分

ストーリー

佐藤CTO
オブザーバビリティ基盤ができた。メトリクス、トレース、ログが揃っている。次の問題はいつ、誰に、何を通知するか
あなた
今のアラートは多すぎて、チームが疲弊しています。1日に50件以上のアラートが飛んで、ほとんどが対応不要です
佐藤CTO
それがアラート疲れ(Alert Fatigue)だ。アラートが多すぎると、本当に重要なアラートも無視されるようになる。効果的なアラート設計 — シグナルとノイズを分離する技術を学ぼう

アラート疲れ(Alert Fatigue)

アラート疲れとは

本来対応すべきアラートが大量の不要アラートに埋もれ、重要なアラートが見逃される現象です。

指標健全な状態アラート疲れ状態
1日あたりアラート数0〜5件50件以上
アクショナブル率80%以上20%以下
アラートの無視率5%以下50%以上
平均対応時間5分以内30分以上(埋もれる)
オンコールエンジニアの満足度高い非常に低い

アラート疲れの悪循環

graph TD
    A["大量のアラートが通知される"] --> B["ほとんどが対応不要(ノイズ)"]
    B --> C["エンジニアがアラートを無視し始める"]
    C --> D["本当に重要なアラートも見逃される"]
    D --> E["重大インシデントの検知が遅れる"]
    E --> F["「アラートが役に立たない」という認識が広まる"]
    F --> G["アラートへの信頼が完全に失われる"]
    G -->|"悪循環"| A

    style A fill:#ffebee,stroke:#c62828
    style G fill:#ffebee,stroke:#c62828

シグナルとノイズの分離

良いアラートの3条件

interface GoodAlert {
  // 1. アクショナブル: 受信者が何かアクションを取る必要がある
  actionable: true;
  // 2. 緊急性: すぐに対応しなければ影響が拡大する
  urgent: true;
  // 3. 明確な対応先: 誰が対応すべきか明確
  hasOwner: true;
}

// アラートの分類フレームワーク
type AlertClassification =
  | 'page'      // 即時対応が必要 → PagerDuty
  | 'ticket'    // 営業時間内に対応 → Jiraチケット
  | 'log'       // 記録のみ → ダッシュボード
  | 'suppress'; // 不要 → 削除

アラート分類マトリクス

緊急性高い緊急性低い
ユーザー影響ありPage(即時対応)Ticket(計画対応)
ユーザー影響なしTicket(営業時間対応)Log(記録のみ)

重要度レベル(Severity)

4段階のSeverityモデル

Severity名称基準通知方法対応時間
SEV1Criticalサービス全体停止、データ損失リスクPagerDuty即時呼出5分以内
SEV2Major主要機能の障害、一部ユーザー影響PagerDuty + Slack15分以内
SEV3Minor非主要機能の障害、回避策ありSlack通知営業時間内
SEV4Infoパフォーマンス低下、潜在的問題ダッシュボード次スプリント

Severity判定フロー

graph TD
    A["障害が発生"] --> B{"サービス全体が<br/>停止している?"}
    B -->|"はい"| SEV1A["SEV1"]
    B -->|"いいえ"| C{"主要機能が<br/>使えない?"}
    C -->|"はい"| D{"影響ユーザー数<br/>> 10%?"}
    D -->|"はい"| SEV1B["SEV1"]
    D -->|"いいえ"| SEV2["SEV2"]
    C -->|"いいえ"| E{"非主要機能が<br/>使えない?"}
    E -->|"はい"| SEV3["SEV3"]
    E -->|"いいえ"| F{"パフォーマンス<br/>低下のみ?"}
    F -->|"はい"| SEV4["SEV4"]

    style SEV1A fill:#ffebee,stroke:#c62828,stroke-width:2px
    style SEV1B fill:#ffebee,stroke:#c62828,stroke-width:2px
    style SEV2 fill:#fff3e0,stroke:#e65100,stroke-width:2px
    style SEV3 fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
    style SEV4 fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px

Multi-Window Multi-Burn-Rate アラート

SLOベースアラートの原則

従来の「閾値超えアラート」ではなく、SLOのエラーバジェット消費速度(バーンレート)に基づくアラートです。

// バーンレートの計算
interface BurnRateAlert {
  // バーンレート = 実際のエラー率 / SLOで許容されるエラー率
  burnRate: number;
  // 長いウィンドウ: 全体的な傾向を把握
  longWindow: string;
  // 短いウィンドウ: 現在も問題が継続しているか確認
  shortWindow: string;
  // アラートの重要度
  severity: 'critical' | 'warning' | 'info';
}

// Multi-Window Multi-Burn-Rate設定
const burnRateAlerts: BurnRateAlert[] = [
  {
    burnRate: 14.4,
    longWindow: '1h',
    shortWindow: '5m',
    severity: 'critical',
    // 30日バジェットを約2日で消費するペース
  },
  {
    burnRate: 6.0,
    longWindow: '6h',
    shortWindow: '30m',
    severity: 'warning',
    // 30日バジェットを約5日で消費するペース
  },
  {
    burnRate: 3.0,
    longWindow: '3d',
    shortWindow: '6h',
    severity: 'info',
    // 30日バジェットを約10日で消費するペース
  },
];

Prometheusでの実装

# SLOベースのバーンレートアラート
groups:
  - name: slo_burn_rate
    rules:
      # エラー率の Recording Rule
      - record: sli:http_error_rate:5m
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m]))
          /
          sum(rate(http_requests_total[5m]))

      # Critical: バーンレート 14.4x(1時間ウィンドウ)
      - alert: SLOBurnRateCritical
        expr: |
          (
            sum(rate(http_requests_total{status=~"5.."}[1h]))
            / sum(rate(http_requests_total[1h]))
          ) > (14.4 * 0.001)
          and
          (
            sum(rate(http_requests_total{status=~"5.."}[5m]))
            / sum(rate(http_requests_total[5m]))
          ) > (14.4 * 0.001)
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "SLOバーンレートがCriticalレベル"
          description: "エラーバジェットが約2日で枯渇するペースで消費中"
          runbook_url: "https://wiki.example.com/runbooks/slo-burn-rate-critical"

      # Warning: バーンレート 6.0x(6時間ウィンドウ)
      - alert: SLOBurnRateWarning
        expr: |
          (
            sum(rate(http_requests_total{status=~"5.."}[6h]))
            / sum(rate(http_requests_total[6h]))
          ) > (6.0 * 0.001)
          and
          (
            sum(rate(http_requests_total{status=~"5.."}[30m]))
            / sum(rate(http_requests_total[30m]))
          ) > (6.0 * 0.001)
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "SLOバーンレートがWarningレベル"
          description: "エラーバジェットが約5日で枯渇するペースで消費中"
          runbook_url: "https://wiki.example.com/runbooks/slo-burn-rate-warning"

      # Info: バーンレート 3.0x(3日ウィンドウ)
      - alert: SLOBurnRateInfo
        expr: |
          (
            sum(rate(http_requests_total{status=~"5.."}[3d]))
            / sum(rate(http_requests_total[3d]))
          ) > (3.0 * 0.001)
          and
          (
            sum(rate(http_requests_total{status=~"5.."}[6h]))
            / sum(rate(http_requests_total[6h]))
          ) > (3.0 * 0.001)
        for: 30m
        labels:
          severity: info
        annotations:
          summary: "SLOバーンレートがInfoレベル"
          description: "エラーバジェットが約10日で枯渇するペースで消費中"

アラートルールの設計原則

アンチパターンと改善

アンチパターン問題改善
CPU > 80% でアラートCPU使用率が高くてもユーザー影響がない場合があるSLI(レイテンシ、エラー率)ベースに変更
閾値固定の静的アラートトラフィックパターンの変化に対応できないバーンレートベースの動的アラート
全アラートをPagerDuty送信アラート疲れの原因Severity分類で通知先を分離
アラートにランブックURLなし対応方法がわからない全アラートにrunbook_url付与
重複アラート同じ問題で複数アラート発火アラートのグルーピングと抑制

Alertmanagerでのルーティング

# alertmanager.yml
global:
  resolve_timeout: 5m
  slack_api_url: 'https://hooks.slack.com/services/xxx'

route:
  group_by: ['alertname', 'service']
  group_wait: 30s        # 同じグループのアラートを30秒間集約
  group_interval: 5m     # 同グループの再通知間隔
  repeat_interval: 4h    # 同じアラートの繰り返し通知間隔
  receiver: 'default-slack'
  routes:
    # Critical → PagerDuty即時通知
    - match:
        severity: critical
      receiver: 'pagerduty-critical'
      repeat_interval: 5m
    # Warning → Slack #alerts-warning
    - match:
        severity: warning
      receiver: 'slack-warning'
      repeat_interval: 1h
    # Info → Slack #alerts-info(営業時間のみ)
    - match:
        severity: info
      receiver: 'slack-info'
      repeat_interval: 12h
      mute_time_intervals:
        - outside-business-hours

receivers:
  - name: 'pagerduty-critical'
    pagerduty_configs:
      - service_key: '<pagerduty-service-key>'
        severity: critical
  - name: 'slack-warning'
    slack_configs:
      - channel: '#alerts-warning'
        title: '{{ .CommonLabels.alertname }}'
        text: '{{ .CommonAnnotations.description }}'
  - name: 'slack-info'
    slack_configs:
      - channel: '#alerts-info'
  - name: 'default-slack'
    slack_configs:
      - channel: '#alerts-general'

# 抑制ルール: Criticalが発火中はWarningを抑制
inhibit_rules:
  - source_match:
      severity: critical
    target_match:
      severity: warning
    equal: ['alertname', 'service']

# 営業時間外の定義
time_intervals:
  - name: outside-business-hours
    time_intervals:
      - weekdays: ['saturday', 'sunday']
      - times:
          - start_time: '00:00'
            end_time: '09:00'
          - start_time: '18:00'
            end_time: '24:00'
アラートの継続的改善プロセス

アラートは一度設計して終わりではありません。定期的にレビューし改善するプロセスが重要です。

週次アラートレビューのチェックリスト:

  1. 発火回数の確認: 各アラートの発火回数を集計
  2. アクション率の計算: 実際にアクションが取られたアラートの割合
  3. 偽陽性の特定: アクション不要だったアラートの原因を分析
  4. 不足の特定: アラートなしで検知された障害がなかったか
  5. 閾値の調整: データに基づいてアラート閾値を最適化
interface AlertReview {
  alertName: string;
  fireCount: number;
  actionTaken: number;
  falsePositives: number;
  recommendation: 'keep' | 'tune' | 'remove' | 'merge';
}

function calculateActionableRate(review: AlertReview): number {
  return review.actionTaken / review.fireCount * 100;
}

// アクション率50%未満のアラートは見直し対象
function flagForReview(reviews: AlertReview[]): AlertReview[] {
  return reviews.filter(r => calculateActionableRate(r) < 50);
}

まとめ

ポイント内容
アラート疲れ不要アラートの大量通知により重要アラートが見逃される問題
シグナル分離アクショナブル・緊急性・対応先の3条件で分類
SeverityモデルSEV1〜SEV4の4段階で通知方法と対応時間を分離
バーンレートアラートSLOベースのMulti-Window Multi-Burn-Rateで精度向上
継続的改善週次レビューでアクション率を計測し、ノイズを削減

チェックリスト

  • アラート疲れの原因と悪循環を説明できる
  • 良いアラートの3条件を理解した
  • Severity分類に基づく通知ルーティングを設計できる
  • Multi-Window Multi-Burn-Rateアラートの仕組みを説明できる
  • Alertmanagerのルーティング・抑制ルールを設定できる

次のステップへ

次は「オンコール体制の構築」を学びます。アラートを受け取る側の体制 — ローテーション、エスカレーション、ワークライフバランスの維持について深掘りしていきましょう。


推定読了時間: 40分