用燃烧率来挂告警
一句话总结
使用 burn rate 设置告警后,该响时会响,该安静时也能保持安静。
99.9% 意味着每月 43 分钟
| SLO | 每月(30 天)允许时间 |
|---|---|
| 99% | 7.2 小时 |
| 99.9% | 43.2 分钟 |
| 99.95% | 21.6 分钟 |
| 99.99% | 4.32 分钟 |
99.9 和 99.99 在写法上只差一位,要求却相差十倍。在合同中写下 99.99% 之前必须先看这张表——每月只有 4 分钟,一次错误部署就可能全部耗尽。
错误预算就是用来消耗的
如果目标是 99.9%,那么 0.1% 就是允许消耗的份额。完全不使用并不代表做得好——预算始终有剩余,说明目标太低,或部署过于保守。
这正是 SLO 的核心视角:不追求完美,而是约定最多允许出现多少故障。
Burn rate
错误率是允许值的多少倍,称为 burn rate。
- 1 倍 → 到周期结束时恰好用完预算,符合设计
- 14.4 倍 → 只需 2.08 天就会全部耗尽
数字 14.4 的来源如下——它表示一小时内消耗一个月预算的 2%。
0.02 × 30일 × 24시간 = 14.4
以这种速度消耗,就应该立刻叫醒值班人员。反之,如果只有 2 倍左右,创建工单就足够。
为什么当前告警毫无用处
看看一条常见规则。
5분 오류율 > 1%
SLO 为 99.9%,允许错误率是 0.1%,但这条规则并非由该目标推导而来。因此会同时发生两种问题。
- 本该安静却触发告警——凌晨错误率突然升至 1.2%,持续 5 分钟。预算几乎没减少,人却被叫醒了。
- 本该告警却保持安静——错误率整月保持 0.5%,已经消耗预算的五倍,但由于低于阈值,什么也没发生。
告警疲劳并非因为告警数量多,而是因为无用告警太多。
改用 burn rate 表达
오류율 > 14.4 × (1 - SLO)
SLO 为 99.9% 时,阈值是 0.0144(1.44%)。单看数字似乎很接近,但含义完全不同——它表示“正在以每小时消耗预算 2% 的速度燃烧”,因此也明确了应该采取什么行动。
告警名称也要改变。不要叫 HighErrorRate,而要叫 ErrorBudgetBurnFast。名称会决定响应方式。
只有一个窗口并不够
- 只有短窗口(5 分钟)→ 短暂尖峰也会叫醒人
- 只有长窗口(1 小时)→ 快速消耗时发现得太晚
因此,要用 and 连接二者。只有短窗口与长窗口同时超过阈值,才触发告警。这样既能过滤瞬时尖峰,也能快速发现真实消耗。
还要区分严重程度。
| Burn rate | 窗口 | 响应 |
|---|---|---|
| 14.4 | 5 分钟 + 1 小时 | 立即呼叫 |
| 6 | 30 分钟 + 6 小时 | 立即呼叫 |
| 3 | 2 小时 + 1 天 | 工单 |
| 1 | 6 小时 + 3 天 | 工单 |
应该用什么衡量 SLI
计算 burn rate 只是算术。在此之前,如果没有定义什么才算成功,数字就毫无意义。这里需要作出三类选择。
在哪里测量。 在服务器日志中测量,看不到负载均衡器中断的请求;在负载均衡器测量,看不到客户端的 DNS 或 TLS 失败。测量点越接近用户体验越好,但也会混入更多我们无法控制的失败。生产环境中,常见的实用组合是以负载均衡器为基础,再用外部运行的合成监控作为补充。
哪些情况算失败。 5xx 很明确,4xx 则比较模糊。400 通常是客户端错误,可以排除;429 是由我们主动阻断,因此应计入。如果排除 404,就可能错过路由损坏、所有请求都变成 404 的事故。定义必须写入文档;修改定义时,也要同时重新计算历史数据。
慢响应也算失败。 花费 30 秒才成功的响应,对用户而言就是失败。因此,延迟 SLI 不应使用平均值,而应采用在阈值时间内完成的请求比例。与其写“95% 的请求在 300ms 内完成”,不如写“300ms 内完成的请求比例为 99%”,这样就能像可用性一样计算预算。
sum(rate(http_request_duration_seconds_bucket{le="0.3",job="api"}[5m]))
/
sum(rate(http_request_duration_seconds_count{job="api"}[5m]))
不能把所有请求视为相同权重。 如果健康检查和机器人流量占分母的一半,用户经历的失败几乎不会体现在数字中。反过来,如果某个批处理 API 每秒发送数千个请求,一个客户端就会主导整个 SLI。答案是按用户旅程分别测量,但这样做有成本;至少应单独测量一条核心路径。
SLO 不是合同,而是决策工具。 必须先约定:预算有余量时可以进行风险较高的变更,耗尽后则集中精力稳定系统。如果没有这项共识,单纯测量数字,只会在事故后成为争论材料。
告警也必须测试
这是最常遗漏的部分。promtool 支持告警规则的单元测试。
promtool test rules test.yml
输入模拟时间序列,并断言“此时这条告警应该触发”。如果没有测试,就可能部署一条事故发生时反而不响的告警——而且只能在事故中才发现。
还要测试不该触发时是否保持安静(exp_alerts: [])。只测试应该响的情况,只完成了一半。
预算耗尽后怎么办
如果没有这个问题的答案,SLO 就只是装饰。
应提前约定:
- 停止部署新功能,把时间用于稳定系统
- 风险较高的变更推迟到预算恢复之后
- 把重复发生的根因提升为下一个 sprint 的最高优先级
相比确定数字,达成这项共识更困难;没有共识,数字就不会带来任何行动。
在实际工作中
引入这种方法时,最先遇到的不是技术问题,而是共识问题。写下 99.9% 很容易,但要提前确定预算只剩一半时应该停止什么,就必须与产品团队沟通,而这类对话通常并不轻松。
因此,初期最好先设置一段只观察、不触发告警的时期。观察一两个月的实际消耗曲线后,就能看出 99.9% 是否适合服务,还是即使只有 99.5% 也不会有人抱怨。如果设定无法实现的数字,预算每个月第一周就会耗尽,最终所有人都会停止关注它。
此外,使用什么衡量 SLI 是一个比想象中更重大的决定。在负载均衡器侧测量时,即使应用程序宕机也会记录为 5xx,但看不到客户端侧失败;在应用程序侧测量时则正好相反。只需在文档中写明测量位置,就能大幅减少日后围绕数字产生的争论。