SLO

SLO(Service Level Objectives)完全ガイド — 信頼性目標の設計・実装・運用

はじめに

SLO(Service Level Objectives)は、サービスの信頼性を定量的に定義・管理するためのフレームワークである。GoogleのSRE(Site Reliability Engineering)から生まれた概念で、「どの程度の信頼性で十分か」をデータに基づいて決定し、エンジニアリングリソースの配分を最適化する。

SLOの核心は、100%の信頼性は不可能であり、目指すべきでもないという認識にある。99.999%の可用性と99.9%の可用性の差は、ユーザーが認識できる範囲を超えていることが多く、その差を維持するコストは指数関数的に増加する。SLOはこのトレードオフを定量的に管理するメカニズムである。

本記事では、SLI/SLOの設計原則、具体的な計測手法、エラーバジェットの運用、バーンレートアラート、SLOドキュメント、ツールチェーン、組織への導入戦略まで、SLOの全容を体系的に解説する。


第1章: SLOの基本フレームワーク

1.1 SLI → SLO → SLA → エラーバジェットの関係

┌─────────────────────────────────────────────────────────┐
│                                                          │
│  SLI (Service Level Indicator)                          │
│  「何を測るか」— サービス品質の定量的指標                  │
│  例: 成功リクエスト率、P99レイテンシ                       │
│                                                          │
│       ↓                                                  │
│                                                          │
│  SLO (Service Level Objective)                          │
│  「どの水準を目指すか」— 内部のエンジニアリング目標         │
│  例: 可用性 >= 99.9%(30日間ローリング)                   │
│                                                          │
│       ↓                                                  │
│                                                          │
│  SLA (Service Level Agreement)                          │
│  「顧客に何を約束するか」— 法的拘束力のある契約            │
│  例: 可用性 >= 99.5%(違反時はクレジット返金)             │
│  ※ SLA < SLO(SLOはSLAより厳しく設定すべき)              │
│                                                          │
│       ↓                                                  │
│                                                          │
│  エラーバジェット = 1 - SLO                               │
│  「どこまで失敗を許容するか」— イノベーションの余地         │
│  例: 0.1%のリクエストが失敗しても許容                      │
│                                                          │
└─────────────────────────────────────────────────────────┘

1.2 なぜ100%を目指さないのか

100%可用性を目指す場合の問題:

1. コストの指数的増加
   99%   → $X
   99.9% → $10X
   99.99% → $100X
   99.999% → $1000X+

2. イノベーションの停滞
   - 変更を一切入れないのが最も安全
   - しかしビジネスは進化を求める

3. ユーザーの認知限界
   - ユーザーはISP、ネットワーク、端末の問題もある
   - サービスが99.999%でも、ユーザー体験は99.9%以下かもしれない

4. 依存サービスの制約
   - 依存するサービスが99.9%なら、自サービスも99.9%が上限
   - 直列の依存: 99.9% × 99.9% × 99.9% = 99.7%

適切な信頼性 = ユーザーの期待を満たす最低限の水準
  → これがSLOの本質

1.3 SLOの設計原則

原則1: ユーザーの視点で定義する
  ❌ "CPU使用率が80%以下"(インフラ視点)
  ✅ "リクエストの99.9%が200ms以内に応答"(ユーザー視点)

原則2: 測定可能で明確にする
  ❌ "サービスが高速であること"(曖昧)
  ✅ "P95レイテンシが500ms以下のリクエストが99%以上"(明確)

原則3: 達成可能な目標にする
  ❌ 実績が99.95%なのにSLO 99.999%を設定(非現実的)
  ✅ 実績99.95%に対してSLO 99.9%を設定(現実的+改善余地)

原則4: シンプルに保つ
  ❌ 20個のSLIに対して各SLOを定義(複雑すぎる)
  ✅ 3-5個の最も重要なSLIにSLOを定義(管理可能)

原則5: 定期的に見直す
  - 四半期ごとにSLOの妥当性をレビュー
  - ビジネス要件の変化に対応
  - ユーザーからのフィードバックを反映

第2章: SLIの設計と計測

2.1 SLIの4つのカテゴリ(The Four Golden Signals準拠)

┌─────────────────────────────────────────────────────────┐
│  1. 可用性(Availability)                               │
│     「リクエストが正常に処理されたか」                     │
│                                                          │
│     SLI = 成功リクエスト数 / 全リクエスト数               │
│                                                          │
│     成功の定義:                                          │
│       - HTTP: status < 500                               │
│       - gRPC: status = OK                                │
│       - カスタム: ビジネスロジックに基づく成功判定         │
│                                                          │
│     除外すべきもの:                                      │
│       - ヘルスチェックリクエスト                          │
│       - 内部モニタリングリクエスト                        │
│       - ロードバランサーのプローブ                        │
├─────────────────────────────────────────────────────────┤
│  2. レイテンシ(Latency)                                │
│     「リクエストがどれだけ速く処理されたか」               │
│                                                          │
│     SLI = 閾値以内のリクエスト数 / 全リクエスト数         │
│                                                          │
│     複数の閾値を設定:                                    │
│       - P50 (中央値): ユーザーの半数の体験                │
│       - P95: ほとんどのユーザーの体験                     │
│       - P99: テールレイテンシ(最悪ケースの指標)         │
│                                                          │
│     注意: 平均値は使わない(外れ値に影響される)           │
├─────────────────────────────────────────────────────────┤
│  3. 品質/正確性(Quality/Correctness)                    │
│     「レスポンスの内容は正しいか」                        │
│                                                          │
│     SLI = 正確なレスポンス数 / 全レスポンス数             │
│                                                          │
│     例:                                                  │
│       - 検索結果が正しいか                               │
│       - レコメンデーションが劣化していないか              │
│       - データが最新か(鮮度 / Freshness)                │
├─────────────────────────────────────────────────────────┤
│  4. スループット/飽和度(Throughput/Saturation)           │
│     「システムがどの程度の負荷を処理できるか」             │
│                                                          │
│     SLI = 実際のスループット / 期待されるスループット       │
│     または: キューの待ち時間                              │
└─────────────────────────────────────────────────────────┘

2.2 SLI計測のアーキテクチャ

計測ポイントの選択:

① クライアント側(最もユーザーに近い)
   ┌──────┐
   │Client│──── 計測ポイント①
   └──┬───┘
      │
② ロードバランサー(推奨)
   ┌──┴───┐
   │  LB  │──── 計測ポイント②(推奨: ユーザー視点に最も近い)
   └──┬───┘
      │
③ アプリケーション
   ┌──┴───┐
   │  App │──── 計測ポイント③
   └──┬───┘
      │
④ データベース
   ┌──┴───┐
   │  DB  │──── 計測ポイント④(サーバー視点)
   └──────┘

推奨: ②ロードバランサーで計測
  理由: ユーザーに最も近く、アプリケーション障害も検知できる

2.3 具体的なSLI計測例

# Prometheus メトリクスベースの SLI 定義

# 可用性SLI
availability_sli:
  good_events: |
    sum(rate(http_requests_total{status!~"5.."}[5m]))
  total_events: |
    sum(rate(http_requests_total[5m]))
  formula: good_events / total_events

# レイテンシSLI (P99 < 1000ms)
latency_sli:
  good_events: |
    sum(rate(http_request_duration_seconds_bucket{le="1.0"}[5m]))
  total_events: |
    sum(rate(http_request_duration_seconds_count[5m]))
  formula: good_events / total_events
# Splunk SPL ベースの SLI 計算

# 可用性SLI(30日間ローリング)
index=web_logs earliest=-30d
| stats count(eval(status<500)) AS good, count AS total
| eval availability_sli = round(good / total * 100, 4)

# レイテンシSLI(P95 < 500ms)
index=web_logs earliest=-30d
| stats count(eval(response_time<=500)) AS good, count AS total
| eval latency_sli = round(good / total * 100, 4)

第3章: SLOの設定と文書化

3.1 SLOドキュメントテンプレート

# SLO Document: Payment API
service:
  name: "Payment API"
  owner: "Payment Team"
  description: "顧客の決済処理を行うAPI"
  dependencies:
    - "Database (PostgreSQL)"
    - "Payment Gateway (Stripe)"
    - "Fraud Detection Service"

slos:
  # SLO 1: 可用性
  - name: "Availability"
    description: "決済APIが正常に応答する割合"
    sli:
      type: "availability"
      good_event: "HTTP status < 500"
      valid_event: "All HTTP requests (excluding health checks)"
      measurement_point: "Load Balancer access logs"
    objective:
      target: 99.95%
      window: "30-day rolling"
    error_budget:
      total: "0.05% of requests"
      monthly_estimate: "~2,160 failed requests (based on 4.3M requests/month)"
    rationale: >
      決済APIは収益に直結するため、高い可用性が求められる。
      99.95%は月間約22分のダウンタイムに相当し、
      ユーザー影響を最小限に抑えつつ、改善・変更の余地を確保する。

  # SLO 2: レイテンシ(P50)
  - name: "Latency P50"
    description: "リクエストの50%が処理される時間"
    sli:
      type: "latency"
      threshold: "200ms"
      good_event: "response_time <= 200ms"
      valid_event: "All successful HTTP requests (status < 500)"
    objective:
      target: 99%
      window: "30-day rolling"
    rationale: "大多数のユーザーにとって快適なレスポンスタイム"

  # SLO 3: レイテンシ(P99)
  - name: "Latency P99"
    description: "リクエストの99%が処理される時間"
    sli:
      type: "latency"
      threshold: "2000ms"
      good_event: "response_time <= 2000ms"
    objective:
      target: 99.9%
      window: "30-day rolling"
    rationale: "テールレイテンシを2秒以内に抑え、最悪ケースのユーザー体験を保証"

error_budget_policy:
  review_cadence: "Weekly in SRE standup, Monthly in service review"
  thresholds:
    green:
      condition: "remaining > 50%"
      actions: ["Normal operations"]
    yellow:
      condition: "25% < remaining <= 50%"
      actions: ["Review upcoming changes for risk", "Prioritize reliability tasks"]
    orange:
      condition: "5% < remaining <= 25%"
      actions: ["Critical fixes only", "50%+ time on reliability"]
    red:
      condition: "remaining <= 5%"
      actions: ["Feature freeze", "Full focus on reliability", "Escalate to VP Eng"]

stakeholders:
  - role: "SRE Team"
    responsibility: "SLO monitoring, alerting, incident response"
  - role: "Development Team"
    responsibility: "Feature development within error budget, bug fixes"
  - role: "Product Manager"
    responsibility: "Prioritization decisions based on error budget status"
  - role: "VP Engineering"
    responsibility: "Escalation point for Red status"

review_history:
  - date: "2024-01-15"
    change: "Initial SLO document"
  - date: "2024-04-01"
    change: "Adjusted availability target from 99.9% to 99.95% based on Q1 data"

3.2 SLOウィンドウの選択

ローリングウィンドウ vs カレンダーウィンドウ:

ローリングウィンドウ(推奨):
  - 「過去30日間」のように、常に直近の期間を対象
  - 月初にリセットされない
  - 障害の影響がウィンドウから外れるまで残る
  
  例: 30日間ローリング
    1/15の障害 → 2/14まで影響が残る
    → より継続的な改善のインセンティブ

カレンダーウィンドウ:
  - 「2024年1月」のように、固定期間を対象
  - 月初にバジェットがリセット
  
  例: 月次カレンダー
    1/30の障害 → 2/1にバジェットリセット
    → 月末の障害が「許される」リスク

推奨: 30日間ローリングウィンドウ
  - SLO Report: 月次で報告
  - SLO Alert: リアルタイムで監視

第4章: エラーバジェットとバーンレートアラート

4.1 エラーバジェットの計算

基本計算:
  エラーバジェット(割合) = 1 - SLO目標
  エラーバジェット(絶対値) = 全リクエスト数 × (1 - SLO目標)

例: SLO = 99.9%、月間リクエスト = 10,000,000
  エラーバジェット = 10,000,000 × 0.001 = 10,000リクエスト

消費状況の計算:
  消費済み = 実際のエラー数 / エラーバジェット × 100%
  残量 = 100% - 消費済み

時間ベースの計算:
  SLO = 99.9%、30日間ウィンドウ
  許容ダウンタイム = 30日 × 24時間 × 60分 × 0.001 = 43.2分

4.2 バーンレート(Burn Rate)

バーンレート = 実際のエラーレート / SLOが許容するエラーレート

例: SLO = 99.9%(許容エラーレート = 0.1%)
  現在のエラーレート = 0.5%
  バーンレート = 0.5% / 0.1% = 5x

バーンレートの解釈:
  1x  → 30日でちょうどバジェットを使い切る(正常)
  2x  → 15日でバジェットを使い切る(やや危険)
  5x  → 6日でバジェットを使い切る(要対応)
  14.4x → 2日でバジェットを使い切る(緊急)
  720x → 1時間でバジェットを使い切る(クリティカル)

4.3 マルチウィンドウ・マルチバーンレートアラート

Google SRE Workbookが推奨するアラート戦略。

# Prometheus アラートルール

groups:
- name: slo-burn-rate-alerts
  rules:
  
  # ページ(即時対応): 短期的な高バーンレート
  # 1時間で2%のバジェットを消費(バーンレート14.4x)
  - 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: "Critical: Error budget burning at 14.4x rate"
      description: "Budget exhaustion in ~2 days. Immediate action required."

  # ページ: 中期的な高バーンレート
  # 6時間で5%のバジェットを消費(バーンレート6x)
  - alert: SLOBurnRateHigh
    expr: |
      (
        sum(rate(http_requests_total{status=~"5.."}[6h]))
        / sum(rate(http_requests_total[6h]))
      ) > (6 * 0.001)
      AND
      (
        sum(rate(http_requests_total{status=~"5.."}[30m]))
        / sum(rate(http_requests_total[30m]))
      ) > (6 * 0.001)
    for: 5m
    labels:
      severity: warning

  # チケット(次営業日対応): 長期的なゆるやかな消費
  # 3日間で10%のバジェットを消費(バーンレート1x)
  - alert: SLOBurnRateSlow
    expr: |
      (
        sum(rate(http_requests_total{status=~"5.."}[3d]))
        / sum(rate(http_requests_total[3d]))
      ) > (1 * 0.001)
    for: 1h
    labels:
      severity: info

マルチウィンドウの意図:

長期ウィンドウ(1h, 6h, 3d): トレンドの検出
  → 一時的なスパイクでは発火しない

短期ウィンドウ(5m, 30m): 現在進行中の問題の確認
  → 過去の問題で発火しない(問題が解決済みの場合)

両方を組み合わせることで:
  ✅ 本当の問題でのみアラート発火
  ❌ 一時的なスパイクでの誤報を防止
  ❌ 解決済み問題でのアラート残留を防止

第5章: SLOツールチェーンとダッシュボード

5.1 SLOツールの比較

ツール種類特徴
Prometheus + GrafanaOSSメトリクスベース、柔軟なアラート
SlothOSSPrometheusベースのSLO生成ツール
OpenSLOOSS標準SLO定義のオープン標準仕様
Datadog SLOsSaaSGUI操作、統合モニタリング
Google Cloud SLOSaaSGCPネイティブ、Istio連携
Splunk ITSISaaSSPLベース、ビジネスサービス連携
Nobl9SaaSSLO専用プラットフォーム
Dynatrace SLOsSaaSAIベースの自動SLO

5.2 OpenSLO仕様

# OpenSLO v1.0 仕様
apiVersion: openslo/v1
kind: SLO
metadata:
  name: payment-api-availability
  displayName: "Payment API Availability"
spec:
  service: payment-api
  description: "Payment API should be available 99.95% of the time"
  budgetingMethod: Occurrences
  objectives:
  - displayName: "Availability"
    target: 0.9995
    op: gte
    value: 1
    ratioMetrics:
      good:
        source: prometheus
        queryType: promql
        query: sum(rate(http_requests_total{service="payment-api",status!~"5.."}[5m]))
      total:
        source: prometheus
        queryType: promql
        query: sum(rate(http_requests_total{service="payment-api"}[5m]))
  timeWindow:
  - duration: 30d
    isRolling: true
  alertPolicies:
  - alertPolicyRef: payment-api-burn-rate

5.3 Sloth(Prometheus SLO Generator)

# sloth.yaml
version: "prometheus/v1"
service: "payment-api"
labels:
  owner: "payment-team"
  tier: "tier-1"
slos:
  - name: "requests-availability"
    objective: 99.95
    description: "Payment API availability"
    sli:
      events:
        error_query: sum(rate(http_requests_total{service="payment-api",status=~"5.."}[{{.window}}]))
        total_query: sum(rate(http_requests_total{service="payment-api"}[{{.window}}]))
    alerting:
      name: PaymentAPIAvailability
      labels:
        team: payment
      annotations:
        runbook: "https://wiki.example.com/runbooks/payment-api"
      page_alert:
        labels:
          severity: critical
      ticket_alert:
        labels:
          severity: warning
# Slothの実行(Prometheusルール生成)
$ sloth generate -i sloth.yaml -o prometheus-rules.yaml

5.4 SLOダッシュボードの設計

┌──────────────────────────────────────────────────┐
│              SLO Dashboard - Payment API           │
├──────────────────────────────────────────────────┤
│                                                    │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│  │ Availability │ │ Latency P50 │ │ Latency P99 │ │
│  │   99.97%     │ │   99.5%     │ │   99.92%    │ │
│  │  SLO: 99.95% │ │  SLO: 99%   │ │  SLO: 99.9% │ │
│  │  ✅ Met      │ │  ✅ Met     │ │  ✅ Met     │ │
│  └─────────────┘ └─────────────┘ └─────────────┘ │
│                                                    │
│  ┌─────────────────────────────────────────────┐  │
│  │ Error Budget Remaining                       │  │
│  │ ████████████████████░░░░░  78.3%             │  │
│  │ 消費: 21.7% | 残り: 7,830 requests           │  │
│  │ 現在のバーンレート: 0.8x (正常)              │  │
│  │ 枯渇予測: なし(現在のレートでは枯渇しない) │  │
│  └─────────────────────────────────────────────┘  │
│                                                    │
│  ┌─────────────────────────────────────────────┐  │
│  │ SLI Trend (30 days)                          │  │
│  │  99.99% ─┐                                   │  │
│  │  99.95% ─┤─── SLO Target ──────────────────  │  │
│  │  99.90% ─┤     ╱╲    ╱╲                      │  │
│  │  99.85% ─┤    ╱  ╲╱╱  ╲   ╱╲                │  │
│  │  99.80% ─┤───╱────────────╱──╲───────────    │  │
│  │          └──────────────────────────────────  │  │
│  │           Day 1        Day 15       Day 30    │  │
│  └─────────────────────────────────────────────┘  │
│                                                    │
│  ┌─────────────────────────────────────────────┐  │
│  │ Recent Incidents                             │  │
│  │ • 2024-01-10: DB failover (15min, 0.02% budget) │
│  │ • 2024-01-05: Deploy rollback (5min, 0.01%)  │  │
│  └─────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────┘

第6章: SLOの高度なパターン

6.1 依存サービスのSLO

直列依存(Sequential Dependencies):
  ServiceA → ServiceB → ServiceC
  
  合成SLO = SLO_A × SLO_B × SLO_C
  例: 99.9% × 99.9% × 99.9% = 99.7%
  
  → 依存が増えるほど、全体のSLOは低下する
  → 各サービスのSLOはエンドツーエンドSLOより高く設定する必要がある

並列依存(Parallel Dependencies with redundancy):
  ServiceA → ServiceB (primary)
           → ServiceC (fallback)
  
  両方が同時に失敗する確率:
  = (1 - SLO_B) × (1 - SLO_C)
  = 0.001 × 0.001 = 0.000001 = 99.9999%

6.2 ユーザージャーニーベースのSLO

# 単一エンドポイントではなく、ユーザージャーニー全体でSLOを定義
user_journey: "商品購入フロー"
steps:
  - name: "商品検索"
    slo: { availability: 99.9%, latency_p95: 500ms }
  - name: "商品詳細表示"
    slo: { availability: 99.9%, latency_p95: 300ms }
  - name: "カートに追加"
    slo: { availability: 99.95%, latency_p95: 200ms }
  - name: "決済処理"
    slo: { availability: 99.99%, latency_p95: 2000ms }
    
# 重要度に応じてSLOの厳しさを変える
# 決済 > カート > 検索 の順に厳しく

6.3 段階的SLOの導入

Month 1-2: 観測フェーズ
  - SLIの計測を開始(SLOは設定しない)
  - ベースラインデータを収集
  - チームにSLI/SLOの概念を教育

Month 3-4: 初期SLOフェーズ
  - ベースラインデータに基づいてSLOを設定
  - ダッシュボードの構築
  - アラートの初期設定(情報アラートのみ)

Month 5-6: 運用フェーズ
  - エラーバジェットポリシーの導入
  - バーンレートアラートの有効化
  - ポストモーテムとSLOの連携

Month 7+: 最適化フェーズ
  - SLOの見直しと調整
  - 他サービスへの展開
  - SLOベースの意思決定の定着

まとめ

SLOはサービスの信頼性を定量的に管理するための不可欠なフレームワークである:

  1. SLI設計: ユーザー視点で可用性/レイテンシ/品質/スループットを計測
  2. SLO設定: 達成可能で意味のある目標を3-5個に絞って定義
  3. エラーバジェット: イノベーションと信頼性のバランスを定量管理
  4. バーンレートアラート: マルチウィンドウ方式で誤報を最小化
  5. ダッシュボード: リアルタイムのSLO達成状況とエラーバジェット残量
  6. ツールチェーン: OpenSLO、Sloth、Prometheus、Grafana
  7. 文書化: SLOドキュメントによるステークホルダー間の合意
  8. 段階的導入: 観測→初期SLO→運用→最適化のフェーズ

参考文献