SLO設計で2年間失敗し続けた僕が、ようやく運用が回り始めた話

「SLOは作ったのに誰も見てない」「バジェットが枯渇して初めて気づいた」そんな経験ありませんか?2年間の失敗から学んだ現実的な運用パターンを共有します。

SLI/SLOを「ちゃんと設計した」気になっていた時期があった。ドキュメントも整えた、ダッシュボードも作った。でも半年後にインシデントが起きて振り返ってみると、設定していた SLO が全員に無視されていた。というか、SLO が何を意味するか理解されていなかった。

その苦い経験から2年、今のチームではだいぶ整ってきたので、失敗談も含めて書き残しておこうと思う。「SLI/SLO の概念自体を基礎から学びたい」というよりは、「概念は知ってるけど実際の運用が難しい」という人に向けて書く。


なぜ最初の SLO 設計は死んだか

最初の失敗を振り返ると、大きく3つの問題があった。

1つ目は「技術者だけで閉じた SLO」だったこと。 HTTP 5xx エラー率 < 0.1% みたいなやつ。この数字、ユーザーにとって何を意味するか説明できますか? 当時の僕には正直できなかった。エラー率が 0.09% でも、ユーザーが使う決済フローでだけエラーが集中していたら意味がない。

2つ目はアラートと SLO の乖離。 SLO を設定したのに、アラートは別で管理されていた。SLO バジェットが枯渇しかけていても誰も気づかず、突然大障害になって初めて「あ、バジェットなかったのか」となった。

3つ目は SLO の数が多すぎた。 最初の設計で 20 個以上の SLI を定義した。運用しているうちに更新されなくなって、形骸化した。

インシデント対応の最新ベストプラクティス2026でも触れているけど、インシデントが起きてから SLO の大切さに気づくのはあるあるなんですよね。そうなる前に設計を見直しておきたい。


2026年の SLI 設計:ユーザージャーニーから逆算する

今は 「ユーザーが何をしたいか」から SLI を決める ようにしている。Google の SRE ワークブックにも書いてあることだけど、実際にやると思ったより難しい。

具体的には、まずサービスのクリティカルユーザージャーニー(CUJ)を3〜5個書き出す。うちのプロダクト(BtoB SaaS)だとこんな感じ。

CUJ 1: ユーザーがログインしてダッシュボードを開ける
CUJ 2: データをアップロードして処理が完了する
CUJ 3: レポートを生成してダウンロードできる
CUJ 4: API 経由で外部システムからデータを取得できる

これを見ながら、各 CUJ に対応する SLI を決める。

# SLO 定義ファイル(社内 YAML 管理)
slо_definitions:
  - name: "login_and_dashboard_availability"
    cuj: "ユーザーがログインしてダッシュボードを開ける"
    sli:
      type: request_based
      good_events: "HTTP 2xx レスポンス かつ レスポンスタイム < 2000ms"
      total_events: "全リクエスト"
      measurement_window: "28日間ローリング"
    slo_target: 99.5
    error_budget_alert_threshold: 50  # バジェット 50% 消化でアラート

  - name: "data_upload_processing"
    cuj: "データをアップロードして処理が完了する"
    sli:
      type: window_based
      good_window: "ジョブ投入から 5 分以内に完了"
      total_windows: "全ジョブ実行"
    slo_target: 99.0
    error_budget_alert_threshold: 30

この形式で管理することで、SLO がなぜ存在するかが誰にでもわかる。プロダクトマネージャーとの会話でも「この SLO はログインの体験を守るためです」と説明できる。

SLI の種類を使い分ける

地味に大事なのが SLI タイプの選択で、適当に選ぶと後で計算が壊れる。

SLI タイプ適用場面注意点
Request-basedAPI・Web レスポンスサンプリングに注意
Window-basedバッチ処理・非同期ウィンドウ定義が難しい
Uptime-based基盤インフラの疎通確認精度が粗い、補助指標向き
User-basedプレミアム顧客向け加重実装コスト高め

正直、Window-based SLI はまだ設計に迷うことが多い。バッチ処理のジョブが「完了」したとみなす定義が曖昧だと、SLO の計算がおかしくなる。ここは好み分かれるかも。


コンポジット SLO の実装:複数サービスをまとめて見る

2026年時点でうちのチームが一番効果を感じているのがコンポジット SLO だ。マイクロサービス構成だと、個別の SLO が全部グリーンなのにユーザー体験が悪いという状況が起きる。

例えばこういう依存関係があるとする。

flowchart TB
    User([ユーザー])
    FE[フロントエンド\nSLO: 99.9%]
    API[API Gateway\nSLO: 99.9%]
    Auth[認証サービス\nSLO: 99.95%]
    Data[データサービス\nSLO: 99.5%]
    DB[(PostgreSQL)]

    User --> FE
    FE --> API
    API --> Auth
    API --> Data
    Data --> DB

    subgraph composite ["コンポジット SLO 対象"]
        FE
        API
        Auth
        Data
    end

各サービスが SLO を達成しても、直列依存の場合は掛け算になる。0.999 × 0.999 × 0.9995 × 0.995 ≈ 0.993。つまりユーザー体験の実効可用性は 99.3% 程度になる。これをコンポジット SLO として別途定義する。

# コンポジット SLO 計算スクリプト
from dataclasses import dataclass
from typing import List
import math

@dataclass
class ServiceSLO:
    name: str
    target: float  # 0.999 のような小数
    dependency_type: str  # 'serial' or 'parallel'

def calculate_composite_slo(
    services: List[ServiceSLO],
    journey_name: str
) -> float:
    """
    ユーザージャーニー全体のコンポジット SLO を計算
    直列依存は積、並列依存は補集合の積の補集合
    """
    serial_services = [s for s in services if s.dependency_type == 'serial']
    parallel_groups = [s for s in services if s.dependency_type == 'parallel']
    
    # 直列依存の計算
    serial_availability = math.prod([s.target for s in serial_services])
    
    # 並列依存の計算(冗長化されているケース)
    if parallel_groups:
        parallel_unavailability = math.prod([1 - s.target for s in parallel_groups])
        parallel_availability = 1 - parallel_unavailability
        composite = serial_availability * parallel_availability
    else:
        composite = serial_availability
    
    print(f"Journey: {journey_name}")
    print(f"  各サービス SLO: {[f'{s.name}={s.target*100:.3f}%' for s in services]}")
    print(f"  コンポジット SLO: {composite * 100:.3f}%")
    print(f"  月間許容ダウンタイム: {(1-composite)*30*24*60:.1f} 分")
    return composite

# 実際に計算してみる
services = [
    ServiceSLO("frontend", 0.999, "serial"),
    ServiceSLO("api_gateway", 0.999, "serial"),
    ServiceSLO("auth_service", 0.9995, "serial"),
    ServiceSLO("data_service", 0.995, "serial"),
]

result = calculate_composite_slo(services, "ダッシュボード表示")

実行すると:

Journey: ダッシュボード表示
  各サービス SLO: ['frontend=99.900%', 'api_gateway=99.900%', 'auth_service=99.950%', 'data_service=99.500%']
  コンポジット SLO: 99.254%
  月間許容ダウンタイム: 53.7 分

この結果をプロダクトチームに見せると「え、各サービスは 99.9% なのに全体は 99.2%?」という反応になる。そこから「じゃあ data_service の SLO を引き上げましょう」という具体的な議論になる。数字があると会話が変わるんですよね、本当に。


Prometheus + Sloth でエラーバジェットを実装する

概念の話だけだとふわっとするので、実際の実装を見ていく。2026年時点でうちが使っているのは Sloth(SLO Generator)で、YAML で SLO を定義すると Prometheus ルールを自動生成してくれる。

# sloth-slo.yaml
version: "prometheus/v1"
service: "api-gateway"
labels:
  team: "platform"
  env: "production"

slos:
  - name: "requests-availability"
    objective: 99.9
    description: "API ゲートウェイの可用性 SLO"
    sli:
      events:
        error_query: |
          sum(rate(http_requests_total{
            job="api-gateway",
            code=~"5.."
          }[{{.window}}]))
        total_query: |
          sum(rate(http_requests_total{
            job="api-gateway"
          }[{{.window}}]))
    alerting:
      name: "ApiGatewayAvailability"
      page_alert:
        labels:
          severity: critical
          channel: pagerduty
      ticket_alert:
        labels:
          severity: warning
          channel: slack

Sloth でビルドすると Prometheus の recording rules と alerting rules が生成される。

$ sloth generate -i sloth-slo.yaml -o prometheus-rules.yaml

# 生成されたルールの確認
$ cat prometheus-rules.yaml | grep 'record:' | head -10
    record: slo:sli_error:ratio_rate5m
    record: slo:sli_error:ratio_rate30m
    record: slo:sli_error:ratio_rate1h
    record: slo:sli_error:ratio_rate6h
    record: slo:sli_error:ratio_rate1d
    record: slo:sli_error:ratio_rate3d
    record: slo:error_budget:ratio
    record: slo:time_period:days
    ...

このルールを Grafana でダッシュボード化するとこういう構成になる。

flowchart LR
    subgraph datasource ["データソース"]
        App[アプリケーション]
        Infra[インフラ]
    end

    subgraph collection ["収集・集計"]
        Prom[Prometheus]
        Sloth[Sloth SLO Generator]
    end

    subgraph visualization ["可視化・通知"]
        Grafana[Grafana Dashboard]
        AM[AlertManager]
        PD[PagerDuty]
        Slack[Slack]
    end

    App -->|metrics| Prom
    Infra -->|metrics| Prom
    Sloth -->|recording rules| Prom
    Prom -->|SLO metrics| Grafana
    Prom -->|alerts| AM
    AM --> PD
    AM --> Slack

Grafana SLO プラグイン(2025年末にリリースされた新版)も試してみたけど、正直 Sloth + カスタムダッシュボードの方が細かく制御できる。現状はそっちを使い続けている。ここはまだ検証中で、組織によって向き不向きがあると思う。

エラーバジェット消化率の可視化

月次でこういうグラフを出している。

xychart-beta
    title "エラーバジェット消化率 月次推移(%)"
    x-axis ["1月", "2月", "3月", "4月", "5月"]
    y-axis "消化率 (%)" 0 --> 120
    line [23, 45, 18, 102, 61]
    bar [23, 45, 18, 102, 61]

4月に 100% 超えているのは実際にインシデントがあった月だ。こういうグラフを見せると「4月は何があったか」という会話が生まれる。インシデント対応の最新ベストプラクティス2026で書いたポストモーテムと組み合わせて使うと、再発防止の議論がしやすくなる。


運用して2年でわかった、SLO 設計の現実的なコツ

SLO の数は「5個以内」に絞る

最初の失敗で書いたとおり、20個の SLO は死ぬ。今は CUJ ベースで5個以内に絞っている。迷ったら「この SLO が赤くなったとき、具体的に誰が何をするか言えるか?」を問いにする。言えないなら削除候補。これ、シンプルだけど地味に効く基準だと思う。

エラーバジェットポリシーを先に決める

「バジェットが 50% 消化したら機能開発を止めて信頼性改善に集中する」というポリシーを、マネージャーと事前合意しておく。これがないと、バジェット枯渇しても「今スプリント中だから…」で誰も動かない。

うちのチームのポリシーはこんな感じ。

消化率アクション
50% 未満通常開発継続
50〜75%信頼性改善タスクを次スプリントに入れる
75〜100%機能開発と信頼性改善を 50:50 にする
100% 超過機能リリースを一時停止、信頼性改善に全集中

これを JIRA のダッシュボードウィジェットに組み込んで、毎朝スタンドアップで全員が見られるようにしている。

SLO はサービスオーナーと一緒に決める

これが一番大事かもしれない。SRE チームだけで SLO を決めると「押しつけ感」が出て誰も守らなくなる。ワークショップ形式で「ユーザーが怒るのはどんな状況?」から逆算して、サービスオーナーと一緒に数値を決める。最初は合意形成が面倒だけど、後の運用が全然違う。

皆さんのチームでは、SLO の設定はどうやって合意取ってますか? うちはまだ完璧ではなくて、四半期ごとの SLO レビューで「この数値おかしくない?」という議論が毎回出る。正直もっとうまい方法がある気がしていて、まだ模索中。

SLO と SLA の使い分けを明確に

外部顧客向けの SLA(例:月次 99.9% の可用性を契約で保証)と、内部の SLO(例:99.95% を目標として運用する)を分けて管理する。SLO は SLA より高い目標にして、SLA 違反の前にアクションできる余裕を作る。

指標用途
SLA(顧客契約)99.9%外部契約の保証ライン
SLO(内部目標)99.95%バッファ 0.05% を確保
SLI(実測値)リアルタイム計測実際の観測値

このバッファがないと、SLO 違反 = SLA 違反になってしまって心臓に悪い。CloudWatch vs Datadog 2026でも比較したけど、SLO トラッキングの機能は Datadog の方が圧倒的に充実していて、コストと相談しながら使い分けるのが現実解だと思う。


SLO 達成率とビジネス指標の相関を取る

2026年のトレンドとして「SLO とビジネス KPI を紐付ける」という動きが強くなっている。SLO を達成できているとチャーン率が下がる、という相関を示せると、経営層への説得力が全然違う。

xychart-beta
    title "SLO 達成率 vs チャーン率 の相関(四半期別)"
    x-axis ["Q1 2025", "Q2 2025", "Q3 2025", "Q4 2025", "Q1 2026"]
    y-axis "SLO 達成率 (%)" 95 --> 100
    line [96.2, 98.1, 99.3, 97.8, 99.5]

このデータはあくまで例だけど、うちのプロダクトでも似たような相関が取れてきた。「信頼性への投資はビジネス価値がある」という議論を数字でできるようになると、SRE チームのプレゼンスが上がる。

ただ相関と因果は別なので、過信は禁物。SLO 改善しか要因がないわけじゃないし、「SLO 達成率を上げれば必ずチャーン減る」と言い切るのは危険。あくまで傾向として持っておく程度が現実的だと思う。


まとめ

SLI/SLO 設計で2年間失敗して学んだことをまとめる。

  • CUJ から逆算して SLI を定義する。技術指標ではなくユーザー体験を守るための数値として設計する。SLO は5個以内に絞ること
  • コンポジット SLO で全体像を把握する。個別サービスの SLO が全部グリーンでも、直列依存では実効可用性が落ちる。計算して見せると認識が変わる
  • Sloth + Prometheus で SLO を自動計算する。手動管理は死ぬ。ツールで自動化して、人間はポリシーと会話に集中する
  • エラーバジェットポリシーを事前に合意する。数値だけ決めても動かない。「バジェット X% 消化したら何をするか」まで決めてから運用開始する
  • SLO はサービスオーナーと一緒に決める。SRE が一方的に設定した SLO は形骸化する。ワークショップで合意形成することが長期的に効く

次のアクション: まずサービスのクリティカルユーザージャーニーを3個書き出してみてほしい。それだけで SLI 設計の議論が始められる。完璧な設計を最初から目指すより「動かしながら改善する」姿勢の方が、SLO は長続きする。

カオスエンジニアリング2026と組み合わせると、SLO がどれだけ実際のカオス試験に耐えられるかを検証できるので、次のステップとして試してみる価値があると思う。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事