LESSON 30分

ストーリー

佐藤CTO
SLOが99.9%だとすると、0.1%は何だと思う?
あなた
0.1%のダウンタイムは…許容される失敗、ですか?
佐藤CTO
その通り。SREではこの0.1%をエラーバジェット — 使える失敗の予算 — と呼ぶ。これがSREで最も革新的な概念だ

佐藤CTOはグラフを描きながら続けます。

佐藤CTO
100%の信頼性を目指すのは非現実的であり、害悪ですらある。なぜなら変更を一切できなくなるからだ。エラーバジェットは、イノベーションの速度と信頼性のバランスを取る仕組みなんだ

エラーバジェットとは

基本概念

エラーバジェットは「SLO目標を達成しつつ、許容できる不信頼性の量」です。

エラーバジェット = 100% - SLO目標

例: SLO = 99.9% の場合
エラーバジェット = 100% - 99.9% = 0.1%
→ 30日間で 0.1% × 30 × 24 × 60 = 43.2分 の障害が許容される

エラーバジェットが解決する問題

従来の対立エラーバジェットによる解決
開発チーム「もっとリリースしたい」バジェットが残っていればリリースOK
運用チーム「変更は最小限にしたい」バジェット消費率で客観的に判断
経営層「速く、かつ安定して」SLOとバジェットで両方を数値管理
品質議論「十分安定しているのか?」エラーバジェットの残量で明確に回答

エラーバジェットの計算

基本的な計算

interface ErrorBudgetConfig {
  sloTarget: number;          // 例: 0.999 (99.9%)
  windowDays: number;         // 計測ウィンドウ(例: 30日)
  totalRequests?: number;     // リクエストベースの場合
}

interface ErrorBudgetStatus {
  totalBudgetMinutes: number;
  consumedMinutes: number;
  remainingMinutes: number;
  consumedPercentage: number;
  burnRate: number;           // 1.0 = 予定通りの消費速度
}

function calculateErrorBudget(
  config: ErrorBudgetConfig,
  downtimeMinutes: number
): ErrorBudgetStatus {
  const totalMinutes = config.windowDays * 24 * 60;
  const errorBudgetRatio = 1 - config.sloTarget;
  const totalBudgetMinutes = totalMinutes * errorBudgetRatio;

  const consumedPercentage = (downtimeMinutes / totalBudgetMinutes) * 100;
  const burnRate = consumedPercentage / 100;

  return {
    totalBudgetMinutes,
    consumedMinutes: downtimeMinutes,
    remainingMinutes: totalBudgetMinutes - downtimeMinutes,
    consumedPercentage,
    burnRate,
  };
}

// 例: SLO 99.9%, 30日ウィンドウ, これまでに15分のダウンタイム
const status = calculateErrorBudget(
  { sloTarget: 0.999, windowDays: 30 },
  15
);
// totalBudgetMinutes: 43.2分
// consumedMinutes: 15分
// remainingMinutes: 28.2分
// consumedPercentage: 34.7%
// burnRate: 0.347

リクエストベースのエラーバジェット

function calculateRequestBasedBudget(
  sloTarget: number,
  totalRequests: number,
  failedRequests: number
): {
  allowedErrors: number;
  actualErrors: number;
  remainingBudget: number;
  budgetConsumedPercent: number;
} {
  const allowedErrors = Math.floor(totalRequests * (1 - sloTarget));
  const remainingBudget = allowedErrors - failedRequests;
  const budgetConsumedPercent = (failedRequests / allowedErrors) * 100;

  return {
    allowedErrors,
    actualErrors: failedRequests,
    remainingBudget,
    budgetConsumedPercent,
  };
}

// 例: SLO 99.9%, 今月100万リクエスト, 500リクエスト失敗
const reqBudget = calculateRequestBasedBudget(0.999, 1_000_000, 500);
// allowedErrors: 1000
// actualErrors: 500
// remainingBudget: 500
// budgetConsumedPercent: 50%

エラーバジェットポリシー

エラーバジェットポリシーは、バジェットの消費状況に応じて取るべきアクションを定義したものです。

ポリシーテンプレート

# エラーバジェットポリシー
service: "payment-service"
slo_target: 99.95%
window: 30days

# バジェット消費レベルごとのアクション
levels:
  green:
    condition: "消費率 < 50%"
    actions:
      - 通常のリリースサイクルを継続
      - リスクの高い変更(大規模リファクタリング等)も許可
      - 新機能の実験的デプロイを許可
    review: 月次レビュー

  yellow:
    condition: "消費率 50% - 80%"
    actions:
      - リスクの高い変更は延期
      - リリース前のテスト基準を強化
      - ロールバック手順の再確認
      - 信頼性改善タスクの優先度を上げる
    review: 週次レビュー

  orange:
    condition: "消費率 80% - 100%"
    actions:
      - 信頼性に直接寄与しない変更を凍結
      - インシデント振り返りを即日実施
      - SREチームが全リリースをレビュー
      - 自動ロールバックの閾値を厳格化
    review: 日次レビュー

  red:
    condition: "消費率 > 100%(SLO未達成)"
    actions:
      - 全変更を凍結(セキュリティ修正を除く)
      - 経営層にエスカレーション
      - 信頼性改善を最優先プロジェクトとする
      - ポストモーテムを実施し根本原因を解消
    review: 日次レビュー(経営層参加)

  # バジェットのリセット
  reset:
    schedule: "毎月1日"
    carryover: false  # 未消費分の繰越なし

ポリシーの運用フロー

graph TD
    Check["毎日のバジェット確認"]
    Check --> Green["消費率 < 50%(Green)<br/>通常運用、イノベーション加速"]
    Check --> Yellow["消費率 50-80%(Yellow)<br/>リスク軽減、テスト強化"]
    Check --> Orange["消費率 80-100%(Orange)<br/>変更制限、信頼性改善優先"]
    Check --> Red["消費率 > 100%(Red)<br/>変更凍結、緊急対応モード"]
    Red --> Fix["根本原因の解消"]
    Fix --> Green

    classDef check fill:#6c757d,stroke:#495057,color:#fff
    classDef green fill:#198754,stroke:#146c43,color:#fff
    classDef yellow fill:#f5a623,stroke:#c47d10,color:#fff
    classDef orange fill:#fd7e14,stroke:#ca6510,color:#fff
    classDef red fill:#e94560,stroke:#c23050,color:#fff

    class Check check
    class Green green
    class Yellow yellow
    class Orange orange
    class Red,Fix red

バーンレート(Burn Rate)

バーンレートは、エラーバジェットがどのくらいの速度で消費されているかを示す指標です。

バーンレートの計算

バーンレート = 実際のエラー率 / 許容エラー率

例: SLO 99.9%(許容エラー率 0.1%)
    現在のエラー率が 0.5% の場合
    バーンレート = 0.5% / 0.1% = 5.0

→ 通常の5倍の速度でバジェットを消費している

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

Googleが推奨するアラート手法で、複数の時間ウィンドウとバーンレートを組み合わせてアラートを設定します。

# Multi-Window Multi-Burn-Rate アラート設定
# SLO: 99.9% (30日ウィンドウ)

alerts:
  # 急激なバジェット消費(高バーンレート × 短いウィンドウ)
  - name: "急速バーンレートアラート"
    severity: critical
    burn_rate: 14.4     # 30日分のバジェットを2日で消費するペース
    long_window: 1h     # 長いウィンドウ
    short_window: 5m    # 短いウィンドウ(誤検知防止)
    action: "即座にページ(オンコール呼び出し)"

  # 中程度のバジェット消費
  - name: "中速バーンレートアラート"
    severity: warning
    burn_rate: 6.0      # 30日分のバジェットを5日で消費するペース
    long_window: 6h
    short_window: 30m
    action: "チケット作成、当日中に対応"

  # 緩やかなバジェット消費
  - name: "低速バーンレートアラート"
    severity: info
    burn_rate: 3.0      # 30日分のバジェットを10日で消費するペース
    long_window: 3d
    short_window: 6h
    action: "次のスプリントで対応"

PromQLでのバーンレート計算

# 1時間ウィンドウでのバーンレート計算
# SLO: 99.9% (許容エラー率: 0.001)

# エラー率の計算
- record: slo:error_rate:1h
  expr: |
    1 - (
      sum(rate(http_requests_total{status!~"5.."}[1h]))
      /
      sum(rate(http_requests_total[1h]))
    )

# バーンレートの計算
- record: slo:burn_rate:1h
  expr: |
    slo:error_rate:1h / 0.001

# 急速バーンレートアラート
- alert: HighBurnRate
  expr: |
    slo:burn_rate:1h > 14.4
    and
    slo:burn_rate:5m > 14.4
  for: 2m
  labels:
    severity: critical
  annotations:
    summary: "エラーバジェットが急速に消費されています"
    description: "バーンレート: {{ $value }}x(1時間ウィンドウ)"
バーンレートの閾値の導出方法

Multi-Window のバーンレート閾値は、以下の考え方で導出されます。

30日ウィンドウのSLOの場合:

消費速度バーンレート意味
2日で30日分消費14.4x全バジェット消費まで2日
5日で30日分消費6.0x全バジェット消費まで5日
10日で30日分消費3.0x全バジェット消費まで10日
30日で30日分消費1.0xちょうどSLO境界上

計算式: バーンレート = SLOウィンドウ日数 / 消費完了日数

例: 30日 / 2日 = 15(実際は14.4 = 30 * 24 / (2 * 24 + αの余裕))

短いウィンドウは「長いウィンドウのアラートが誤検知でないことを確認する」ために使います。長いウィンドウだけだと、過去の障害の影響で誤報が発生しやすくなります。


エラーバジェットの活用パターン

パターン1: リリース判断

interface ReleaseDecision {
  canRelease: boolean;
  reason: string;
  conditions: string[];
}

function makeReleaseDecision(
  budgetConsumedPercent: number,
  changeRisk: 'low' | 'medium' | 'high',
  hasRollbackPlan: boolean
): ReleaseDecision {
  // Red: 変更凍結
  if (budgetConsumedPercent > 100) {
    return {
      canRelease: false,
      reason: 'SLO未達成 - 全変更凍結中',
      conditions: ['信頼性改善のみ許可'],
    };
  }

  // Orange: 信頼性に寄与しない変更は凍結
  if (budgetConsumedPercent > 80) {
    return {
      canRelease: changeRisk === 'low' && hasRollbackPlan,
      reason: 'バジェット残量少 - 低リスク変更のみ',
      conditions: [
        'ロールバック手順が確認済みであること',
        'SREレビューを通過していること',
      ],
    };
  }

  // Yellow: リスクの高い変更は延期
  if (budgetConsumedPercent > 50) {
    if (changeRisk === 'high') {
      return {
        canRelease: false,
        reason: 'バジェット注意レベル - 高リスク変更は延期',
        conditions: ['次のウィンドウまで延期を推奨'],
      };
    }
    return {
      canRelease: hasRollbackPlan,
      reason: 'バジェット注意レベル - ロールバック計画必須',
      conditions: ['ロールバック手順を用意すること'],
    };
  }

  // Green: 通常リリース
  return {
    canRelease: true,
    reason: 'バジェット十分 - 通常リリース可能',
    conditions: [],
  };
}

パターン2: 開発チームとの対話

開発チーム: 「大規模なDBマイグレーションを実施したい」

SREチーム確認:
  - 現在のバジェット消費率: 35%(Green)
  - 残りバジェット: 28分
  - 想定リスク: High(過去の類似作業で10分のダウンタイム)
  - 判断: 実施可能。ただし以下の条件付き
    1. メンテナンスウィンドウ内で実施
    2. ロールバック手順を事前に検証
    3. 段階的にマイグレーション(シャドーテーブル方式)

まとめ

ポイント内容
エラーバジェット100% - SLO目標 = 許容できる不信頼性の量
ポリシーバジェット消費率に応じてGreen/Yellow/Orange/Redのアクションを定義
バーンレートバジェット消費の速度。Multi-Window方式でアラートを設計
リリース判断バジェット残量と変更リスクを組み合わせて客観的に判断
チーム間の共通言語開発と運用の対立をデータで解消する

チェックリスト

  • エラーバジェットの計算方法を理解した
  • エラーバジェットポリシーの4段階を説明できる
  • バーンレートの概念とMulti-Window方式を理解した
  • PromQLでのバーンレートアラート設定を把握した
  • エラーバジェットを使ったリリース判断の考え方を理解した

次のステップへ

次は「トイルの削減」を学びます。SREの業務時間の50%以上をエンジニアリングに充てるために、手作業的な運用作業(トイル)をどう特定し、削減するかを学びましょう。


推定読了時間: 30分