概述

凌晨三点,你被告警吵醒。爬起来一看:CPU 正常、内存正常、Pod 也在跑,但用户投诉说"系统卡得用不了"。你打开 Grafana 面板,画了一堆曲线,却说不清楚——这到底算不算故障?

这个场景,我在多个企业客户的 SRE 咨询中反复遇到。核心问题不是监控不够多,而是度量体系设计错了方向:我们一直在监控"系统内部的指标",而不是"用户感受到的服务质量"。

曾经在一次某出行平台的故障复盘中,团队发现一个讽刺的事实:告警系统在故障发生 12 分钟后才触发,而用户在故障发生后 30 秒就已经开始投诉了。原因很简单——告警基于 CPU 使用率超 80% 触发,但实际故障是数据库连接池耗尽,CPU 才 35%,根本没碰阈值。

可靠性度量的本质,是把"系统稳不稳"这个问题,从主观感觉变成可量化的数字。这篇文章不讲 SLI/SLO 的基础概念(这些在 相关文章:SRE核心理念:SLI、SLO与错误预算 中已经讲透了),而是聚焦于:如何从零设计一套完整、可落地、能驱动决策的可靠性度量体系

为什么传统监控体系不能回答"系统稳不稳"

先说结论:传统监控体系回答不了"系统稳不稳",因为它度量的对象错了。

传统监控的三个盲区

盲区一:监控的是资源,不是用户体验

大多数团队的监控面板长这样:CPU 折线图、内存折线图、磁盘 IO 折线图、网络流量折线图。这些指标反映的是"服务器忙不忙",而不是"用户爽不爽"。

举个真实的例子:某电商平台的订单服务,CPU 使用率常年 70%,运维觉得"不太对劲",计划扩容。但实际上 P99 延迟 120ms,请求成功率 99.97%,用户体验完全没问题。另一个缓存服务,CPU 才 20%,看起来"很健康",但缓存命中率从 95% 掉到 60%,导致数据库被打爆,用户实际体验已经很差了。

盲区二:没有"好"和"坏"的量化边界

“CPU 80% 算好还是算坏?"——这个问题在传统监控体系里没有答案。80% 可能是正常的批处理负载,也可能是即将打满的前兆。没有 SLO 的情况下,80% 就只是一个数字,不是判断依据。

Google SRE Book 里有一段话说得很到位:“如果你不能度量它,你就不能管理它。” 但更准确的说法应该是:如果你度量错了对象,比不度量更危险——因为你会基于错误的信号做决策。

盲区三:告警基于静态阈值,不感知业务影响

传统告警的逻辑是"指标超过阈值就报”。问题是,同样的阈值在不同业务场景下含义完全不同:

场景CPU 80% 含义是否需要告警
凌晨 2 点批处理任务正常负载不需要
大促期间核心交易服务即将打满紧急告警
测试环境压测中预期行为不需要
生产环境空闲时段异常进程需要排查

静态阈值无法区分这些场景,导致的结果就是:要么告警太多(告警疲劳),要么告警太晚(用户已经投诉了才报)。

四层可靠性度量模型

我用这套模型在多个企业落地过,核心思路是:从用户视角出发,层层下钻到基础设施,每一层都有明确的度量目标和告警规则

┌─────────────────────────────────────────────┐
│  第四层:错误预算治理(决策层)              │
│  "还能不能发版?" "要不要暂停迭代?"         │
├─────────────────────────────────────────────┤
│  第三层:SLO 目标设定(治理层)              │
│  "可用性 ≥ 99.9%" "P99 延迟 ≤ 300ms"        │
├─────────────────────────────────────────────┤
│  第二层:SLI 指标采集(度量层)              │
│  "请求成功率" "延迟分布" "吞吐量"            │
├─────────────────────────────────────────────┤
│  第一层:用户旅程拆解(基础层)              │
│  "用户做了什么?" "关键路径是什么?"         │
└─────────────────────────────────────────────┘

第一层:用户旅程拆解

很多人跳过这一步直接选 SLI,结果选了一堆跟用户感知无关的指标。用户旅程拆解是整个体系的地基——**先搞清楚"用户在乎什么",再决定"度量什么"。

拆解方法很简单:画出用户使用你系统的关键路径,标注每一步的"成功"和"失败"标准。

以一个电商系统为例:

用户旅程步骤成功标准失败标准关键依赖
打开首页页面 2s 内加载完成白屏 / 加载超 5sCDN、前端服务
搜索商品搜索结果 1s 内返回搜索超时 / 结果为空搜索引擎、商品服务
加入购物车操作 500ms 内完成接口报错 / 数据不一致购物车服务、Redis
提交订单订单创建 2s 内成功下单失败 / 重复下单订单服务、库存服务
完成支付支付 3s 内完成支付超时 / 支付失败支付网关、风控服务

关键原则:每个服务都做这个拆解,但只对核心路径定义 SLO。不是所有服务都需要 SLO——内部工具服务用"尽力而为"模式就够了,强行加 SLO 只会增加运维负担。

我在某新能源物流平台的实践中,把 120+ 微服务分成三档:

  • S0(核心路径,8 个服务):SLO 99.95%,多窗口燃烧速率告警,On-Call 响应 < 5min
  • S1(重要支撑,30 个服务):SLO 99.9%,阈值告警 + 燃烧速率告警
  • S2(内部工具,其余):尽力而为,基础监控即可

这个分级让团队把精力集中在真正影响业务的 8 个服务上,告警量直接降了 60%。

第二层:SLI 指标采集

SLI(Service Level Indicator)是度量服务质量的具体指标。选 SLI 时最容易犯的错:把基础设施指标当 SLI。

好的 SLI 长这样

  • HTTP 请求成功率:非 5xx 请求 / 总请求
  • API 响应 P99 延迟
  • 消息处理成功率
  • 订单创建成功率

不适合当 SLI 的指标

  • CPU 使用率(用户不在乎你的 CPU 多少)
  • 内存使用率(同上)
  • 磁盘 IO(同上)
  • 数据库连接池大小(这是诊断指标,不是体验指标)

这些诊断指标不是没用——它们在排障时极其重要,但不应该作为判断"系统稳不稳"的依据。详见 相关文章:告警策略设计:从噪声到信号 中对诊断指标和体验指标的区分。

SLI 的数量控制:每个服务不超过 4-5 个 SLI。超过这个数量,监控和告警配置会指数级复杂化,团队根本维护不过来。

SLI 的 PromQL 实现

# 可用性 SLI:请求成功率(排除 5xx)
sum(rate(http_requests_total{status!~"5.."}[5m])) 
  / 
sum(rate(http_requests_total[5m]))

# 延迟 SLI:P99 响应时间
histogram_quantile(0.99, 
  sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)

# 吞吐量 SLI:每秒请求数
sum(rate(http_requests_total[5m]))

这里有个踩坑点:延迟 SLI 必须用百分位数(P95/P99),不能用平均值。平均值会掩盖长尾延迟——1000 个请求平均 50ms,可能其中 10 个请求是 5 秒,那 10 个用户体验极差但被平均值稀释了。

我见过一个团队用平均延迟做 SLI,结果大促时 P99 飙到 3 秒但平均值才 200ms,告警完全没触发,用户投诉炸了才发现问题。从此以后我定了一条铁律:延迟 SLI 只用百分位数,禁用平均值

第三层:SLO 目标设定

SLO(Service Level Objective)是对 SLI 设定的期望值。比如"可用性 ≥ 99.9%(30天)“或"P99 延迟 ≤ 300ms”。

SLO 数值怎么定

这是被问最多的问题。我的方法分三步:

第一步:拉历史数据

拉过去 90 天的 SLI 实际表现数据,看历史波动范围和故障频率。

# 过去 90 天的可用性
1 - (
  sum(rate(http_requests_total{status=~"5.."}[90d])) 
    / 
  sum(rate(http_requests_total[90d]))
)

第二步:设定略高于历史水平的目标

如果历史可用性是 99.89%,SLO 不要直接定 99.99%(团队达不到会摆烂),也不要定 99.8%(没有挑战性)。推荐定在 99.9%-99.95% 之间——稍微努力就能达到,但需要持续改进才能稳定保持

第三步:跟产品方确认

SLO 不是运维单方面拍脑袋定的。拉一个 30 分钟的会议,运维、开发、产品三方坐下来确认:

  • 产品方说清楚"用户体验下降到什么程度会影响业务"
  • 运维方说清楚"以现有架构,这个目标可不可达"
  • 开发方说清楚"要达到这个目标需要哪些技术改进"

SLO 的分层设计

单一维度的 SLO 不够用。推荐用"多维 SLO"方式:

SLI 维度S0 核心服务S1 重要服务S2 内部工具
可用性≥ 99.95%≥ 99.9%尽力而为
P99 延迟≤ 200ms≤ 500ms≤ 1s
P99.9 延迟≤ 1s≤ 2s不设
数据持久性零丢失零丢失尽力而为

关键认知:SLO 不是越高越好。99.999% 的 SLO 意味着每年只允许 5.26 分钟不可用——这在大多数业务场景下是过度设计,维护成本远超收益。我在咨询中见过一个团队把内部工具的 SLO 定到 99.99%,结果每周花 10 小时维护一个没几个用户用的服务,典型的"为了 SLO 而 SLO"。

关于可用性数字的实际含义,这里给一个直观的对照:

SLO 目标允许年停机时间允许月停机时间适用场景
99%3.65 天7.3 小时内部工具
99.9%8.76 小时43.8 分钟一般业务
99.95%4.38 小时21.9 分钟核心业务
99.99%52.6 分钟4.38 分钟支付/交易
99.999%5.26 分钟26.3 秒生命攸关系统

第四层:错误预算治理

这一层是整个体系的决策引擎。错误预算把"系统稳不稳"转化成了"还能不能发版"这种业务能理解的决策。

错误预算 = 1 - SLO。如果 SLO 是 99.9%,错误预算就是 0.1%。在 30 天周期内,0.1% 的不可用时间 = 43.2 分钟。

错误预算的核心价值不是"告诉你还剩多少分钟",而是建立一套基于预算消耗的决策机制

预算消耗状态决策动作例子
剩余 > 50%正常迭代,允许发布新功能日常开发节奏
剩余 25%-50%谨慎发布,新功能需要额外审批大促前两周
剩余 < 25%冻结非紧急发布,投入稳定性改进预算快耗尽
预算耗尽全员投入稳定性修复,禁止任何非紧急变更SLO 已破线

这套机制的价值在于:把"要不要发版"从主观判断变成了数据驱动决策。产品经理想催你发版?看错误预算——预算充足就发,预算紧张就不发,不需要吵架。

我在某电商平台推动这套机制时,最大的阻力来自开发团队——“为什么之前的指标都正常,现在不让我们发版了?“解释的关键是:错误预算不是限制发布,而是让发布决策有据可依。预算充足时大胆发,预算紧张时保守发。一个季度后,团队发现发布频率不仅没降,反而因为"预算充足时放心发"而提高了 30%。

错误预算的消耗策略和行动纲领,在 相关文章:错误预算的消耗策略与行动纲领 里有更详细的讨论。

多窗口燃烧速率告警:从原理到实现

传统的阈值告警(“CPU > 80% 就报”)有两个致命问题:一是阈值不好定,二是无法区分"慢慢恶化"和"突然故障”。

Google SRE 提出的多窗口多燃烧速率(Multi-Window Multi-Burn-Rate)告警解决了这个问题。核心思想:用两个不同时间窗口的燃烧速率组合判断,既能快速发现突发故障,又能捕捉缓慢恶化。

燃烧速率是什么

燃烧速率(Burn Rate)= 实际错误率 / 允许错误率。

假设 SLO 是 99.9%(允许 0.1% 错误率):

  • 如果当前错误率是 0.1%,燃烧速率 = 0.1% / 0.1% = 1(正常消耗速度)
  • 如果当前错误率是 1%,燃烧速率 = 1% / 0.1% = 10(10 倍速消耗)
  • 如果当前错误率是 10%,燃烧速率 = 10% / 0.1% = 100(100 倍速消耗)

燃烧速率为 1 意味着在 SLO 周期结束前刚好消耗完预算。燃烧速率为 10 意味着 1/10 的时间就会消耗完——30 天的预算 3 天就烧完了。

多窗口组合的妙处

单一窗口无法区分两种情况:

  • 突发故障:5 分钟内错误率飙升 → 需要立即告警
  • 缓慢恶化:6 小时内错误率持续偏高 → 需要关注但不紧急

多窗口方案用"短窗口 × 长窗口"组合判断:

告警级别短窗口长窗口燃烧速率阈值含义
Page(紧急)5 分钟1 小时14.42 天内预算耗尽
Ticket(工单)30 分钟6 小时65 天内预算耗尽
Ticket(工单)2 小时1 天310 天内预算耗尽

短窗口确认"问题正在发生”,长窗口确认"不是偶发抖动"。两个窗口同时超阈值才触发告警——这就是多窗口的精髓:用短窗口保速度,用长窗口保精度

为什么是 14.4 而不是整数?因为 30 天的 SLO 周期,14.4 倍速消耗 = 30 / (14.4 × 2) ≈ 1 天。这个数字来自 Google SRE Workbook 的推导:在 2% 的预算消耗时就要触发 Page 级告警。具体推导过程可以参考 Google SRE Workbook 第 5 章。

Prometheus 实现完整配置

下面是一个可直接使用的 Prometheus 告警规则,基于多窗口多燃烧速率方案:

# slo-burn-rate-alerts.yaml
# SLO: 99.9% 可用性,30天周期
# 错误预算: 0.1% × 30天 = 43.2分钟

groups:
  - name: slo-burn-rate
    interval: 30s
    rules:
      # ============ Page 级告警 ============
      # 短窗口 5min + 长窗口 1h,燃烧速率 14.4
      # 触发条件:5分钟和1小时的错误率都超过 1.44%(14.4 × 0.1%)
      # 含义:按这个速度,2天内30天预算就会耗尽
      - alert: SLOBurnRateCritical
        expr: |
          (
            sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
              / 
            sum(rate(http_requests_total[5m])) by (service)
          ) > (14.4 * 0.001)
          and
          (
            sum(rate(http_requests_total{status=~"5.."}[1h])) by (service)
              / 
            sum(rate(http_requests_total[1h])) by (service)
          ) > (14.4 * 0.001)          
        for: 2m
        labels:
          severity: page
          slo_severity: critical
        annotations:
          summary: "{{ $labels.service }} SLO 燃烧速率超临界值"
          description: "{{ $labels.service }} 5分钟和1小时窗口的燃烧速率均超过 14.4,按此速度2天内错误预算将耗尽"

      # ============ Ticket 级告警 ============
      # 短窗口 30min + 长窗口 6h,燃烧速率 6
      # 触发条件:30分钟和6小时的错误率都超过 0.6%
      # 含义:按这个速度,5天内预算会耗尽
      - alert: SLOBurnRateWarning
        expr: |
          (
            sum(rate(http_requests_total{status=~"5.."}[30m])) by (service)
              / 
            sum(rate(http_requests_total[30m])) by (service)
          ) > (6 * 0.001)
          and
          (
            sum(rate(http_requests_total{status=~"5.."}[6h])) by (service)
              / 
            sum(rate(http_requests_total[6h])) by (service)
          ) > (6 * 0.001)          
        for: 5m
        labels:
          severity: ticket
          slo_severity: warning
        annotations:
          summary: "{{ $labels.service }} SLO 燃烧速率偏高"
          description: "{{ $labels.service }} 30分钟和6小时窗口的燃烧速率均超过 6,按此速度5天内错误预算将耗尽"

      # ============ 延迟 SLO 燃烧速率 ============
      # SLO: P99 延迟 ≤ 300ms
      # 超过 300ms 的请求比例作为"错误率"
      - alert: SLOLatencyBurnRateCritical
        expr: |
          (
            sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (service)
              / 
            sum(rate(http_request_duration_seconds_count[5m])) by (service)
          ) < (1 - 14.4 * 0.001)
          and
          (
            sum(rate(http_request_duration_seconds_bucket{le="0.3"}[1h])) by (service)
              / 
            sum(rate(http_request_duration_seconds_count[1h])) by (service)
          ) < (1 - 14.4 * 0.001)          
        for: 2m
        labels:
          severity: page
          slo_type: latency
        annotations:
          summary: "{{ $labels.service }} 延迟 SLO 燃烧速率超临界值"
          description: "P99 延迟超过 300ms 的比例持续偏高,按此速度2天内延迟 SLO 预算将耗尽"

一个真实踩坑:燃烧速率告警的误报问题

在某出行项目落地燃烧速率告警的第一周,团队收到 23 条 Page 级告警,其中 18 条是误报。排查后发现两个原因:

原因一:低流量服务的统计噪声

一个日均 1000 请求的内部服务,5 分钟窗口只有 3-4 个请求。一个 5xx 就让错误率飙到 25%,燃烧速率 250,触发告警。但实际影响几乎为零。

修复方案:对低流量服务加最小请求数门槛:

# 在告警条件中增加最小请求数过滤
- alert: SLOBurnRateCriticalWithTraffic
  expr: |
    (
      sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
        / 
      sum(rate(http_requests_total[5m])) by (service)
    ) > (14.4 * 0.001)
    and
    (
      sum(rate(http_requests_total{status=~"5.."}[1h])) by (service)
        / 
      sum(rate(http_requests_total[1h])) by (service)
    ) > (14.4 * 0.001)
    # 新增:5分钟内至少 100 个请求才告警
    and
    sum(rate(http_requests_total[5m])) by (service) > 100    
  for: 2m
  labels:
    severity: page

原因二:计划内维护触发告警

每周三凌晨的数据库维护导致 2 分钟的连接超时,错误率短暂飙升,触发 Page 级告警。值班人员每次都被吵醒,开始对告警产生免疫——这是最危险的信号。

修复方案:在维护窗口期间配置静默规则,同时在 SLO 计算中排除计划内维护时间:

# Alertmanager 静默规则(维护窗口)
# 每周三 02:00-02:30 静默
- name: maintenance-silence
  matchers:
    - name: service
      value: "doctor-gateway"
    - name: maintenance_window
      value: "weekly-db"
  startsAt: "2026-07-30T02:00:00+08:00"
  endsAt: "2026-07-30T02:30:00+08:00"

这两个修复让误报从 18 条/周降到 2 条/周,团队重新开始信任告警系统。

可靠性度量体系的落地路径

不要试图一步到位。我在多个企业落地的经验是:分三个阶段,每个阶段 4-6 周。

阶段一:可观测(第 1-4 周)

目标:让 SLI 可见,但不急着定 SLO。

  • 选 3-5 个核心服务,部署 HTTP 指标采集(请求成功率、延迟分布)
  • 在 Grafana 上建一个"SLI 概览"面板,只看这几个指标
  • 不要配置任何告警,先观察数据 2 周
  • 重点确认:指标是否准确反映用户体验

这个阶段最大的价值是发现"监控盲区"——你会发现很多服务连基础的 HTTP 指标都没采集,或者延迟直方图配置不合理(桶太少导致 P99 计算不准)。

阶段二:可度量(第 5-8 周)

目标:定义 SLO 并开始度量

  • 基于阶段一的数据,设定初始 SLO(略低于历史最佳表现)
  • 实现错误预算计算和可视化
  • 配置燃烧速率告警,但先只开 Ticket 级(不开 Page)
  • 每周 review SLO 达成情况

关键动作:把错误预算仪表盘放到团队都能看到的地方。我在一个团队的做法是:在办公区放一个大屏,实时显示各服务的错误预算消耗条。这个"可视化压力"比任何 KPI 都管用——开发团队会主动关注自己服务的预算消耗。

阶段三:可决策(第 9-12 周)

目标:用错误预算驱动发布决策

  • 开启 Page 级燃烧速率告警
  • 建立错误预算决策流程(预算 < 25% 时冻结非紧急发布)
  • 跟产品方确认 SLO 调整机制(季度 review)
  • 开始做故障复盘时关联错误预算消耗

到这个阶段,可靠性度量体系才算真正"活"起来——不是一堆面板和告警,而是一套驱动团队行为的决策机制

度量体系的常见陷阱

陷阱一:SLO 过多

不是所有服务都需要 SLO。我曾见过一个团队给 80 个服务都定了 SLO,结果维护 SLO 配置本身就成了全职工作。

建议:S0 服务 5-8 个,S1 服务 20-30 个,其余用"尽力而为"。优先把核心服务做到极致,而不是把所有服务都做到"差不多"。

陷阱二:SLO 永不调整

SLO 不是刻在石头上的。业务在变、架构在变、用户期望也在变。一个合理的 SLO review 节奏是:

  • 季度 review:评估 SLO 是否仍然合理,是否需要调整
  • 年度 review:重新审视整个度量体系,是否需要增减 SLI 维度

调整 SLO 时有一个原则:只往上调,不往下调——除非有充分理由(架构降级、业务转型)。往下调 SLO 等于降低标准,会让团队形成"达不到就降标准"的习惯。

陷阱三:只看 SLO 达标率,不看预算消耗趋势

“SLO 达标了"不等于"系统很健康”。一个服务 SLO 是 99.9%,本月可用性 99.92%——达标了。但如果上月是 99.98%,说明趋势在恶化,下个月可能就不达标了。

建议:在仪表盘上加一个"预算消耗趋势"面板,看的是斜率而不是绝对值:

# 过去 7 天的错误预算消耗速率(每日)
avg_over_time(
  (1 - (
    sum(rate(http_requests_total{status!~"5.."}[1d])) 
      / 
    sum(rate(http_requests_total[1d]))
  ))[7d:1d]
)

如果消耗速率在加速,即使当前 SLO 还达标,也应该提前介入。

陷阱四:把 SLO 当 KPI

这是最危险的一个。一旦 SLO 跟绩效挂钩,团队的行为就会变形:

  • SLO 定得保守(定 99.8% 而不是 99.9%,因为更容易达标)
  • 故障不上报(报了会消耗预算,影响"达标率")
  • 新功能不敢发(发了可能出问题,影响达标)

正确做法:SLO 是工程治理工具,不是绩效考核工具。SLO 的目的是帮助团队做更好的决策,而不是惩罚达不到目标的人。Google SRE Book 里说得很清楚:“SLO 不达标时,正确的反应是投入资源改进系统,而不是追责。”

工具链推荐

层次工具用途推荐理由
SLI 采集Prometheus指标采集与存储社区活跃,PromQL 灵活
SLO 计算Sloth自动生成 SLO 告警规则原生支持多窗口多燃烧速率
SLO 可视化PyrraSLO 仪表盘与告警管理UI 直观,支持错误预算可视化
全局看板Grafana统一面板支持 Prometheus 数据源
日志关联Loki日志查询与 Grafana 无缝集成
链路追踪Jaeger请求级追踪排障时关联 SLI 异常

Sloth 和 Pyrra 的区别:Sloth 是"配置即代码"模式,用 YAML 定义 SLO,自动生成 Prometheus 告警规则;Pyrra 是"可视化界面"模式,有 Web UI 管理 SLO。两者可以配合使用——Sloth 做 CI/CD 管理,Pyrra 做日常可视化。

如果你不想引入额外工具,也可以手写 PromQL 实现。上面的告警规则就是纯 Prometheus 原生配置,不依赖 Sloth。但手动维护的缺点是:SLO 数量超过 10 个时,规则文件会变得很长很难维护。

Grafana 错误预算仪表盘搭建

度量体系要发挥价值,必须让数据"被看见"。一个设计良好的仪表盘,比 10 封告警邮件更有用。

仪表盘的四块核心面板

我推荐的错误预算仪表盘布局,分四个区域:

面板一:SLO 达成总览

用 Stat 面板展示每个服务的 SLO 达成状态。绿色 = 达标,黄色 = 接近底线(预算消耗 > 75%),红色 = 已破线。一眼看出哪些服务需要关注。

# 当前周期(30天)的 SLO 达成率
1 - (
  sum(rate(http_requests_total{status=~"5..", service="$service"}[30d]))
    /
  sum(rate(http_requests_total{service="$service"}[30d]))
)

面板二:错误预算消耗条

用 Gauge 面板展示每个服务的错误预算剩余比例。仪表盘的视觉冲击力在于:当指针从绿色区域滑向红色区域时,团队能直观感受到"预算在燃烧"。

# 错误预算剩余比例(30天周期)
1 - (
  (
    sum(rate(http_requests_total{status=~"5..", service="$service"}[30d]))
      /
    sum(rate(http_requests_total{service="$service"}[30d]))
  ) / (1 - 0.999)  # 1 - SLO
)

这个查询的逻辑:当前错误率 / 允许错误率 = 预算消耗比例。1 减去消耗比例就是剩余比例。当结果为 0 时,预算耗尽;为负数时,SLO 已破线。

面板三:燃烧速率实时曲线

用 Time Series 面板展示燃烧速率随时间变化。这条曲线的价值在于看趋势——平稳的曲线代表系统健康,突然飙升代表刚发生了故障。

# 实时燃烧速率(5分钟窗口)
(
  sum(rate(http_requests_total{status=~"5..", service="$service"}[5m]))
    /
  sum(rate(http_requests_total{service="$service"}[5m]))
) / 0.001  # 除以允许错误率

在 Grafana 中给这条曲线加两条阈值线:14.4(红色,Page 级)和 6(黄色,Ticket 级),团队一眼就能看出当前处于什么状态。

面板四:每日错误预算消耗明细

用 Bar Chart 面板展示过去 30 天每天的预算消耗量。这个面板的价值是发现规律——如果每周三消耗都偏高,说明周三是某个定期任务执行的日期,需要针对性优化。

# 每日错误预算消耗
sum by (day) (
  increase(http_requests_total{status=~"5..", service="$service"}[1d])
    /
  increase(http_requests_total{service="$service"}[1d])
) / 0.001

变量配置

为了让仪表盘支持多服务切换,配置一个 service 变量:

{
  "name": "service",
  "type": "query",
  "datasource": "Prometheus",
  "query": "label_values(http_requests_total, service)",
  "refresh": 2,
  "includeAll": true,
  "multi": true
}

这个配置会自动从 Prometheus 的 service 标签中拉取所有服务名,下拉选择切换。refresh: 2 表示每 2 分钟刷新一次标签列表,新服务上线后自动出现在下拉框中。

一个仪表盘踩坑:时区导致的预算计算偏差

有一次团队的 SLO 仪表盘显示"预算已耗尽",但实际系统运行正常。排查发现是 Grafana 时区配置和 Prometheus 查询窗口不对齐导致的。

Prometheus 的 rate() 函数基于 UTC 时间计算,而 Grafana 面板默认显示 UTC 时间。如果 SLO 周期是"北京时间每月 1 号 00:00 到月底 23:59",但 Prometheus 按 UTC 算就是"上月最后一天 16:00 到本月最后一天 16:00"——差了 8 小时的数据窗口。

修复方案:在 Prometheus 查询中统一使用 UTC 偏移量,同时在 Grafana 面板设置中把时区改为 Asia/Shanghai,并确保查询的时间范围与 SLO 周期一致。这个问题看起来小,但在 SLO 跟团队绩效相关时会引发严重争议——团队会质疑"数据准不准"。

架构权衡:自建 SLO 平台 vs 开源工具

在落地 SLO 体系时,一个常见的架构决策是:用开源工具(Sloth/Pyrra)还是自建 SLO 管理平台。我在不同规模团队都实践过,结论是——看团队规模和 SLO 数量

开源工具方案(Sloth + Pyrra)

适合 SLO 数量 < 30、团队 < 20 人的场景。

优势

  • 上手快,1-2 天就能跑起来
  • Sloth 用 YAML 定义 SLO,版本可控,适合 GitOps
  • Pyrra 提供 Web UI,开发团队能自助查看 SLO 状态
  • 社区活跃,Google SRE 方法论的原生实现

劣势

  • SLI 类型有限(主要支持 HTTP/gRPC 请求成功率和延迟)
  • 自定义 SLI 需要写原生 PromQL,Pyrra 不支持展示
  • 多团队/多租户场景下,权限管理薄弱
  • 错误预算的计算周期固定(7天/30天),不支持自定义周期

自建 SLO 平台方案

适合 SLO 数量 > 30、多团队协作、需要自定义 SLI 类型的场景。

优势

  • SLI 类型完全自定义(支持消息队列、批处理任务、数据库等非 HTTP 场景)
  • 错误预算计算周期灵活(支持 7天/30天/90天,甚至按业务周期如"大促周期")
  • 权限管理完善(不同团队只能看自己的服务)
  • 可以跟内部工单系统、发布系统集成

劣势

  • 开发成本高(至少 1 个 SRE 全职 2-3 个月)
  • 维护成本持续(Prometheus 版本升级可能影响 PromQL 兼容性)
  • 需要团队有 Prometheus 深度使用经验

我的建议

团队规模SLO 数量推荐方案理由
< 10 人< 10手写 PromQL最简单,无额外依赖
10-50 人10-30Sloth + Pyrra开箱即用,维护成本低
50-200 人30-100Sloth + 自建看板Sloth 管规则,自建看板做可视化
> 200 人> 100全自建平台需要多租户、自定义 SLI、流程集成

一个中间路线:如果团队在 30-50 人,SLO 数量在 20-40 之间,推荐 Sloth + Grafana 自建看板的组合。Sloth 负责自动生成 Prometheus 告警规则(省去手写 PromQL 的工作),Grafana 负责可视化(比 Pyrra 的 UI 更灵活,可以嵌入到现有监控面板中)。这个组合的维护成本最低,同时满足中等规模团队的需求。

复杂依赖链下的 SLO 传递

实际系统中,一个服务的可用性依赖底层基础设施和上游服务。如果数据库挂了导致 API 超时,算谁的 SLO 失败?这个问题在多团队协作时会引发大量扯皮。

依赖链 SLO 模型

核心思路:上游服务的 SLO 必须优于下游服务的 SLO。如果一个支付服务依赖数据库,支付服务的 SLO 是 99.95%,那数据库的 SLO 至少要 99.97%——因为数据库不可用会直接导致支付服务不可用。

用户请求 → API 网关 (99.95%) → 订单服务 (99.95%) → 数据库 (99.97%)
                              库存服务 (99.9%)

这个模型的数学基础是可用性乘法原则:链路整体可用性 = 各环节可用性的乘积。如果 4 个环节都是 99.9%,链路整体是 99.9%^4 ≈ 99.6%——比单个环节低很多。

故障归因:SLI 分层标记

当 SLO 不达标时,需要快速定位是自身服务的问题还是上游依赖的问题。方法是在 SLI 中标注故障来源:

# 自身服务导致的错误率(排除上游依赖故障)
sum(rate(http_requests_total{status=~"5..", service="order-service", upstream_error="false"}[5m]))
  /
sum(rate(http_requests_total{service="order-service"}[5m]))

# 上游依赖导致的错误率
sum(rate(http_requests_total{status=~"5..", service="order-service", upstream_error="true"}[5m]))
  /
sum(rate(http_requests_total{service="order-service"}[5m]))

这个实现需要在应用层标注错误来源——当捕获到上游超时或下游拒绝时,在 HTTP 响应中添加 upstream_error 标签。这样错误预算消耗就能区分"自身问题"和"依赖问题",避免跨团队扯皮。

总结

可靠性度量体系的核心不是工具,是思维方式的转变——从"监控资源"到"度量体验",从"静态阈值"到"动态预算",从"主观判断"到"数据决策"。

回顾这套四层模型的关键要点:

  1. 用户旅程拆解是地基:先搞清楚用户在乎什么,再决定度量什么。不做这一步,后面全是空中楼阁
  2. SLI 聚焦用户体验:CPU、内存、磁盘这些是诊断指标,不是体验指标。延迟用百分位数,禁用平均值
  3. SLO 基于数据设定:拉 90 天历史数据,定在略高于历史水平的位置。SLO 不是越高越好
  4. 错误预算驱动决策:预算充足就大胆迭代,预算紧张就保守运维。这不是限制发布,是让发布决策有据可依
  5. 多窗口燃烧速率告警:短窗口保速度,长窗口保精度。两个窗口同时超阈值才触发,兼顾灵敏度和准确率

在实际项目中,我推动可用性从 99.5% 提升到 99.9% 的过程,不是靠加监控加告警——监控和告警一直都有。真正起作用的是建立错误预算决策机制:让团队知道"什么时候可以放心发版、什么时候需要踩刹车"。当发布决策从"凭感觉"变成"看数据",可靠性提升就是自然结果。

如果你正在搭建 SLO 体系,我的建议是:从一个核心服务开始,走完四层全流程,再扩展到更多服务。不要一上来就给所有服务定 SLO——那会让团队陷入配置泥潭,最后不了了之。记住,度量体系的终极目标是让系统可靠性可决策,而不是让面板看起来很满。

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. Google SRE Workbook - Chapter 5: Alerting on SLOs — Google SRE 团队,多窗口多燃烧速率告警的原始方法论
  2. 可缩放的云应用程序和站点可靠性工程 (SRE) — Microsoft Azure 团队,SLI/SLO 在云架构中的落地实践
  3. SLO工程化:从定义、精准计算、错误预算策略到智能化落地演进 — 腾讯云开发者社区,三层模型定义与企业落地陷阱分析
  4. 使用 Prometheus 配置 SLO 监控和告警 — 知乎,燃烧速率告警的 Prometheus 实现参考
  5. SRE 运维:从 0 到 1 建设可落地的可靠性度量框架 — CSDN,SLI 选取原则和错误预算管理实践
  6. 告警治理:如何把每天 500+ 条告警降到 50 条 — 51CTO,告警疲劳问题的量化数据与治理方案