1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SLO/SLI設計とエラーバジェット運用をPrometheusで実践する

1
Last updated at Posted at 2026-03-03

SLO/SLI設計とエラーバジェット運用をPrometheusで実践する

この記事でわかること

  • SLI/SLOの設計手順と、ユーザー体験から逆算するSLI選択の考え方
  • エラーバジェットの計算方法と、チーム間で合意するポリシー策定の進め方
  • Prometheusを使ったMulti-Window Multi-Burn-Rate Alertsの実装方法
  • Sloth・Pyrra・OpenSLOといったSLOツールの特徴と選定基準
  • エラーバジェット枯渇時のリリース凍結・解除フローの設計パターン

対象読者

  • 想定読者: SREやDevOpsに関心のあるバックエンドエンジニア・インフラエンジニア
  • 必要な前提知識:
    • Linux/コンテナ環境での基本的なサーバー運用経験
    • Prometheus/Grafanaの基本操作(PromQLの基礎)
    • KubernetesまたはDockerの基本概念
    • HTTPステータスコードやレイテンシの基礎知識

結論・成果

SLO/SLI設計とエラーバジェット運用を適切に導入することで、開発速度と信頼性のバランスを定量的に管理できるようになります。Google SRE Workbookで推奨されているMulti-Window Multi-Burn-Rate Alertsは、従来の閾値ベースアラートと比較して検知精度(precision)とリセット時間の両方を改善し、アラートファティーグを大幅に軽減できることが示されています(Google SRE Workbook - Alerting on SLOs)。また、国内事例では、エラーバジェットベースのアラート導入によりMTTR(平均復旧時間)60%短縮を達成した報告もあります(SLO/SLI 2025 - 信頼性目標駆動の運用)。

この記事では、SLI/SLOの基本概念からPrometheusでの実装、エラーバジェットの組織的運用まで、段階的に解説していきます。

SLI/SLOの基本概念を理解する

SLI/SLOはGoogle発祥のSRE(Site Reliability Engineering)プラクティスの中核をなす概念です。まずは用語を整理しましょう。

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

用語 正式名称 定義
SLI Service Level Indicator サービス品質の定量的指標 リクエスト成功率、P99レイテンシ
SLO Service Level Objective SLIに対する目標値 成功率 99.9%、P99 < 300ms
SLA Service Level Agreement SLO未達時の契約上の補償 稼働率99.95%未満で返金
エラーバジェット Error Budget SLOの余裕分(= 1 - SLO) 0.1%(月間約43分のダウン許容)

ここで重要なのは、SLIは技術メトリクス、SLOは目標値、エラーバジェットはリスク許容量という関係です。SLAはビジネス契約であり、通常SLOよりも緩く設定します。

SLIの種類と選び方

Google SRE Workbookでは、システムの種類に応じて適切なSLIを選択することが推奨されています(Implementing SLOs)。

リクエスト駆動型サービス(API、Webアプリケーション):

SLIカテゴリ 測定内容 PromQL例
Availability 成功リクエストの割合 sum(rate(http_requests_total{code!~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
Latency 閾値内に完了したリクエストの割合 histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
Quality 劣化なしレスポンスの割合 フォールバック応答でない割合

パイプライン型システム(バッチ処理、ETL):

SLIカテゴリ 測定内容
Freshness データが許容時間内に更新された割合 最終更新から5分以内のレコード率
Correctness 正しい出力を生成した割合 処理結果の整合性チェック
Coverage 処理対象データのうち実際に処理された割合 全レコード中の処理成功率

なぜSLI選択が重要か:

SLI選択を誤ると、「システムは正常だがユーザーは困っている」または「システム異常だがユーザーには影響がない」という乖離が生じます。たとえばCPU使用率やメモリ使用率は内部メトリクスであり、SLIとして適切ではありません。SLIはユーザーが直接体験する品質を測定すべきです(SLI、SLO、エラーバジェット導入の前に知っておきたいこと)。

注意: 最初のSLI/SLOは完璧でなくてよいです。Google SRE Workbookでも「Your first attempt at an SLI and SLO doesn't have to be correct; the most important goal is to get something in place and measured」と明記されています。まずは始めて、運用しながら改善しましょう。

CUJ(クリティカルユーザージャーニー)からSLIを導出する

SLI設計の出発点はCUJ(Critical User Journey)の特定です。これは、ユーザーがサービスで達成したい主要な行動シナリオを指します。

たとえばECサイトの場合を考えてみましょう。

CUJから導出したSLIとSLOの例:

CUJ SLI SLO目標
商品検索 検索APIの成功率 99.9%(30日間)
商品検索 検索APIのP99レイテンシ 300ms以下
購入完了 決済APIの成功率 99.95%(30日間)
購入完了 決済APIのP99レイテンシ 1000ms以下

ハマりポイント: CUJを細かくしすぎると管理が複雑になります。最初は3〜5個のCUJに絞り、対応するSLIも各CUJあたり1〜2個に抑えましょう。Google SRE Workbookでも段階的に拡張することが推奨されています。

SLOとエラーバジェットを設計する

SLIを決定したら、次にSLO目標値を設定し、エラーバジェットを算出します。

SLO目標値の設定方法

初期SLO設定のアプローチは過去のパフォーマンスデータに基づくのが定石です(Implementing SLOs)。

ステップ1: 現状の計測

# SLO設定の前に、過去4週間のデータを確認する例(Python + Prometheus API)
# prometheus_client は pip install prometheus-api-client でインストール
import requests
from datetime import datetime, timedelta

PROMETHEUS_URL = "http://prometheus:9090"
QUERY = 'sum(rate(http_requests_total{code!~"5.."}[28d])) / sum(rate(http_requests_total[28d]))'

response = requests.get(
    f"{PROMETHEUS_URL}/api/v1/query",
    params={"query": QUERY},
    timeout=30,
)
result = response.json()

if result["status"] == "success" and result["data"]["result"]:
    current_availability = float(result["data"]["result"][0]["value"][1])
    # 例: 0.99723 → 99.723%
    print(f"過去28日間の可用性: {current_availability * 100:.3f}%")

    # ステップ2: 有効数字2桁に切り捨て → 初期SLO候補
    # 99.723% → 99.7% を初期SLOとして提案
    proposed_slo = round(current_availability * 1000) / 1000
    print(f"初期SLO候補: {proposed_slo * 100:.1f}%")

ステップ2: エラーバジェットの算出

SLO目標値が決まれば、エラーバジェットは自動的に算出されます。

SLO目標 エラーバジェット 30日間の許容ダウンタイム 30日間の許容エラー数(100万リクエスト/日)
99% 1% 7時間12分 300,000件
99.5% 0.5% 3時間36分 150,000件
99.9% 0.1% 43分12秒 30,000件
99.95% 0.05% 21分36秒 15,000件
99.99% 0.01% 4分19秒 3,000件

よくある間違い: 最初から99.99%のような厳しいSLOを設定してしまうケースです。エラーバジェットが月間4分19秒しかないため、定期メンテナンスですら予算を消費してしまいます。Google SRE Workbookでは、まず現状のパフォーマンスから切り捨てた値を初期SLOとし、段階的に引き上げることを推奨しています。

エラーバジェットポリシーを策定する

エラーバジェットは「算出して終わり」ではなく、枯渇時のアクションを事前にポリシーとして定めることが最重要です(Error Budget Policy)。

典型的なエラーバジェットポリシーは以下の4段階で構成されます:

バジェット残量 ステータス アクション
50%以上 Green 通常の開発速度で機能リリース
20〜50% Yellow デプロイ頻度を下げ、追加レビューを要求
1〜20% Orange 非クリティカルな機能デプロイを凍結
0%(枯渇) Red 全機能デプロイ凍結、信頼性回復に全力

ポリシー策定で合意すべき3者:

Google SRE Workbookでは、エラーバジェットポリシーの策定にあたり、以下の3者の合意が必須とされています:

  1. プロダクトマネージャー: SLOがユーザー体験として許容可能か確認
  2. 開発チーム: バジェット枯渇時のリリース凍結に同意
  3. SRE/運用チーム: SLOが過度な負荷なく防衛可能か確認

制約条件: エラーバジェットポリシーは事前合意が鍵です。バジェット枯渇後に「凍結するかどうか」を議論し始めると、チーム間の対立が生じやすくなります。HRMOSの事例でも、「エラーバジェット運用を開始する前に各関係者の合意を取り、ルール化する」ことの重要性が報告されています(円滑なエラーバジェット運用に向けた取り組み)。

凍結時に許容するリリースの定義

バジェット枯渇による凍結中でも、以下のリリースは許容するのが一般的です(円滑なエラーバジェット運用に向けた取り組み):

  • 本番障害への緊急対応(hotfix)
  • 信頼性回復のための改修(SLO違反の根本原因修正)
  • 対外的に時期を約束しているリリース(事前にステークホルダーが合意済み)
  • セキュリティパッチ

凍結解除の基準も事前に定めておきましょう。単にSLOが回復しただけでなく、ポストモーテムのアクションアイテム完了を解除条件に含めるアプローチもあります。

PrometheusでMulti-Window Multi-Burn-Rate Alertsを実装する

SLI/SLOを設計したら、次はアラートの実装です。Google SRE Workbookで推奨されているMulti-Window Multi-Burn-Rate Alertsは、SLOベースアラートの業界標準となっています(Alerting on SLOs)。

バーンレートとは

バーンレート(Burn Rate)は、エラーバジェットの消費速度を表す指標です。

  • バーンレート 1x: 30日間でちょうどバジェットを使い切る速度
  • バーンレート 10x: 30日分のバジェットを3日で使い切る速度
  • バーンレート 14.4x: 30日分のバジェットを約2日で使い切る速度(=2%を1時間で消費)

従来の「エラー率 > X%」という閾値アラートでは、短時間のスパイクで過剰にアラートが発生するか、緩やかな劣化を見逃すかのトレードオフがありました。バーンレートアラートは「バジェット消費の速度」で判断するため、このトレードオフを解消できます。

Multi-Window Multi-Burn-Rate Alertsの構成

Google SRE Workbookの推奨構成は以下の3レベルです:

重要度 ロングウィンドウ ショートウィンドウ バーンレート 消費バジェット
Page(即時対応) 1時間 5分 14.4x 2%
Page(即時対応) 6時間 30分 6x 5%
Ticket(営業時間内対応) 3日 6時間 1x 10%

なぜ2つのウィンドウを使うのか:

ロングウィンドウだけだと、問題が解消した後もアラートが残り続けます。ショートウィンドウを追加し、「両方の条件を同時に満たす場合のみ発火」とすることで、問題解消から約5分でアラートがリセットされます。

Prometheus Recording Rulesの実装

まず、各時間ウィンドウでのエラー率を事前計算するRecording Rulesを定義します。以下はGoogle公式サンプル(prometheus-slo-burn-example)を参考に、ECサイトの検索APIに適用した例です。

# prometheus/rules/slo_recording_rules.yml
# SLO: 検索API可用性 99.9%(30日間)
groups:
  - name: slo_search_api_error_rates
    # 各時間ウィンドウでのエラー率を事前計算
    rules:
      # 5分ウィンドウ(ショートウィンドウ: Page用)
      - record: job:slo_errors_per_request:ratio_rate5m
        expr: |
          sum by (job) (rate(http_requests_total{job="search-api", code=~"5.."}[5m]))
          /
          sum by (job) (rate(http_requests_total{job="search-api"}[5m]))

      # 30分ウィンドウ(ショートウィンドウ: Page用)
      - record: job:slo_errors_per_request:ratio_rate30m
        expr: |
          sum by (job) (rate(http_requests_total{job="search-api", code=~"5.."}[30m]))
          /
          sum by (job) (rate(http_requests_total{job="search-api"}[30m]))

      # 1時間ウィンドウ(ロングウィンドウ: Page用)
      - record: job:slo_errors_per_request:ratio_rate1h
        expr: |
          sum by (job) (rate(http_requests_total{job="search-api", code=~"5.."}[1h]))
          /
          sum by (job) (rate(http_requests_total{job="search-api"}[1h]))

      # 6時間ウィンドウ(ロング/ショートウィンドウ兼用)
      - record: job:slo_errors_per_request:ratio_rate6h
        expr: |
          sum by (job) (rate(http_requests_total{job="search-api", code=~"5.."}[6h]))
          /
          sum by (job) (rate(http_requests_total{job="search-api"}[6h]))

      # 3日ウィンドウ(ロングウィンドウ: Ticket用)
      - record: job:slo_errors_per_request:ratio_rate3d
        expr: |
          sum by (job) (rate(http_requests_total{job="search-api", code=~"5.."}[3d]))
          /
          sum by (job) (rate(http_requests_total{job="search-api"}[3d]))

      # 28日ウィンドウ(エラーバジェット残量計算用)
      - record: job:slo_errors_per_request:ratio_rate28d
        expr: |
          sum by (job) (rate(http_requests_total{job="search-api", code=~"5.."}[28d]))
          /
          sum by (job) (rate(http_requests_total{job="search-api"}[28d]))

  - name: slo_search_api_error_budget
    rules:
      # エラーバジェット残量(%)
      - record: job:error_budget:remaining
        expr: |
          1 - job:slo_errors_per_request:ratio_rate28d / (1 - 0.999)
        labels:
          slo: "search-api-availability"

Alerting Rulesの実装

Recording Rulesを使ったアラートルールを定義します。

# prometheus/rules/slo_alerting_rules.yml
# SLO: 99.9%(エラーバジェット = 0.001)
groups:
  - name: slo_search_api_alerts
    rules:
      # Page: バーンレート14.4x(1時間ウィンドウ + 5分ウィンドウ)
      # または バーンレート6x(6時間ウィンドウ + 30分ウィンドウ)
      - alert: SearchApiHighErrorBurnRate
        expr: |
          (
            job:slo_errors_per_request:ratio_rate1h{job="search-api"} > (14.4 * 0.001)
            and
            job:slo_errors_per_request:ratio_rate5m{job="search-api"} > (14.4 * 0.001)
          )
          or
          (
            job:slo_errors_per_request:ratio_rate6h{job="search-api"} > (6 * 0.001)
            and
            job:slo_errors_per_request:ratio_rate30m{job="search-api"} > (6 * 0.001)
          )
        labels:
          severity: page
          slo: "search-api-availability"
        annotations:
          summary: "検索APIのエラーバジェット消費速度が高い"
          description: >
            検索APIのエラー率がSLO 99.9%の許容範囲を超える速度で
            バジェットを消費しています。即時対応が必要です。
          runbook_url: "https://wiki.example.com/runbooks/search-api-slo"

      # Ticket: バーンレート1x(3日ウィンドウ + 6時間ウィンドウ)
      - alert: SearchApiSlowErrorBurnRate
        expr: |
          (
            job:slo_errors_per_request:ratio_rate3d{job="search-api"} > (1 * 0.001)
            and
            job:slo_errors_per_request:ratio_rate6h{job="search-api"} > (1 * 0.001)
          )
        labels:
          severity: ticket
          slo: "search-api-availability"
        annotations:
          summary: "検索APIのエラーバジェットが緩やかに消費されている"
          description: >
            検索APIのエラー率がSLO 99.9%のバジェットを緩やかに
            消費しています。営業時間内に調査してください。

なぜこの実装を選んだか:

  • Recording Rulesで事前計算: アラート評価時に毎回rate()を計算すると、Prometheusへの負荷が高くなります。Recording Rulesで事前計算しておくことで、アラート評価が軽量になります
  • 14.4 * 0.001の計算: 14.4はバーンレート、0.001はエラーバジェット(= 1 - 0.999)です。SLO目標を変更する場合はこの0.001を変更するだけで済みます

注意: このアプローチはリクエスト数が少ないサービスでは精度が低下します。1時間あたりのリクエスト数が1,000件未満の場合、統計的なノイズが大きくなるため、より長いウィンドウの使用やイベントベースのSLIへの切り替えを検討してください。

Grafanaダッシュボードでエラーバジェットを可視化する

アラートだけでなく、日常的にエラーバジェットの残量を可視化しておくことも重要です。以下はGrafanaダッシュボードで使用するPromQLクエリの例です。

# エラーバジェット残量(%表示)
(1 - job:slo_errors_per_request:ratio_rate28d{job="search-api"} / 0.001) * 100

# バーンレート(現在の消費速度)
job:slo_errors_per_request:ratio_rate1h{job="search-api"} / 0.001

# SLO達成状況(直近28日)
(1 - job:slo_errors_per_request:ratio_rate28d{job="search-api"}) * 100

SLOツールを活用して運用を自動化する

Recording RulesとAlerting Rulesを手動で書くのは、SLIの数が増えると管理が煩雑になります。ここでは、SLOの定義からPrometheusルールを自動生成するツールを紹介します。

主要SLOツールの比較

ツール 特徴 UI Kubernetes対応 開発状態
Sloth YAML定義からPrometheusルール自動生成 Grafanaダッシュボード CRD対応 メンテナンスモード
Pyrra Web UI付きのSLOオペレータ 内蔵Web UI CRD対応 アクティブ開発中
OpenSLO ベンダー中立のSLO定義仕様 なし(仕様のみ) 間接的 コミュニティ主導
Nobl9 SaaS型SLO管理プラットフォーム フルUI 間接的 商用
Datadog SLO Datadog統合SLO機能 Datadog UI 間接的 商用

SlothによるSLO定義の例

Slothを使うと、シンプルなYAML定義からMulti-Window Multi-Burn-Rate Alertsのルールが自動生成されます。

# slo/search-api.yml
version: "prometheus/v1"
service: "search-api"
labels:
  team: "platform"
  environment: "production"
slos:
  - name: "search-api-availability"
    objective: 99.9  # SLO目標: 99.9%
    description: "検索APIの可用性SLO"
    sli:
      events:
        error_query: sum(rate(http_requests_total{job="search-api",code=~"5.."}[{{.window}}]))
        total_query: sum(rate(http_requests_total{job="search-api"}[{{.window}}]))
    alerting:
      name: SearchApiAvailability
      labels:
        team: "platform"
      annotations:
        runbook_url: "https://wiki.example.com/runbooks/search-api-slo"
      page_alert:
        labels:
          severity: page
      ticket_alert:
        labels:
          severity: ticket
# SlothでPrometheusルールを生成
# Slothのインストール: https://github.com/slok/sloth#install
sloth generate -i slo/search-api.yml -o prometheus/rules/generated_slo_rules.yml

# 生成されたルールをPrometheusに反映
# prometheus.ymlのrule_filesに追加後、リロード
curl -X POST http://prometheus:9090/-/reload

Slothが自動生成するルールには、先ほど手動で定義したRecording RulesとAlerting Rulesがすべて含まれます。SLIの数が増えても、YAML定義を追加するだけで対応できるため、運用負荷が大幅に下がります。

PyrraによるKubernetes CRDの例

Kubernetes環境であれば、PyrraのCRDとして定義するアプローチもあります。PyrraはWeb UIが内蔵されているため、Grafanaダッシュボードを別途構築する必要がありません。

# k8s/pyrra-slo.yml
apiVersion: pyrra.dev/v1alpha1
kind: ServiceLevelObjective
metadata:
  name: search-api-availability
  namespace: monitoring
  labels:
    team: platform
    pyrra.dev/team: platform
spec:
  target: "99.9"  # SLO目標
  window: 28d     # 評価ウィンドウ
  description: "検索APIの可用性SLO"
  indicator:
    ratio:
      errors:
        metric: http_requests_total{job="search-api",code=~"5.."}
      total:
        metric: http_requests_total{job="search-api"}
# PyrraをKubernetesにデプロイ(Helm chart使用)
helm repo add pyrra https://pyrra-dev.github.io/helm-charts
helm install pyrra pyrra/pyrra -n monitoring --create-namespace

# SLOリソースを適用
kubectl apply -f k8s/pyrra-slo.yml

# Pyrra Web UIにアクセス
kubectl port-forward -n monitoring svc/pyrra 9099:9099
# http://localhost:9099 でSLOダッシュボードを確認

OpenSLOによるベンダー中立な定義

特定のツールに依存しない形でSLOを定義したい場合、OpenSLO仕様が有用です。OpenSLOはCNCF(Cloud Native Computing Foundation)傘下で策定されたベンダー中立のYAML仕様で、GitOpsワークフローとの親和性が高いのが特徴です。

# openslo/search-api-slo.yml
apiVersion: openslo/v1
kind: SLO
metadata:
  name: search-api-availability
  displayName: 検索API可用性
spec:
  description: 検索APIの可用性目標
  service: search-api
  budgetingMethod: Occurrences
  objectives:
    - displayName: 可用性
      target: 0.999  # 99.9%
      ratioMetrics:
        good:
          source: prometheus
          queryType: promql
          query: sum(rate(http_requests_total{job="search-api",code!~"5.."}[{{.window}}]))
        total:
          source: prometheus
          queryType: promql
          query: sum(rate(http_requests_total{job="search-api"}[{{.window}}]))
  timeWindow:
    - duration: 28d
      isRolling: true

ツール選定のトレードオフ:

  • Sloth: シンプルで導入しやすいが、開発者がメンテナンスモードと宣言しており、新機能追加は期待できない
  • Pyrra: アクティブに開発されWeb UIもあるが、Kubernetes環境が前提
  • OpenSLO: ベンダーロックインを避けられるが、単体では機能せず別途ツールとの組み合わせが必要

よくある問題と解決方法

SLO/SLI運用で発生しがちな問題と対策をまとめます。

問題 原因 解決方法
アラートが多すぎて対応しきれない 閾値ベースアラートのまま Multi-Window Multi-Burn-Rate Alertsに移行
エラーバジェットがすぐ枯渇する SLO目標が厳しすぎる 過去データに基づいて現実的な目標に調整
チーム間でSLO解釈が異なる ポリシーが文書化されていない エラーバジェットポリシーを事前に策定・合意
SLOがビジネス指標と乖離 CPUなど内部メトリクスをSLIに使用 CUJからSLIを導出し直す
Recording Rulesの管理が煩雑 手動でルールを記述 Sloth/Pyrra等のSLOツールで自動生成
バジェット枯渇後に凍結判断が遅れる 凍結基準・解除基準が未定義 事前にポリシー文書で合意を取る
リクエスト数が少なくアラート精度が低い 統計的サンプル不足 ウィンドウを広げるか、イベントベースSLIに変更

まとめと次のステップ

まとめ:

  • SLI設計はCUJ(クリティカルユーザージャーニー) から逆算し、ユーザーが体験する品質を測定すべき
  • SLO目標は過去のパフォーマンスデータに基づいて設定し、段階的に引き上げる
  • エラーバジェットポリシーは事前に3者(PM・開発・SRE)の合意を得て文書化する
  • PrometheusアラートはMulti-Window Multi-Burn-Rate Alertsを採用し、アラートノイズを削減する
  • SLO定義が増えたらSloth/Pyrra/OpenSLO等のツールで自動化する

次にやるべきこと:

  1. 自分のサービスのCUJを3〜5個洗い出し、対応するSLIを定義する
  2. 過去4週間のデータから初期SLO目標値を設定する
  3. エラーバジェットポリシーのドラフトを作成し、ステークホルダーと合意する
  4. Prometheusに Recording Rules + Alerting Rulesを導入し、Grafanaで可視化する

参考


注意: この記事はAI(Claude Code)により自動生成されました。内容の正確性については複数の情報源で検証していますが、実際の利用時は公式ドキュメントもご確認ください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?