SLO 与燃烧率告警 · 从根上解决阈值问题
错误预算把「可靠性」变成一种可以花的预算。这一章讲清 SLI/SLO/错误预算三件套、燃烧率(burn rate)的定义与那张经典的多窗口告警表,以及为什么它同时解决了误报、漏报和低流量三个难题——附可直接抄的规则。
SLO 与燃烧率告警 · 从根上解决阈值问题
上一章讲了四种定阈值的方法,但都有点将就。有没有一种方法能从根上解决?
有。它来自 Google SRE,叫基于错误预算燃烧率的告警。这是现代告警工程的最佳实践,也是本部分最有价值的一章。
三件套:SLI / SLO / 错误预算
先把三个词分清——它们经常被混用,但含义完全不同:
| 词 | 是什么 | 例子 |
|---|---|---|
| SLI(指标) | 你测的那个数 | 成功请求数 / 总请求数 |
| SLO(目标) | 你给自己定的目标 | SLI ≥ 99.9%(按 30 天滚动窗口) |
| 错误预算 | 1 − SLO,你被允许失败的额度 | 0.1% = 30 天里约 43 分钟 |
| SLA(协议) | 对外的合同,违约要赔钱 | 通常比 SLO 宽松 |
SLI 是事实,SLO 是目标,错误预算 是两者之差。 注意 SLO 必须带时间窗口(“30 天内 99.9%”),否则无意义。
关键思维转变:预算是用来花的
错误预算最反直觉、也最有价值的一点:它不是“底线”,而是“额度”——你应该把它花掉。**
传统思维:“任何故障都是失败,我们要追求零故障。” 错误预算思维:“我们被允许在 30 天内故障 43 分钟。没用完的预算是浪费的。”
为什么“没用完是浪费”?因为可靠性是有成本的,而且成本随着九的增加指数上升:
- 如果你长期只用掉 10% 的预算,说明你过度投资了可靠性——那些工程时间本可以拿去做新功能。
- 预算充足时,应该更激进:更快地发布、多做实验、多搞混沌工程。
- 预算快烧完时,才该踩刹车:冻结发布、专心修稳定性。
这把“要不要发布”这个永恒的争吵,从“拍脑袋 / 谁嗓门大”变成了一个数据驱动的决策**:**
错误预算还剩 60% → 发!
错误预算还剩 5% → 冻结,先修这是 SRE 最重要的文化贡献:它让“稳定性”和“速度”从对立变成了一个可量化的权衡。 而且它给了开发和运维同一个数字去争论,而不是各说各话。
燃烧率:预算烧得多快
燃烧率(burn rate)= 当前错误率 / 错误预算率。它的物理意义是“按这个速度,预算会被用完得多快”。
SLO = 99.9% → 错误预算率 = 0.1%
当前错误率 0.1% → 燃烧率 = 1 → 30 天正好烧完(符合预期)
当前错误率 1% → 燃烧率 = 10 → 3 天就烧完(!)
当前错误率 10% → 燃烧率 = 100 → 7.2 小时烧完(!!)
当前错误率 0.01% → 燃烧率 = 0.1 → 300 天才烧完(过度可靠)燃烧率 = 1 意味着“刚好按计划消耗”。> 1 就是在超支。
这个定义的美妙之处:它把“错误率多少算严重”这个问题,变成了“预算多久会烧完”——而后者有明确的、可行动的含义。 燃烧率 14.4 意味着“2 天的预算会在 1 小时内烧光”,这显然该叫醒人;燃烧率 1.5 意味着“会略微超支”,开个工单就行。
阈值不再需要拍脑袋——它由“你愿意在多久内烧掉多少预算”推导出来。
多窗口多燃烧率:那张经典的表
《SRE Workbook》给出的方案(业界事实标准):用几组不同的窗口 + 燃烧率,兼顾“快速发现严重问题”和“不漏掉慢性问题”。
| 严重度 | 燃烧率 | 长窗口 | 短窗口 | 含义 | 动作 |
|---|---|---|---|---|---|
| 紧急 | 14.4 | 1h | 5m | 2 天预算会在 1 小时内烧光 | page |
| 紧急 | 6 | 6h | 30m | 2 天预算在 6 小时内烧光 | page |
| 一般 | 3 | 1d | 2h | 10% 预算 1 天内烧掉 | ticket |
| 一般 | 1 | 3d | 6h | 慢性劣化 | ticket |
为什么要“长窗口 + 短窗口”两个条件同时满足?
- 长窗口 —— 判断“问题足够严重/持久”。它防误报:一次抖动不会让 1 小时的燃烧率飙到 14.4。
- 短窗口 —— 判断“问题还在持续”。它让告警快速恢复:问题解决后,5 分钟窗口立刻降下来,告警就消了;如果只看 1 小时窗口,问题修好后告警还要挂 1 小时。
两个窗口是一对:长的防误报,短的保证快速恢复。 这比 for 子句更精细——for 只能延迟触发,而短窗口能加速恢复。
为什么燃烧率越高、窗口越短? 因为严重程度和紧迫性成正比:烧得越快,越该早发现(1 小时窗口);烧得慢的(燃烧率 1),你有几天时间反应,可以用 3 天窗口慢慢确认——这样既不会漏掉“慢性失血”,也不会为它半夜叫人。
可以直接抄的规则
配合上一章的 recording rules(多窗口错误率已预聚合好):
groups:
- name: slo-alerts
rules:
# 紧急:14.4 倍燃烧率(1h 且 5m 都超标)
- alert: ErrorBudgetBurnFast
expr: |
(
service:http_errors:ratio_rate1h > (14.4 * 0.001)
and
service:http_errors:ratio_rate5m > (14.4 * 0.001)
)
labels:
severity: page
annotations:
summary: "{{ $labels.service }} 正在快速烧错误预算(2 天的预算 1 小时内烧光)"
runbook_url: "https://wiki/runbooks/error-budget-burn"
# 紧急:6 倍燃烧率(6h 且 30m)
- alert: ErrorBudgetBurnMedium
expr: |
(
service:http_errors:ratio_rate6h > (6 * 0.001)
and
service:http_errors:ratio_rate30m > (6 * 0.001)
)
labels:
severity: page
# 一般:3 倍燃烧率(1d 且 2h)→ 工单
- alert: ErrorBudgetBurnSlow
expr: |
(
service:http_errors:ratio_rate1d > (3 * 0.001)
and
service:http_errors:ratio_rate2h > (3 * 0.001)
)
labels:
severity: ticket注意 0.001 就是错误预算率(1 − 99.9%)。改 SLO 只需要改这个数。
注意这里不需要 for —— 长窗口本身已经起到了防抖作用(1 小时的平均错误率不会因为一次抖动就飙升)。这是燃烧率方案的又一个优点:少一个要调的参数。
它同时解决了三个难题
回头看,燃烧率方案一口气解决了前面几章提到的所有痛点:
- ✅ 阈值不用拍脑袋 —— 由 SLO 和“愿意多久烧完”推导出来。改 SLO,所有阈值自动跟着变。
- ✅ 误报少 —— 长窗口天然防抖,不需要额外调
for。 - ✅ 漏报少 —— 多个燃烧率覆盖了“急性”和“慢性”两类问题。传统单阈值告警必然漏掉慢性失血(错误率 0.3% 永远够不到 1% 的阈值,但它在稳定地烧预算)。
- ✅ 低流量陷阱自动解决 —— 燃烧率衡量的是预算消耗速度。深夜 2 个请求错 1 个,虽然错误率 50%,但烧掉的预算绝对量极小——用长窗口(1h)一平均,燃烧率根本上不去。问题自然消失,不需要打补丁。
- ✅ 严重度自然分级 —— 燃烧率的大小直接对应紧迫性,page 还是 ticket 一目了然。
- ✅ 对症状告警 —— SLI 天然是用户视角的症状,不是内部原因。
一套机制解决六个问题,这就是为什么它是现代告警的最佳实践。
落地建议
① 别一上来就给所有服务定 SLO
从最关键的 1~2 个用户旅程开始(比如“下单”、“登录”)。SLO 应该覆盖“用户旅程”,不是“每个微服务”——用户不在乎你的 auth 服务可用性,他在乎“能不能登录成功”。
② SLO 要和业务方达成一致
99.9% 还是 99.95%,这是个商业决策(多一个九要多花多少钱?值吗?)。技术团队单方面定的 SLO 没有意义——因为“预算烧完要冻结发布”这个约束,必须是业务方也认的。
③ 先观察再执行
新定的 SLO 先只看不告警,跑几周,看真实的燃烧率长什么样。如果基线就达不到 SLO,那不是告警问题,是系统或 SLO 本身的问题。
④ 别忘了延迟 SLO
成功率只是一半。“99.9% 的请求在 300ms 内成功”才是完整的 SLI——慢和坏在用户眼里没区别。延迟 SLI 通常表达为“快于阈值的请求占比”:
# 延迟 SLI:P99 < 300ms 等价于"99% 的请求 < 300ms"
sum(rate(http_request_duration_seconds_bucket{le="0.3", status=~"2.."}[5m]))
/ sum(rate(http_request_duration_seconds_count{status=~"2.."}[5m]))注意这里直接用了 le="0.3" 这个桶——所以桶边界必须正好在 SLO 阈值上!这是第 2 部分那条“桶要在 SLO 阈值附近加密”的硬核理由:没有 le="0.3" 这个桶,你根本算不出这个 SLI。
小结与下一章
SLI(事实)/ SLO(目标,必带时间窗口)/ 错误预算(1−SLO)。关键思维转变:预算是用来花的,不是底线——没用完说明过度投资了可靠性,那些工程时间本可做新功能;预算充足就激进发布,快烧完才踩刹车。这让“稳定性 vs 速度”从争吵变成数据驱动的决策。燃烧率 = 当前错误率 / 错误预算率,物理意义是“预算多久烧完”——阈值由此推导,不用拍脑袋。多窗口多燃烧率(14.4/1h+5m → page;6/6h+30m → page;3/1d+2h → ticket):长窗口防误报、短窗口保证快速恢复,两者是一对;燃烧率越高窗口越短(严重度 ∝ 紧迫性)。它一口气解决六个问题:阈值有依据、误报少(长窗口自带防抖,不用 for)、不漏慢性失血、低流量陷阱自动消失、严重度自然分级、天然对症状告警。落地:从关键用户旅程而非每个微服务开始、SLO 要业务方认可、先观察再执行、别忘延迟 SLO——延迟 SLI 直接用 le="0.3" 桶,这是“桶必须落在 SLO 阈值上”的硬核理由。
告警规则写好了,但通知怎么送到人?下一章看 Alertmanager。👉 Alertmanager。
参考资料
- [1] Google《SRE Workbook》第 5 章「Alerting on SLOs」(多窗口多燃烧率表的来源)、《SRE Book》第 3-4 章。
- [2] 关联阈值怎么定、几个九的含义、recording rules 多窗口预聚合。
- [3] Prometheus 官方文档:Alerting rules。