概述
凌晨三点,你被告警吵醒。爬起来一看: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 内加载完成 | 白屏 / 加载超 5s | CDN、前端服务 |
| 搜索商品 | 搜索结果 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.4 | 2 天内预算耗尽 |
| Ticket(工单) | 30 分钟 | 6 小时 | 6 | 5 天内预算耗尽 |
| Ticket(工单) | 2 小时 | 1 天 | 3 | 10 天内预算耗尽 |
短窗口确认"问题正在发生”,长窗口确认"不是偶发抖动"。两个窗口同时超阈值才触发告警——这就是多窗口的精髓:用短窗口保速度,用长窗口保精度。
为什么是 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 可视化 | Pyrra | SLO 仪表盘与告警管理 | 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-30 | Sloth + Pyrra | 开箱即用,维护成本低 |
| 50-200 人 | 30-100 | Sloth + 自建看板 | 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 标签。这样错误预算消耗就能区分"自身问题"和"依赖问题",避免跨团队扯皮。
总结
可靠性度量体系的核心不是工具,是思维方式的转变——从"监控资源"到"度量体验",从"静态阈值"到"动态预算",从"主观判断"到"数据决策"。
回顾这套四层模型的关键要点:
- 用户旅程拆解是地基:先搞清楚用户在乎什么,再决定度量什么。不做这一步,后面全是空中楼阁
- SLI 聚焦用户体验:CPU、内存、磁盘这些是诊断指标,不是体验指标。延迟用百分位数,禁用平均值
- SLO 基于数据设定:拉 90 天历史数据,定在略高于历史水平的位置。SLO 不是越高越好
- 错误预算驱动决策:预算充足就大胆迭代,预算紧张就保守运维。这不是限制发布,是让发布决策有据可依
- 多窗口燃烧速率告警:短窗口保速度,长窗口保精度。两个窗口同时超阈值才触发,兼顾灵敏度和准确率
在实际项目中,我推动可用性从 99.5% 提升到 99.9% 的过程,不是靠加监控加告警——监控和告警一直都有。真正起作用的是建立错误预算决策机制:让团队知道"什么时候可以放心发版、什么时候需要踩刹车"。当发布决策从"凭感觉"变成"看数据",可靠性提升就是自然结果。
如果你正在搭建 SLO 体系,我的建议是:从一个核心服务开始,走完四层全流程,再扩展到更多服务。不要一上来就给所有服务定 SLO——那会让团队陷入配置泥潭,最后不了了之。记住,度量体系的终极目标是让系统可靠性可决策,而不是让面板看起来很满。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Google SRE Workbook - Chapter 5: Alerting on SLOs — Google SRE 团队,多窗口多燃烧速率告警的原始方法论
- 可缩放的云应用程序和站点可靠性工程 (SRE) — Microsoft Azure 团队,SLI/SLO 在云架构中的落地实践
- SLO工程化:从定义、精准计算、错误预算策略到智能化落地演进 — 腾讯云开发者社区,三层模型定义与企业落地陷阱分析
- 使用 Prometheus 配置 SLO 监控和告警 — 知乎,燃烧速率告警的 Prometheus 实现参考
- SRE 运维:从 0 到 1 建设可落地的可靠性度量框架 — CSDN,SLI 选取原则和错误预算管理实践
- 告警治理:如何把每天 500+ 条告警降到 50 条 — 51CTO,告警疲劳问题的量化数据与治理方案