SLO 与错误预算
衔接 reliability rel-slo。
SLO 与错误预算
这一课你将能做到
- 从 SLI 推导一条可执行的 SLO(含时间窗口与目标百分比),并为判题接口写出完整定义
- 估算 错误预算,能算出 99.9% 对应每月约 43 分钟的「坏时间」,并说明预算充足/告急时对发布节奏的含义
- 用 burn rate(燃烧率) 解释为何需要多窗口告警,能说清「快烧」和「慢烧」的区别和告警策略
为什么要学
指标告诉你「发生了什么」;SLO 告诉团队「怎样才算对用户够好」;错误预算 把稳定与速度变成可谈判的数字。没有 SLO,告警阈值往往拍脑袋——「5xx > 5% 就叫」这种规则既没依据也没共识;有了 SLO,才能决定「预算将尽时冻结功能、专注稳定性」「预算充足时可以大胆灰度发布」。
判题场景典型:比赛期间用户提交量暴涨 10 倍,一次 30 分钟的故障可能烧光整月预算。面试里「SLO 怎么定」「错误预算怎么算」「burn rate 告警怎么设计」是高频题,本课结合 rel-slo 与 obs-metrics;PromQL 实现见 prom-promql。
先建立图像
| 概念 | 判题系统示例 |
|---|
| SLI | 非 5xx 请求占比;P99 延迟 < 500ms 的请求占比 |
| SLO | 过去 30 天滚动窗口,可用率 ≥ 99.9% |
| Error Budget | 允许的不达标额度:0.1% ≈ 每月约 43 分钟「坏」时间(按时间近似) |
| Burn Rate | 当前消耗错误预算的速度 / 「匀速烧完窗口」的速度 |
预算充足(剩余 > 50%)──► 可加快发布、做实验、灰度更大胆
预算告急(剩余 < 20%)──► 冻结非必要功能、修可靠性、做 postmortem 行动项
预算耗尽(剩余 = 0%) ──► 只允许稳定性工作(团队协议)
Burn rate 直觉:
月预算 = 43.2 分钟
如果 1 小时内烧了 10 分钟(burn rate ≈ 7.2)──► 可能 4~5 小时烧光整月预算 → page
如果 6 小时烧了 5 分钟(burn rate ≈ 0.7) ──► 约 9 天烧光 → ticket(工作时间处理)
跟着做
步骤 1:为判题接口写一条完整 SLO
服务:coderoadmap-backend 判题接口
接口:POST /api/judge/submissions
SLI-1 可用率:非 5xx 请求 / 总请求(按 application 标签聚合,滚动 5 分钟窗口)
SLI-2 延迟:P99 < 500ms(http_server_requests_seconds_bucket 直方图)
SLO:任意滚动 30 天窗口,可用率 ≥ 99.9% 且 P99 达标率 ≥ 99%
排除:计划内维护窗口(需提前 24 小时公告并记录)
应得到:可写进 README/运维约定的一句话承诺,而不是「尽量高可用」这种空话。
步骤 2:估算月度错误预算
对 99.9%:允许失败比例 0.1%。
时间口径(适合「系统可用时长」场景):
30 天 × 24 小时 × 60 分钟 × 0.1% = 43.2 分钟
即每月允许约 43 分钟的「不可用时间」
请求口径(适合「按请求数计费」场景):
假设 30 天总请求 1,000,000 次
错误预算 = 1,000,000 × 0.1% = 1,000 次失败
写下你们团队更倾向时间还是请求口径,并保持一致。判题系统推荐请求口径:比赛期间流量集中,时间口径会被低峰期稀释。
步骤 3:把预算接到发布策略
1. 预算剩余 > 50%:正常发布流程,可灰度 10% → 50% → 100%
2. 预算剩余 20%~50%:需额外评审,缩小灰度范围,回滚方案必须演练过
3. 预算剩余 < 20% 或已耗尽:只合并稳定性修复和 postmortem 行动项,冻结新功能
步骤 4:理解 burn rate 多窗口告警(核心)
单窗口问题:只看「当前错误率 > 1%」无法区分紧急程度——
场景 A:1 小时内错误率 10%,烧了 6 分钟预算
→ burn rate = 6 / (43.2 / 30 / 24) ≈ 10 → 约 4.3 小时烧光整月预算 → 立即 page
场景 B:6 小时错误率 1.5%,烧了 5.4 分钟预算
→ burn rate = 5.4 / (43.2 / 30 / 6) ≈ 1.9 → 约 15 天烧光 → ticket 即可
# 快烧告警:1 小时窗口,burn rate > 10(可能 4 小时烧光月预算)
- alert: JudgeErrorBudgetBurnFast
expr: |
(
sum(rate(http_server_requests_seconds_count{uri="/api/judge/submissions",status=~"5.."}[1h]))
/
sum(rate(http_server_requests_seconds_count{uri="/api/judge/submissions"}[1h]))
) > (10 * 0.001) # 10 倍于匀速烧率
for: 2m
labels:
severity: page
# 慢烧告警:6 小时窗口,burn rate > 2(可能 15 天烧光)
- alert: JudgeErrorBudgetBurnSlow
expr: |
(
sum(rate(http_server_requests_seconds_count{uri="/api/judge/submissions",status=~"5.."}[6h]))
/
sum(rate(http_server_requests_seconds_count{uri="/api/judge/submissions"}[6h]))
) > (2 * 0.001)
for: 1h
labels:
severity: ticket
应得到:快烧 page、慢烧 ticket,避免「短抖动误 page」和「慢流血无人知」。
步骤 5:核对指标标签与 SLO 一致
步骤 6:算一笔账——今天还能不能发版?
假设:30 天 1,000,000 次请求,SLO 99.9%,错误预算 = 1,000 次
今天已失败 400 次,剩余预算 600 次(60%)
问题:现在能不能发新版?
人话答案:
预算剩 60% 看起来还行,但要看——
1. 近期失败集中在哪条路径?如果是判题提交接口,发版风险高
2. 回滚成本高不高?如果涉及数据库 schema 变更,回滚困难
3. 是不是比赛窗口?如果是,冻结发布
结论:预算只是输入之一,结合风险评估和发布时间窗口决策
把这笔账写进发布检查清单,SLO 才从海报变成门禁。
讲清楚要点
- SLI 是测量、SLO 是目标,所以先有可靠 SLI 再谈 SLO——测不准承诺无意义,见 obs-metrics。
- 100% 不可达且昂贵,所以 SLO 故意留错误预算——预算是创新的「燃料」,不是「我们可以烂一点」的借口。
- 预算是决策工具,所以要写进发布与 on-call 协议——否则只是墙上海报,没人当回事。
- 瞬时错误率不等于预算危机,所以要用 burn rate 看「烧完还剩多久」——单阈值告警无法理解「相对预算的紧急程度」。
- 用户路径不同,所以核心 API 与内部批处理应分开 SLO——避免次要路径绑架整体,判题提交和后台统计报表不该共享同一个 SLO。
- 告警为行动服务,burn 告警要链到 obs-monitoring 的 runbook——快烧 page 要 30 秒内能看到 Grafana 面板和止血开关。
- 依赖下游超 SLO 怎么办?——主路径 SLO 仍对用户负责,但复盘要拆「本服务错误」与「依赖错误」;仪表盘用标签分开,避免把全部火力打到自己的 Deployment。
你可能会懵的地方
-
99.9% 和 99.99% 差很多吗?
差一个数量级预算:约 43 分钟 vs 约 4.3 分钟/月。目标每加一个 9,成本与工程约束显著上升——多副本、跨区域容灾、变更冻结窗口都要跟上。选 SLO 是商业决策,不是技术攀比。
-
延迟 SLO 怎么用错误预算?
把「未达延迟目标的请求」当「坏事件」,同样可算预算。例如:P99 < 500ms 达标率 99%,意味着允许 1% 的请求超过 500ms。也可以单独做延迟 SLO,与可用率 SLO 并行。
-
维护窗口怎么处理?
团队协议:计划内维护是否计入。计入则更严;排除则需严格变更窗口记录(提前公告、限定时段、记录审计)。判题系统比赛期间禁止维护,平时维护窗口要明确排除。
-
和 rel-slo 重复吗?
reliability 课偏组织与策略——怎么定 SLO、怎么写进 SLA、怎么做 postmortem;本课偏如何用可观测信号把 SLO 算出来、告起来——PromQL、burn rate、Grafana 面板。两门课交叉引用,不是重复堆砌。
-
多窗口 burn 告警为何成对出现?
只看 1 小时会漏「温水煮青蛙」的慢烧——错误率 0.5% 持续一周,每天烧一点,等你发现时预算已耗尽。只看 6 小时又会在短暂毛刺时误叫——发布瞬间的 30 秒错误飙升不该 page。常见配对:短窗快烧(立刻 page)+ 长窗慢烧(工作日处理)。参数要按你们实际流量调,不要抄数字当宗教。
错法对照
| 错法 | 问题 | 正解 |
|---|
| 「我们要 100% 可用」 | 不可达且昂贵,团队失去信心 | 选可负担的 SLO(如 99.9%),保留预算 |
| SLO 写了但从不算 | 海报工程,发布决策无依据 | 仪表盘 + burn 告警 + 发布门禁 |
| 所有服务共用一个模糊目标 | 次要路径拖垮主路径 | 按用户关键路径拆分 SLO |
| 只看瞬时错误率 page | 短抖动误报,慢烧漏报 | 结合 burn rate 多窗口告警 |
| 次要批任务拖垮主站 SLO | 内部报表失败影响判题接口承诺 | 分类、权重或独立目标 |
卡点急救
- burn rate 告警公式 PromQL 报错或结果异常:确认分子分母的
[1h] 或 [6h] 窗口一致;确认标签选择器没有拼错(如 uri="/api/judge/submissions" 而非 uri="/api/judge/submissions/");先在 Grafana Explore 里单独跑 expr 验证数值合理。
- SLO 面板显示「预算剩余 -50%」:说明已超支。立即启动 postmortem 流程,冻结非必要发布,专注稳定性修复;超支部分不是「欠账下个月还」,而是「这个月已经失败,下个月重新开始」。
- 比赛期间流量暴涨,SLO 计算失真:低峰期的成功请求会稀释比赛期间的失败率。解决方案:按请求口径而非时间口径算预算;或为比赛窗口单独定义更严格的 SLO(如 99.95%)。
面试常问
-
SLO 怎么定?错误预算怎么算?
背这个版本:SLO 定义含四要素——SLI(测量指标,如可用率)、目标值(如 99.9%)、时间窗口(如滚动 30 天)、适用范围(如判题提交接口)。错误预算是允许的不达标额度:99.9% 对应 0.1%,时间口径约每月 43 分钟,请求口径按总请求数 × 0.1%。预算的用途:充足时加快发布,告急时冻结功能,耗尽时只做稳定性工作。
-
burn rate 告警为什么需要多窗口?
单窗口无法区分紧急程度。快烧(如 1 小时 burn rate > 10)意味着可能几小时烧光整月预算,必须立即 page;慢烧(如 6 小时 burn rate > 2)意味着十几天才烧光,工作时间 ticket 处理即可。多窗口组合避免「短抖动误 page」和「慢流血无人知」,参数按实际流量调整。
-
SLO 和 SLA 什么区别?
SLO 是内部目标,用来指导工程决策(发布节奏、告警阈值);SLA 是外部合同承诺,违约要赔钱。SLO 通常比 SLA 更严格——内部留缓冲,避免外部违约。例如:SLO 99.95%,SLA 99.9%,中间 0.05% 是安全垫。
自测三问
- 写出判题接口的一条完整 SLO(含 SLI、目标值、时间窗口、适用范围)。
- 99.9% 月度错误预算是多少分钟?如果预算剩 15%,发布策略该怎么调整?
- 为什么需要快烧 + 慢烧两种 burn rate 告警窗口?各自的 severity 应该是什么?
小结一句话
SLO 把指标变成产品决策;错误预算让「稳定与速度」可谈判——用烧预算速度决定吵不吵醒人。
延伸