SLO、SLI 与错误预算:数据驱动的可靠性工程

系统性 SRE 可靠性工程指南:SLI(Service Level Indicator)选取方法论、SLO(Service Level Objective)目标设定、错误预算定义与消费、Burn Rate 告警策略、多窗口多阈值告警(Multi-window Multi-burn-rate Alerting)、服务水平协议(SLA)与 SLO 的区别、基于错误预算的发布策略(发布窗口/金丝雀阈值)、SLO 监控面板设计、SLO 文化与团队治理。附 Google SRE 示例与 Prometheus/Grafana 实战配置。

「如果没有量化的可靠性目标,你就无法判断一次发布是『太激进』还是『太保守』。」 — Google SRE Book

SLO(Service Level Objective)是 SRE 文化的基石。它将模糊的"系统要稳定"转化为精确的"99.9% 的请求必须在 200ms 内返回",进而推导出错误预算——告诉你这个月还能承受多少停机时间。


一、核心概念

1.1 术语金字塔

                    ┌─────────┐
                    │   SLA   │  ← 面向客户的合同承诺(法律约束)
                    │  99.95% │     违约有赔偿
                    └────┬────┘
                         │ 基于
                    ┌────▼────┐
                    │   SLO   │  ← 内部目标(略高于 SLA)
                    │  99.98% │     团队努力的方向
                    └────┬────┘
                         │ 基于
                    ┌────▼────┐
                    │   SLI   │  ← 量化指标(原始数据)
                    │  可用性 │     可测量的东西
                    └─────────┘

关键区别:
  SLA = 对客户的承诺(做到 99.9%)
  SLO = 内部目标(努力做到 99.95%)
  SLI = 测量指标(实际测量值)

错误预算 = 1 - SLO
  例:SLO = 99.9% → 错误预算 = 0.1% = 43.8 分钟/月

1.2 SLI 选取方法论

SLI 选取原则:
1. 用户关心什么?不是"CPU 使用率",而是"页面加载速度"
2. 可测量?有数据源能持续采集
3. 可控制?团队能影响这个指标

SLI 常见类型:
├── Availability(可用性)— 请求是否成功?
├── Latency(延迟)      — 多快返回?
├── Quality(质量)      — 返回的内容是否正确?
├── Error Rate(错误率) — 多少比例失败?
└── Throughput(吞吐)   — 处理多少请求?

反模式:
  ❌ CPU 使用率作为 SLI(用户不关心)
  ❌ 测试覆盖率作为 SLI(不是用户体验)
  ❌ 部署频率作为 SLI(是过程指标,非结果指标)

1.3 SLI 定义示例

服务SLI 类型定义测量方式
Web API可用性成功 HTTP 请求比例1 - (5xx / 总请求)
Web API延迟P99 响应时间histogram_quantile(0.99)
搜索质量搜索结果相关性得分人工标注 + ML 模型
视频流质量播放过程中不卡顿比例前端事件上报
消息队列延迟消息从发布到消费的 P99消息时间戳差值

二、SLO 目标设定

2.1 目标值选择

SLO 不是越高越好:
├── 99.9%  = 43.8 分钟/月 停机预算
├── 99.95% = 21.9 分钟/月
├── 99.99% = 4.38 分钟/月  ← 成本剧增
└── 99.999% = 26.3 秒/月   ← 多数公司不需要

成本分析:
  从 99.9% → 99.99%,可靠性提升 10 倍
  但成本可能提升 50-100 倍
  → 除非业务需要,否则不要过度工程化

设定原则:
  - 从当前水平出发,设定有挑战但可达的目标
  - 比 SLA 稍高(留出缓冲)
  - 少而精:每个服务 2-3 个核心 SLO

2.2 SLO 模板

# slo.yml
service: payment-api
version: 1.0

slos:
  - name: availability
    display_name: "Payment API Availability"
    description: "Successful requests ratio"
    sli:
      type: availability
      expression: |
        sum(rate(http_requests_total{job="payment-api",status!~"5.."}[window])) /
        sum(rate(http_requests_total{job="payment-api"}[window]))
    target: 0.9995  # 99.95%
    window: 30d
    alerting:
      burn_rate_alerts:
        fast_burn: { multiplier: 14, window: 1h }
        slow_burn: { multiplier: 2, window: 6h }

  - name: latency
    display_name: "Payment API Latency"
    description: "P99 latency for successful requests"
    sli:
      type: latency
      expression: |
        histogram_quantile(0.99,
          sum(rate(http_request_duration_seconds_bucket{job="payment-api"}[window]))
        )
    target: 0.2  # 200ms
    window: 30d

三、错误预算

3.1 计算

错误预算 = 允许的不可用/不满足请求数

Example:
  SLO: 99.9% 可用性
  月度请求总数: 1,000,000,000
  错误预算 = 1,000,000,000 × 0.001 = 1,000,000 请求

时间等价:
  30天 = 30 × 24 × 60 = 43,200 分钟
  99.9% → 错误预算 = 43.2 分钟/月
  99.99% → 错误预算 = 4.32 分钟/月

3.2 错误预算消费监控

# 30 天错误预算消费率
(
  sum(rate(http_requests_total{status=~"5.."}[30d])) /
  sum(rate(http_requests_total[30d]))
) / (1 - 0.999)  # 除以错误预算比例

# 结果解读:
# < 1    → 预算健康,有余量
# = 1    → 刚好用完预算
# > 1    → 预算已超支,停止非必要变更

四、Burn Rate 告警

4.1 核心思想

Burn Rate = 当前错误率 / SLO 错误预算比率

例:
  SLO = 99.9% → 错误预算比率 = 0.1%
  当前错误率 = 1%
  Burn Rate = 1% / 0.1% = 10x

含义:以当前速度,10 天内就会耗尽月度错误预算。

Google SRE 推荐双窗口多 Burn Rate:
├── 快速燃尽(Fast Burn):Burn Rate ≥ 14.4x
│   └── 在 2 天内耗尽预算 → 立即通知 P0
│   └── 检测窗口:1h(长窗口)+ 5min(短窗口)
│
└── 慢速燃尽(Slow Burn):Burn Rate ≥ 2x
    └── 在 15 天内耗尽预算 → 通知 P1
    └── 检测窗口:6h(长窗口)+ 30min(短窗口)

4.2 Prometheus Burn Rate 告警

# burn_rate_alerts.yml
groups:
  - name: burn_rate_fast
    interval: 30s
    rules:
      # 快速燃尽:1h 窗口内 Burn Rate >= 14.4
      - alert: ErrorBudgetBurnFast
        expr: |
          (
            sum(rate(http_requests_total{status=~"5.."}[1h]))
            /
            sum(rate(http_requests_total[1h]))
          ) / (1 - 0.999) > 14.4
          and
          (
            sum(rate(http_requests_total{status=~"5.."}[5m]))
            /
            sum(rate(http_requests_total[5m]))
          ) / (1 - 0.999) > 14.4
        for: 2m
        labels:
          severity: p0
        annotations:
          summary: "Fast error budget burn detected"
          description: "Error rate is {{ $value | humanizePercentage }}x over budget"

  - name: burn_rate_slow
    interval: 30s
    rules:
      # 慢速燃尽:6h 窗口内 Burn Rate >= 2
      - alert: ErrorBudgetBurnSlow
        expr: |
          (
            sum(rate(http_requests_total{status=~"5.."}[6h]))
            /
            sum(rate(http_requests_total[6h]))
          ) / (1 - 0.999) > 2
          and
          (
            sum(rate(http_requests_total{status=~"5.."}[30m]))
            /
            sum(rate(http_requests_total[30m]))
          ) / (1 - 0.999) > 2
        for: 1h
        labels:
          severity: p1
        annotations:
          summary: "Slow error budget burn detected"

五、基于错误预算的发布策略

5.1 发布窗口

错误预算充足 (> 50%):
  ✅ 正常发布
  ✅ 可以尝试新技术
  ✅ 可以关闭部分冗余监控节省成本

错误预算紧张 (10-50%):
  ⚠️ 增加 Canary 比例和观察时间
  ⚠️ 非紧急功能延后
  ⚠️ 增加发布审批流程

错误预算耗尽 (< 10%):
  ❌ 禁止非必要发布
  ❌ 只接受 P0 修复
  ❌ 需要 VP 审批才能发布
  ❌ 团队 focus 转移到稳定性修复

5.2 发布看板

月度错误预算看板:

Payment Service
  SLO: 99.9% | 预算: 43.2 分钟
  ┌──────────────────────────────┐
  │ ████████████████████░░░░░░░░ │  已用: 68% (29.4 分钟)
  │ 绿色=健康  黄色=谨慎  红色=停止发布  │
  └──────────────────────────────┘
  
  本月剩余预算: 13.8 分钟
  当前状态: 🟡 谨慎发布

Search Service
  SLO: 99.95% | 预算: 21.9 分钟
  ┌──────────────────────────────┐
  │ █████░░░░░░░░░░░░░░░░░░░░░░░ │  已用: 15% (3.3 分钟)
  └──────────────────────────────┘
  
  本月剩余预算: 18.6 分钟
  当前状态: 🟢 正常发布

六、SLO 面板设计

// Grafana Dashboard JSON(关键面板)
{
  "title": "SLO Status",
  "panels": [
    {
      "title": "Availability (30d)",
      "type": "stat",
      "targets": [{
        "expr": |
          sum(rate(http_requests_total{job=~"$service",status!~"5.."}[30d])) /
          sum(rate(http_requests_total{job=~"$service"}[30d]))
      }],
      "fieldConfig": {
        "defaults": {
          "unit": "percentunit",
          "thresholds": {
            "steps": [
              { "color": "red", "value": 0 },
              { "color": "yellow", "value": 0.999 },
              { "color": "green", "value": 0.9995 }
            ]
          }
        }
      }
    },
    {
      "title": "Error Budget Remaining",
      "type": "bargauge",
      "targets": [{
        "expr": |
          1 - (
            sum(rate(http_requests_total{job=~"$service",status=~"5.."}[30d])) /
            sum(rate(http_requests_total{job=~"$service"}[30d]))
          ) / (1 - 0.999)
      }],
      "fieldConfig": {
        "defaults": {
          "unit": "percentunit",
          "min": 0,
          "max": 1
        }
      }
    },
    {
      "title": "Burn Rate",
      "type": "timeseries",
      "targets": [
        {
          "expr": |
            (sum(rate(http_requests_total{job=~"$service",status=~"5.."}[1h])) /
             sum(rate(http_requests_total{job=~"$service"}[1h]))) / (1 - 0.999),
          "legendFormat": "1h burn rate"
        }
      ]
    }
  ]
}

七、SLO 文化与治理

7.1 实施路线图

Phase 1: 试点(1-2 个月)
  ├── 选 1-2 个核心服务
  ├── 定义 SLI + SLO
  ├── 建立错误预算看板
  └── 团队培训

Phase 2: 推广(3-6 个月)
  ├── 扩展到所有关键服务
  ├── 建立 Burn Rate 告警
  ├── 错误预算纳入发布流程
  └── 定义发布冻结策略

Phase 3: 成熟(6+ 个月)
  ├── SLO 纳入团队 OKR
  ├── 自动化错误预算管理
  ├── 与财务挂钩(过度可靠性成本)
  └── 年度 SLO 评审与调整

7.2 常见陷阱

陷阱表现解决
SLO 太多每个服务 10 个 SLO限制 2-3 个核心
SLO 太松100% 时间达标有挑战性的目标
SLO 不可测没有数据源确保可自动化测量
忽视用户体验后端 99.99% 但前端很卡SLI 从用户视角定义
从不调整设定后一年不变季度评审

参考与延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「infra」更多文章

  1. 可观测性数据存储选型:TSDB、列式存储、对象存储与成本优化
  2. 云原生 APM 与性能剖析:Continuous Profiling 与火焰图
  3. Kubernetes 可观测性实战:集群、Pod、网络、存储全链路监控