跳至主要內容

SLI_SLO_SLA:用数据衡量服务质量

郑天祺大约 10 分钟运维SLISLOSLA错误预算服务质量SRE

SLI/SLO/SLA:用数据衡量服务质量

前言

"系统稳定吗?"——这个问题太模糊了。SRE(Site Reliability Engineering)的核心方法论之一就是用 SLI、SLO、SLA 三个层次来精确衡量和管理服务质量。

本文带你理解这三个概念的关系、如何选择正确的 SLI、如何制定合理的 SLO,以及如何用"错误预算"来驱动技术决策。


第一部分:SLI、SLO、SLA 是什么

1.1 一层一层的关系

SLA (Service Level Agreement)   ← 对外承诺(合同级)
│  "99.9% 可用性,违约赔偿"
│
├── SLO (Service Level Objective) ← 内部目标(比 SLA 更严格)
│   │  "99.95% 可用性"(留出缓冲空间)
│   │
│   └── SLI (Service Level Indicator) ← 实际测量值
│        "过去 30 天:99.97% 可用性"
│
└── 关系:SLI 测量 → SLO 对比 → SLA 承诺

1.2 定义详解

术语定义举例谁关心
SLIService Level Indicator
服务水平的具体测量指标
"P99 请求延迟 = 200ms"
"错误率 = 0.05%"
工程师
SLOService Level Objective
服务水平的内部目标值
"P99 延迟 ≤ 500ms"
"可用性 ≥ 99.95%"
SRE/运维
SLAService Level Agreement
服务水平的对外承诺
(通常有违约条款)
"月可用性 ≥ 99.9%"
"低于此值按比例退款"
客户/业务

1.3 实际案例

案例:AWS S3 的 SLA

SLI:测量到的 S3 API 错误率
  "过去 1 小时,GetObject API 错误率 = 0.02%"

SLO:AWS 内部的 S3 可靠性目标
  "GetObject API 错误率 ≤ 0.01%"(内部不公开)

SLA:AWS 对客户的承诺
  "S3 月可用性 ≥ 99.9%,低于此值赔偿 10%~25% 月度费用"

SLO 比 SLA 更严格的原因:预留缓冲空间。如果 SLO 和 SLA 一样,一旦 SLI 略有波动就触发违约赔偿,没有容错余地。


第二部分:如何选择 SLI

2.1 SLI 的四大黄金指标

Google SRE 团队建议从四个维度选择 SLI:

┌──────────────────────────────────────────┐
│            四大黄金指标                    │
├────────────┬─────────────────────────────┤
│ 延迟       │ 请求花了多长时间?            │
│ (Latency)  │ P50, P95, P99 延迟          │
├────────────┼─────────────────────────────┤
│ 可用性     │ 服务是否可用?               │
│(Availability)│ 成功响应 / 总请求           │
├────────────┼─────────────────────────────┤
│ 错误率     │ 请求是否成功?               │
│ (Errors)   │ 5xx 错误 / 总请求            │
├────────────┼─────────────────────────────┤
│ 吞吐量     │ 系统能处理多少请求?          │
│(Throughput)│ QPS / TPS                   │
└────────────┴─────────────────────────────┘

2.2 按场景选择 SLI

用户面 SLI(用户体验相关):

请求成功率:GET /api/orders 的 2xx 比例
响应延迟:POST /api/guarantees 的 P95 延迟
页面加载时间:首页加载完成时间

系统面 SLI(内部运维相关):

数据库连接池可用率
消息队列消费延迟
缓存命中率
JVM GC 暂停时间
错误预算消耗率

2.3 担保系统的 SLI 定义

# guarantee-slis.yaml — 担保系统 SLI 定义

guarantee_core:
  # 担保申请 API
  guarantee_apply:
    availability:
      description: "担保申请 API 成功率"
      promql: |
        sum(rate(http_server_requests_seconds_count{
          uri="/api/guarantees/apply", status=~"2.."}[5m]))
        / sum(rate(http_server_requests_seconds_count{
          uri="/api/guarantees/apply"}[5m]))
    latency:
      description: "担保申请 P99 延迟"
      promql: |
        histogram_quantile(0.99,
          sum(rate(http_server_requests_seconds_bucket{
            uri="/api/guarantees/apply"}[5m])) by (le))

  # 风控评估 API
  risk_evaluate:
    availability:
      promql: |
        sum(rate(http_server_requests_seconds_count{
          uri="/api/risk/evaluate", status=~"2.."}[5m]))
        / sum(rate(http_server_requests_seconds_count{
          uri="/api/risk/evaluate"}[5m]))

  # 签约 API
  contract_sign:
    availability:
      promql: |
        sum(rate(http_server_requests_seconds_count{
          uri="/api/contracts/sign", status=~"2.."}[5m]))
        / sum(rate(http_server_requests_seconds_count{
          uri="/api/contracts/sign"}[5m]))

# 批处理
batch_jobs:
  daily_reconciliation:
    success_rate:
      promql: |
        sum(rate(batch_job_success_total{job="daily_reconciliation"}[24h]))
        / sum(rate(batch_job_total{job="daily_reconciliation"}[24h]))

# 基础设施
infrastructure:
  database:
    availability:
      promql: probe_success{job="mysql-probe"}
  message_queue:
    consumer_lag:
      promql: rocketmq_consumer_offset_diff{group="guarantee-group"}

2.4 SLI 的度量窗口

短窗口(1-5 分钟)→ 告警触发
  "过去 5 分钟,错误率 = 8% → 触发 P0 告警"

中窗口(1 小时)→ 值班响应
  "过去 1 小时,P99 延迟 = 800ms → SLO 有风险"

长窗口(30 天)→ SLO 达标判定
  "过去 30 天,可用性 = 99.92% → 未达到 99.95% 的 SLO"

第三部分:SLO 制定方法论

3.1 SLO 不是 100%

很多团队的第一反应是设 SLO = 99.99%这是错误的

为什么不能设 100%

  1. 成本指数增长:从 99.9% → 99.99%,成本可能增加 10 倍
  2. 创新被扼杀:零错误预算意味着不能做任何变更
  3. 用户感知不到:用户端的可靠性受限于网络、设备等因素
可用性          年故障时间        用户可感知?
─────────────────────────────────────────────
99%            3.65 天          很明显
99.9%          8.76 小时        比较明显
99.95%         4.38 小时        偶尔感觉
99.99%         52.6 分钟        几乎无感
99.999%        5.26 分钟        完全无感

3.2 SLO 制定步骤

Step 1: 理解用户期望

不要猜用户需要什么,去测量:

// 前端埋点:测量用户实际的延迟体验
const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
        if (entry.initiatorType === 'fetch') {
            metrics.record('api.latency', {
                url: entry.name,
                duration: entry.duration,
                userTier: getUserTier()
            });
        }
    }
});
observer.observe({ entryTypes: ['resource'] });

// 如果 95% 用户的 API 延迟 < 500ms
// 那可以设 SLO: P95 延迟 ≤ 500ms

Step 2: 参考行业标准

服务类型典型 SLO
核心交易系统99.99% 可用,P99 ≤ 100ms
互联网 ToC 服务99.95% 可用,P99 ≤ 500ms
内部管理后台99.9% 可用,P95 ≤ 2s
批处理/报表99.5% 可用,无延迟要求

Step 3: 测试并迭代

Week 1-4:测量基线 SLI
  → 实际可用性 = 99.97%

Week 5:设定初始 SLO = 99.95%
  → 有 0.02% 的缓冲空间

Week 6-12:观察 SLO 达标情况
  → 如果轻松达标 → SLO 可以收紧
  → 如果勉强达标 → 先提升系统可靠性再调
  → 如果经常不达标 → SLO 太紧或系统太不稳定

3.3 担保系统 SLO 定义

# guarantee-slos.yaml

services:
  # 担保核心服务
  guarantee-service:
    slos:
      - name: "availability"
        objective: 99.95              # 月可用性 ≥ 99.95%
        measurement_period: 30d
        sli: sum(rate(http_requests_total{status=~"2..|3.."}[30d]))
             / sum(rate(http_requests_total[30d]))

      - name: "latency_p99"
        objective: 1s                 # P99 延迟 ≤ 1 秒
        measurement_period: 30d
        sli: histogram_quantile(0.99,
             rate(http_request_duration_seconds_bucket[30d]))

  # 风控引擎(对延迟敏感)
  risk-service:
    slos:
      - name: "availability"
        objective: 99.99
      - name: "latency_p99"
        objective: 200ms             # 风控评估必须快

  # 通知服务(对延迟不敏感)
  notification-service:
    slos:
      - name: "availability"
        objective: 99.9
      - name: "latency_p95"
        objective: 5s                # 短信晚 5 秒到也无所谓

  # 日终对账(批处理)
  reconciliation:
    slos:
      - name: "daily_completion_rate"
        objective: 99.5              # 对账成功率 ≥ 99.5%
      - name: "completion_time"
        objective: before 6:00 AM    # 必须在早上 6 点前完成

第四部分:错误预算

4.1 什么是错误预算

错误预算 = 1 - SLO

SLO = 99.95% 可用
→ 错误预算 = 0.05%
→ 每月可以"坏"的时间 = 30×24×60 × 0.05% = 21.6 分钟

错误预算不是"允许犯错",而是"允许创新"

错误预算的用途:
  ✅ 发布新版本
  ✅ 做 A/B 测试
  ✅ 基础设施变更
  ✅ 数据库迁移
  ❌ 修复已知 BUG(这不是"花"预算,这是"赚"预算)

4.2 错误预算决策框架

/**
 * 错误预算管理器
 */
@Service
public class ErrorBudgetManager {

    private static final double SLO = 0.9995;  // 99.95%

    public ErrorBudgetStatus getStatus() {
        double currentSLI = prometheusClient.query(
            "sum(rate(http_requests_total{status='2..|3..'}[30d]))"
            + " / sum(rate(http_requests_total[30d]))");

        double errorBudgetRemaining = currentSLI - SLO;

        return ErrorBudgetStatus.builder()
            .slo(SLO)
            .currentSli(currentSLI)
            .errorBudgetRemaining(errorBudgetRemaining)
            .status(determineStatus(errorBudgetRemaining))
            .build();
    }

    private BudgetStatus determineStatus(double remaining) {
        if (remaining < 0) return BudgetStatus.EXHAUSTED;    // 预算耗尽
        if (remaining < 0.01) return BudgetStatus.CRITICAL;  // 预算不足
        if (remaining < 0.05) return BudgetStatus.WARNING;   // 预算紧张
        return BudgetStatus.HEALTHY;                          // 预算充足
    }
}

基于错误预算的发布决策

错误预算剩余    → 可以做什么?
─────────────────────────────────────────────
> 50% (充足)    → 正常发布,无限制
                 → 允许基础设施变更

20-50% (正常)   → 正常发布
                 → 审慎的基础设施变更

5-20% (紧张)    → 只做关键修复和低风险发布
                 → 冻结基础设施变更
                 → 团队聚焦可靠性改进

< 5% (危急)     → 冻结所有发布
                 → 全员修复可靠性问题
                 → 需要 VP 审批才能变更

< 0% (耗尽)     → 违反了 SLO
                 → 冻结所有非紧急变更
                 → 事后复盘

4.3 Grafana 错误预算面板

{
  "panels": [
    {
      "title": "错误预算剩余",
      "type": "gauge",
      "targets": [
        {
          "expr": "(0.9995 - (sum(rate(http_requests_total{status!~'5..'}[30d])) / sum(rate(http_requests_total[30d])))) / 0.0005 * 100",
          "legendFormat": "错误预算剩余 %"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "thresholds": {
            "steps": [
              { "value": 0, "color": "red" },
              { "value": 20, "color": "yellow" },
              { "value": 50, "color": "green" }
            ]
          }
        }
      }
    },
    {
      "title": "错误预算消耗趋势",
      "type": "graph",
      "targets": [
        {
          "expr": "0.0005 - (1 - sum(rate(http_requests_total{status!~'5..'}[30d])) / sum(rate(http_requests_total[30d])))",
          "legendFormat": "剩余错误预算"
        }
      ]
    }
  ]
}

第五部分:实战——为担保系统定义完整的 SLI/SLO

5.1 系统全景的 SLI/SLO 矩阵

服务                 SLI 指标              SLO              测量期
──────────────────────────────────────────────────────────────
担保申请 API        成功率                 ≥ 99.95%          30 天
                    P99 延迟              ≤ 1s              30 天
──────────────────────────────────────────────────────────────
风控评估引擎        成功率                 ≥ 99.99%          30 天
                    P99 延迟              ≤ 200ms           30 天
                    P99.9 延迟            ≤ 500ms           30 天
──────────────────────────────────────────────────────────────
电子签约 API        成功率                 ≥ 99.9%           30 天
                    P95 延迟              ≤ 3s              30 天
──────────────────────────────────────────────────────────────
额度服务            成功率                 ≥ 99.95%          30 天
                    P99 查询延迟          ≤ 500ms           30 天
──────────────────────────────────────────────────────────────
消息队列            消息投递成功率         ≥ 99.99%          30 天
                    消息消费延迟           ≤ 1s              1 小时
──────────────────────────────────────────────────────────────
数据库              连接可用率             ≥ 99.99%          30 天
                    慢查询比例             ≤ 1%              1 小时
──────────────────────────────────────────────────────────────
日终对账            对账完成率             ≥ 99.5%           每日
                    完成时间              早于 6:00 AM       每日

5.2 SLI 的 Prometheus 记录规则

# prometheus-rules.yml
groups:
  - name: sli-recording-rules
    interval: 30s
    rules:
      # 担保申请成功率(30 天窗口)
      - record: guarantee:apply:success_rate_30d
        expr: |
          sum(rate(http_server_requests_seconds_count{
            uri="/api/guarantees/apply", status=~"2.."}[30d]))
          / sum(rate(http_server_requests_seconds_count{
            uri="/api/guarantees/apply"}[30d]))

      # 风控评估 P99 延迟(30 天窗口)
      - record: risk:evaluate:latency_p99_30d
        expr: |
          histogram_quantile(0.99,
            sum(rate(http_server_requests_seconds_bucket{
              uri="/api/risk/evaluate"}[30d])) by (le))

      # 全局可用性
      - record: global:availability_30d
        expr: |
          sum(rate(http_server_requests_seconds_count{
            status=~"2..|3.."}[30d]))
          / sum(rate(http_server_requests_seconds_count[30d]))

      # 错误预算剩余(担保服务)
      - record: guarantee:error_budget_remaining_30d
        expr: |
          (0.9995 - (
            sum(rate(http_server_requests_seconds_count{
              service="guarantee-service", status=~"2..|3.."}[30d]))
            / sum(rate(http_server_requests_seconds_count{
              service="guarantee-service"}[30d]))
          )) / 0.0005

5.3 SLO 达标告警

groups:
  - name: slo-alerts
    rules:
      # 错误预算消耗过快(24 小时内消耗 > 20% 的月预算)
      - alert: ErrorBudgetBurnRateHigh
        expr: |
          (
            (1 - sum(rate(http_server_requests_seconds_count{
              status!~"5.."}[1h]))
            / sum(rate(http_server_requests_seconds_count[1h])))
            / (1 - 0.9995)
          ) > 14.4
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "错误预算消耗速度过快"
          description: |
            过去 1 小时消耗了 14.4 倍正常速率的错误预算。
            按此速度,错误预算将在 2.5 天内耗尽。

      # 错误预算即将耗尽(30 天内剩余 < 10%)
      - alert: ErrorBudgetNearlyExhausted
        expr: guarantee:error_budget_remaining_30d < 0.10
        for: 1h
        labels:
          severity: critical
        annotations:
          summary: "担保服务错误预算即将耗尽"
          description: "错误预算剩余: {{ $value | humanizePercentage }}"

      # P99 延迟超标
      - alert: P99LatencyAboveSLO
        expr: risk:evaluate:latency_p99_30d > 0.2
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "风控服务 P99 延迟超过 200ms SLO"

第六部分:SLO 文化落地

6.1 月度 SLO 评审

月度 SLO 评审会议(1 小时):

1. 回顾上月 SLO 达标情况 (10 分钟)
   - 哪些达标?哪些未达标?
   - 错误预算消耗了多少?

2. 未达标原因分析 (20 分钟)
   - 根因分析(不是指责人)
   - 改进方案

3. 错误预算使用决策 (15 分钟)
   - 下月是否有重大发布需要消耗预算?
   - 是否需要暂停发布来积累预算?

4. SLO 调整讨论 (15 分钟)
   - 当前 SLO 是否合理?
   - 业务变化是否需要调整?

6.2 错误预算的"金主"原则

谁使用错误预算?        → 产品经理和业务方
谁管理错误预算?        → SRE 团队
什么时候可以花预算?     → 产品经理有"花钱"的决定权
什么时候不能花?        → 预算耗尽时,SRE 有否决权

错误预算 = 可靠性领域的"预算制"管理
就像财务预算一样,花完了就没了,要等下个月

6.3 SLI/SLO 成熟度模型

Level 1: "我们有关键指标监控"
  有 Prometheus/Grafana,但 SLO 不正式

Level 2: "我们有 SLO 定义"
  关键服务有 SLO,定期评审

Level 3: "我们用错误预算驱动决策"
  发布/回滚决策基于错误预算
  超标时自动冻结

Level 4: "SLO 是团队文化"
  每个团队都有 SLO 看板
  On-call 据此决策
  SLO 持续优化

Level 5: "SLO 驱动业务决策"
  产品规划考虑 SLO 影响
  新功能有 SLO 要求
  合同 SLA 基于内部 SLO

总结

核心公式

SLI ≤ SLO ≤ SLA

SLI:实际测量("P99 延迟 = 120ms")
SLO:内部目标("P99 延迟 ≤ 200ms")
SLA:对外承诺("P99 延迟 ≤ 500ms")

错误预算 = 1 - SLO
错误预算消耗率 > 警戒线 → 冻结变更
错误预算剩余 ≈ 0 → Stop Everything, Fix Reliability

行动清单

步骤时间产出
1. 定义核心 SLI第 1 周5-10 个 SLI 指标
2. 测量基线第 2-4 周SLI 历史数据
3. 设定初始 SLO第 5 周SLO 文档
4. 配置监控告警第 6 周Grafana 面板 + 告警
5. 引入错误预算第 7 周错误预算看板
6. 月度评审持续SLO 评审报告

不要把 SLO 设为 100%。给系统留出错的空间,就是给创新留出空间。

上次编辑于:
贡献者: zhengtianqi