概述
错误预算(Error Budget)是 SRE 体系中最精妙的机制设计。它把"稳定性 vs 迭代速度"这个长期依赖口水战的矛盾,转化为一个可量化的工程决策框架:你的系统有一个"不可用额度",花完了就得停下来修。
但实践中,很多团队定义了 SLO 和错误预算之后,就止步于仪表盘上展示一个百分比数字。预算耗尽时该怎么办?快耗尽时要采取什么行动?预算富裕时可以做什么?这些关键问题如果没有明确的策略,错误预算就只是一个好看的数字,而非真正驱动行为的工具。
详细梳理错误预算的消耗状态模型、每种状态对应的行动纲领、发布冻结的标准与流程、预算滚存与重置策略、跨团队协调机制,并配以实战案例。
本文假设读者已了解 SLI/SLO/错误预算的基本概念。如需补充,可参考 Google SRE Book - Embracing Risk 和本站 SRE核心理念:SLI、SLO与错误预算。
一、错误预算的工程本质
不只是"还剩多少额度"
很多人把错误预算理解为"这个月还能宕机多少分钟"。这只是表面理解。错误预算的工程本质是:
错误预算是创新速度与系统稳定性之间的自动调节阀。
它回答了一个在所有工程团队都存在但很难回答的问题:我们现在应该更激进地发布新功能,还是应该停下来提升稳定性?
- 预算充裕 → 系统足够稳定,可以承担更多变更风险 → 加速发布
- 预告耗尽 → 系统已经接近可靠性边界 → 减速发布,专注稳定性
这个调节是自动的、基于数据的、不依赖个人判断和政治博弈的。
错误预算的计算
SLO = 99.9%(30天窗口)
错误预算 = (1 - SLO) × 时间窗口 = 0.1% × 43200 分钟 = 43.2 分钟/月
已消耗预算 = 实际不可用时间
剩余预算 = 43.2 - 已消耗时间
预算消耗率 = 已消耗预算 / 总预算
但"不可用"的判定不只是"服务完全宕机"。任何 SLI 违规都消耗预算:
# 错误预算消耗的多种形式
budget_consumption:
# 完全不可用
- type: downtime
duration: 5min
budget_cost: 5min
# 延迟违规(SLO: P99 < 200ms,实际 P99 = 500ms)
- type: latency_violation
window: 10min # 违规持续 10 分钟
affected_requests: 50000
budget_cost: 10min # 按违规持续时间消耗
# 错误率超标(SLO: 错误率 < 0.1%,实际错误率 = 2%)
- type: error_rate_violation
window: 3min
affected_requests: 8000
budget_cost: 3min
# 数据不一致
- type: correctness_violation
window: 15min
budget_cost: 15min
二、错误预算的四种消耗状态
状态模型
将错误预算的消耗程度划分为四个状态,每个状态对应不同的行动纲领:
预算消耗率
0% ─────────────────────────────────────────── 100%
│ │ │ │
│ 🟢 绿 │ 🟡 黄 │ 🟠 橙 │ 🔴 红
│ 安全区 │ 警戒区 │ 危险区 │ 冻结区
│ 0-25% │ 25-50% │ 50-75% │ 75-100%
│ │ │ │
正常发布 关注但不限制 限制发布 冻结发布
| 状态 | 消耗率 | 含义 | 核心行动 |
|---|---|---|---|
| 🟢 绿色(安全区) | 0-25% | 预算充裕,系统健康 | 正常发布,鼓励创新和实验 |
| 🟡 黄色(警戒区) | 25-50% | 预算开始消耗,需要关注 | 正常发布,但增加监控频率,排查消耗原因 |
| 🟠 橙色(危险区) | 50-75% | 预算消耗过快,有耗尽风险 | 限制非必要发布,要求灰度发布,启动专项治理 |
| 🔴 红色(冻结区) | 75-100% | 预算即将或已经耗尽 | 冻结功能发布,只允许稳定性修复,全员投入改进 |
消耗速率 vs 消耗量
状态判定不能只看"消耗了多少",还要看"消耗得多快"。同样是消耗了 30% 的预算:
- 30 天均匀消耗 30% → 正常节奏,黄色状态即可
- 3 天消耗了 30% → 消耗速率异常,应按橙色处理
引入消耗速率(Burn Rate) 指标:
消耗速率 = (预算消耗量 / 总预算) / (已过时间 / 总时间窗口)
示例:
30天窗口,第3天消耗了 30% 预算
消耗速率 = 0.30 / (3/30) = 0.30 / 0.10 = 3.0
→ 按当前速率,10天就会耗尽整个月的预算
| 消耗速率 | 含义 | 建议行动 |
|---|---|---|
| < 1.0 | 消耗速度正常,预算会持续到窗口结束 | 维持正常节奏 |
| 1.0 - 2.0 | 消耗略快于预期 | 增加关注,排查异常消耗 |
| 2.0 - 5.0 | 消耗明显过快 | 限制发布,启动专项排查 |
| > 5.0 | 消耗极快,即将耗尽 | 立即冻结发布,紧急介入 |
三、每种状态的行动纲领
🟢 绿色状态(0-25%):正常运营
预算充裕意味着系统当前足够稳定,可以承担更多变更风险。
行动纲领:
- 正常发布节奏:不施加额外限制,鼓励团队按计划发布新功能
- 实验性变更:可以尝试新的架构方案、技术栈升级等有风险的改进
- 容量测试:可以进行压力测试、混沌实验等可能短暂影响可靠性的活动
- 预算投资:利用充裕的预算做"预防性投资"——技术债务清理、监控完善、自动化建设
注意事项:
- 绿色状态不意味着可以挥霍预算——故意消耗预算来"刷新"是不合理的
- 如果长期处于绿色状态且剩余预算很多,可能说明 SLO 设定过于宽松,需要考虑收紧
🟡 黄色状态(25-50%):提高警觉
预算开始消耗,虽然还有余量,但需要理解消耗原因。
行动纲领:
- 正常发布,但加强观察:不限制发布,但每次发布后密切关注 SLI 变化
- 消耗归因分析:分析预算消耗的来源——是计划内的维护窗口消耗,还是意外故障?
- 检查趋势:消耗速率是否在加速?如果持续加速,需要提前干预
- 通知相关团队:在 SRE 周报中通报预算消耗情况,让开发团队知晓
消耗归因分类:
budget_consumption_analysis:
total_consumed: 35% # 43.2 分钟中已消耗 15.1 分钟
breakdown:
planned_maintenance:
duration: 4min
percentage: 26%
description: "计划内的数据库维护窗口"
unexpected_incidents:
duration: 7min
percentage: 47%
incidents:
- "07-05 配置错误导致 3 分钟部分不可用"
- "07-08 缓存节点故障导致 4 分钟延迟劣化"
deployment_related:
duration: 4min
percentage: 27%
description: "3 次发布导致的短暂错误率升高"
assessment: "消耗主要来自意外故障,需要关注配置变更管控"
🟠 橙色状态(50-75%):限制风险
预算消耗过半,剩余预算可能不足以支撑到窗口结束。必须采取措施减速。
行动纲领:
- 限制非必要发布:只允许 P0 级别的功能发布和 bug 修复,推迟非紧急功能
- 强制灰度发布:所有变更必须经过灰度发布,从 1% → 10% → 50% → 100%
- 启动专项治理:成立专项小组排查预算消耗原因,制定恢复计划
- 增加防御措施:临时加强监控频率、调低告警阈值、准备回滚方案
- 管理层通报:向技术管理层通报预算状态和风险,争取资源支持
发布限制的具体措施:
# 橙色状态发布策略
deployment_policy:
allowed:
- type: bug_fix
severity: [P0, P1]
requires: canary_release
- type: security_patch
severity: [P0, P1]
requires: canary_release
- type: stability_improvement
requires: full_review
restricted:
- type: new_feature
action: defer
reason: "错误预算橙色状态,推迟到下个预算窗口"
- type: architecture_change
action: defer
reason: "错误预算橙色状态,推迟到下个预算窗口"
mandatory_controls:
- canary_release: true # 必须灰度
- rollback_plan: true # 必须有回滚方案
- change_review: "senior" # 需要 senior 级别 review
- monitoring_watch: 30min # 发布后观察 30 分钟
🔴 红色状态(75-100%):冻结发布
预算即将耗尽或已耗尽。这是最严肃的状态——系统已经逼近可靠性边界。
行动纲领:
- 冻结所有功能发布:只允许安全补丁和稳定性修复,其他一切变更暂停
- 全员投入稳定性:开发团队和 SRE 团队联合投入,集中解决导致预算消耗的根因
- 启动事后复盘:对导致预算耗尽的故障进行深度复盘,确保根因被消除
- 管理层介入:技术管理层需要介入,协调跨团队资源,追踪改进项
- 用户沟通(如需要):如果用户已感知到可靠性下降,需要主动沟通
发布冻结的执行流程:
预算进入红色状态
→ SRE 自动触发冻结通知(邮件 + IM + 看板)
→ CI/CD 管线自动拒绝非稳定性修复的发布请求
→ 开发团队负责人确认冻结范围
→ 每日 Standup 追踪稳定性改进进展
→ 预算恢复到橙色以下 → 解冻发布
冻结期间的发布例外审批:
# 发布冻结期间的例外审批流程
freeze_exception:
criteria:
- "安全漏洞修复(CVE 高危以上)"
- "正在影响用户的 bug 修复"
- "防止预算进一步消耗的稳定性修复"
approval_flow:
1: "提交例外申请,说明原因和风险评估"
2: "SRE 负责人审核技术可行性"
3: "技术总监批准"
4: "仍需灰度发布 + 密切监控"
auto_reject:
- "新功能发布"
- "非紧急的 UI 优化"
- "技术债务清理"
- "依赖升级"
四、预算滚存与重置策略
预算窗口设计
错误预算的窗口长度直接影响行为模式:
| 窗口长度 | 优势 | 局限 | 适用场景 |
|---|---|---|---|
| 7 天(滚动窗口) | 反应灵敏,快速发现问题 | 噪音大,短期波动影响大 | 高频发布的核心服务 |
| 30 天(日历月) | 与业务周期对齐,易于沟通 | 可能"月初挥霍月末紧张" | 大多数服务的默认选择 |
| 90 天(季度窗口) | 平滑短期波动,关注趋势 | 反应迟缓,问题发现太晚 | 成熟稳定的基础服务 |
实践建议:主窗口用 30 天,辅助监控用 7 天滚动窗口的消耗速率。
预算滚存
当一个预算窗口结束时,剩余预算是否可以滚存到下一个窗口?
| 策略 | 说明 | 优势 | 局限 |
|---|---|---|---|
| 不滚存(重置) | 每个窗口重新开始 | 简单清晰,避免预算囤积 | 月末"突击花钱"的道德风险 |
| 部分滚存 | 允许滚存最多 N% | 平衡灵活性和纪律性 | 规则稍复杂 |
| 全额滚存 | 剩余预算全额累积 | 奖励稳定性好的团队 | 可能导致预算囤积过多,SLO 失去约束力 |
推荐策略:部分滚存,上限 50%
新窗口预算 = 基础预算 + min(剩余预算, 基础预算 × 50%)
示例:
基础预算 = 43.2 分钟/月
上月剩余 = 20 分钟
滚存额度 = min(20, 43.2 × 0.5) = min(20, 21.6) = 20 分钟
本月预算 = 43.2 + 20 = 63.2 分钟
另一种情况:
上月剩余 = 35 分钟
滚存额度 = min(35, 43.2 × 0.5) = min(35, 21.6) = 21.6 分钟
本月预算 = 43.2 + 21.6 = 64.8 分钟
预算重置
以下情况需要重置(清零)错误预算:
- SLO 调整:当 SLO 目标值变更时,错误预算基数变化,需要重置
- 重大架构变更:系统架构发生根本性变化后,历史数据不再有参考意义
- 窗口自然结束:如果不采用滚存策略,每个窗口结束时自动重置
重置时的注意事项:
- 重置不等于"债务免除"——导致上一个窗口预算耗尽的根因仍然需要跟进
- 重置后应在 24 小时内确认新的预算基线,并通知所有相关团队
- 避免在窗口边界附近"赶着发布"——这种行为说明文化出了问题
计划内维护的预算处理
计划内维护(如数据库升级、架构迁移)会消耗预算,但这与意外故障性质不同:
策略一:额外预算
月度总预算 = 常规预算 + 维护预算
常规预算 = 43.2 分钟(SLO 99.9%)
维护预算 = 10 分钟(预先申请)
总预算 = 53.2 分钟
策略二:维护窗口豁免
# 维护窗口期间的 SLI 计算
maintenance_window:
start: "2026-07-15 02:00"
end: "2026-07-15 04:00"
slo_exclusion: true # 此期间的 SLI 违规不计入错误预算
conditions:
- "必须提前 7 天公告"
- "必须安排在低峰时段"
- "单月维护窗口不超过 2 次"
- "每次不超过 2 小时"
推荐策略二——维护窗口豁免,但严格管控豁免条件,防止滥用。
五、跨团队协调
依赖服务的预算联动
微服务架构中,服务 A 依赖服务 B。当服务 B 的故障导致服务 A 的 SLO 违规时,预算消耗应该怎么算?
场景:支付服务依赖用户服务
用户服务故障 5 分钟 → 支付服务也间接不可用 5 分钟
→ 两个服务的错误预算都消耗了 5 分钟
原则:上游故障导致的下游预算消耗,由上游承担
# 预算消耗归因
budget_attribution:
payment_service:
total_consumed: 8min
breakdown:
self_caused: 3min # 自身原因导致
upstream_caused: 5min # 上游依赖导致
- user_service: 2min
- inventory_service: 3min
# 预算调整
adjusted_budget:
payment_service:
original_budget: 43.2min
upstream_transfer: 5min # 上游转移的消耗
effective_consumed: 3min # 实际自身消耗
effective_remaining: 40.2min
共享服务的预算分摊
基础设施服务(如数据库、消息队列)被多个业务服务共享。当基础设施故障时,多个业务服务同时受影响:
数据库故障 10 分钟 → 支付服务、订单服务、用户服务全部受影响
→ 三个服务各消耗 10 分钟预算
→ 但根因在数据库团队
协调机制:
- 基础设施服务有自己的 SLO:数据库服务也需要定义 SLO 和错误预算
- 故障归因到基础设施:基础设施故障导致的下游消耗,记入基础设施的预算
- 升级机制:如果基础设施频繁导致下游预算消耗,触发基础设施团队的专项治理
预算协调会议
对于有复杂依赖关系的多服务系统,建议定期召开预算协调会议:
参会者:各服务 SRE 负责人 + 架构师
频率:每两周一次
议题:
1. 各服务预算消耗状态
2. 跨服务故障的归因和分摊
3. 共享基础设施的预算消耗趋势
4. 联合改进项的进展
六、实战案例
案例一:从绿色到红色的 72 小时
背景:某电商平台的支付服务,SLO 99.9%,月度预算 43.2 分钟。
Day 1(7月1日):
14:00 发布新支付渠道集成
14:15 灰度 10%,错误率正常
14:30 全量发布,一切正常
→ 预算消耗:0 分钟,状态:🟢 绿色
Day 2(7月2日):
02:00 新支付渠道的回调处理有 bug,在高并发下出现超时
错误率从 0.05% 升至 0.8%
持续 15 分钟后被告警发现
02:15 回滚发布
→ 预算消耗:15 分钟,状态:🟡 黄色(35%)
Day 3(7月3日):
10:00 修复 bug 后重新发布
10:30 另一个 unrelated 的配置变更导致数据库连接异常
支付服务不可用 12 分钟
→ 预算消耗:15 + 12 = 27 分钟,状态:🟠 橙色(63%)
关键决策点:
状态: 橙色(63%)
消耗速率: 27min / 3天 = 9min/天
按此速率: 43.2 - 27 = 16.2min 剩余,约 1.8 天后耗尽
→ 消耗速率 = 27/43.2 ÷ 3/30 = 0.625 ÷ 0.1 = 6.25(极高)
行动:
1. 冻结所有非稳定性发布 ✅
2. 启动根因分析 ✅
3. 加强配置变更管控 ✅
4. 管理层通报 ✅
结果:冻结发布 5 天,集中修复了支付渠道回调 bug 和配置变更流程。剩余预算 16.2 分钟在月末恰好用完,未突破 SLO。
经验教训:消耗速率比消耗量更能反映风险。如果只看"还剩 37%",可能会低估危险——实际上按当时的速率,不到 2 天就会耗尽。
案例二:预算富裕的正确用法
背景:某服务的 API 网关,SLO 99.95%,月度预算 21.6 分钟。连续 3 个月预算消耗低于 30%。
分析:
budget_history:
- month: "2026-04"
consumed: 4min # 18%
- month: "2026-05"
consumed: 6min # 28%
- month: "2026-06"
consumed: 3min # 14%
assessment: "连续 3 个月预算消耗低于 30%"
可能的解释:
- SLO 设定过于宽松 → 考虑收紧到 99.99%
- 团队过于保守,不敢做有风险的变更 → 鼓励技术创新
- 系统确实非常稳定 → 利用富裕预算做预防性投资
决策:
action_plan:
1. 收紧 SLO:
from: 99.95%
to: 99.99%
reason: "当前 SLO 无法有效约束行为,收紧后预算 = 4.3min/月"
2. 利用当前稳定性窗口做技术升级:
- API 网关从 Nginx 迁移到 Envoy
- 引入 Service Mesh
- 这些变更有风险,但预算充裕时是最佳时机
3. 增加混沌工程实验:
- 主动注入故障验证系统弹性
- 可能短暂消耗预算,但能发现潜在风险
经验教训:预算长期富裕不是好事——要么 SLO 太松失去了约束力,要么团队过于保守失去了创新空间。错误预算的目标不是"不花钱",而是"把钱花在有价值的地方"。
七、错误预算的度量与告警
关键度量指标
# 错误预算剩余比例
1 - (
sum(rate(http_requests_total{status=~"5.."}[30d]))
/
sum(rate(http_requests_total[30d]))
) / (1 - 0.999)
# 消耗速率(1小时窗口)
(
sum(rate(http_requests_total{status=~"5.."}[1h]))
/
sum(rate(http_requests_total[1h]))
) / (1 - 0.999)
# 预计预算耗尽时间
remaining_budget_ratio /
(
sum(rate(http_requests_total{status=~"5.."}[1h]))
/
sum(rate(http_requests_total[1h]))
) / (1 - 0.999)
多窗口告警策略
参考 Google SRE 的多窗口多消耗速率告警:
# 基于消耗速率的多窗口告警
groups:
- name: error-budget-alerts
rules:
# 快速消耗:1小时内消耗了 2% 以上的月度预算
- alert: ErrorBudgetFastBurn
expr: |
(
sum(rate(http_requests_total{status=~"5.."}[1h]))
/
sum(rate(http_requests_total[1h]))
) > (1 - 0.999) * 14
for: 2m
labels:
severity: page
budget_state: orange
annotations:
summary: "错误预算快速消耗(1小时窗口消耗速率 > 14x)"
description: "按当前速率,约 2 小时内将消耗 30% 月度预算"
# 慢速消耗:6小时内消耗了 5% 以上的月度预算
- alert: ErrorBudgetSlowBurn
expr: |
(
sum(rate(http_requests_total{status=~"5.."}[6h]))
/
sum(rate(http_requests_total[6h]))
) > (1 - 0.999) * 6
for: 15m
labels:
severity: ticket
budget_state: yellow
annotations:
summary: "错误预算持续消耗(6小时窗口消耗速率 > 6x)"
description: "按当前速率,约 5 天内将耗尽月度预算"
告警设计原理:
| 告警类型 | 短窗口 | 长窗口 | 消耗速率 | 目的 |
|---|---|---|---|---|
| 快速消耗 | 1h | - | 14x | 捕捉突发故障,立即响应 |
| 持续消耗 | - | 6h | 6x | 捕捉慢性问题,工单跟踪 |
| 预算耗尽 | - | 30d | 1x | 预算耗尽通知,触发冻结 |
八、常见误区与应对
误区一:“预算耗尽就是 SLO 不达标”
误区:认为错误预算耗尽等同于 SLO 违约,因此不敢消耗任何预算。
纠正:错误预算就是用来消耗的——只要不超额,消耗是正常的。零消耗反而说明 SLO 太松或团队太保守。
误区二:“预算耗尽只影响 SRE 团队”
误区:认为错误预算是 SRE 的事,开发团队不受影响。
纠正:预算耗尽导致的发布冻结直接影响开发团队的功能交付。错误预算必须是跨团队共享的约束机制——开发团队用预算来发布新功能,SRE 团队用预算来保障稳定性,双方都有责任管理预算。
误区三:“手动管理预算状态”
误区:依赖人工判断预算状态并决定是否冻结发布。
纠正:预算状态判定和发布冻结应该自动化。通过 CI/CD 管线集成预算检查,当预算进入红色状态时自动拒绝非稳定性发布的合并请求。
# CI/CD 管线中的预算检查
pre_deploy_check:
- name: check-error-budget
script: |
BUDGET_STATE=$(curl -s $BUDGET_API/service/$SERVICE_NAME/state)
if [ "$BUDGET_STATE" = "red" ] && [ "$DEPLOY_TYPE" != "stability_fix" ]; then
echo "Error budget in RED state. Only stability fixes are allowed."
exit 1
fi
if [ "$BUDGET_STATE" = "orange" ] && [ "$CANARY_ENABLED" != "true" ]; then
echo "Error budget in ORANGE state. Canary release is mandatory."
exit 1
fi
误区四:“所有服务用同一个 SLO”
误区:全公司统一用 99.9% 作为 SLO。
纠正:不同服务对业务的关键程度不同,SLO 应该分级设定:
| 服务等级 | SLO | 错误预算/月 | 适用场景 |
|---|---|---|---|
| L1 核心 | 99.99% | 4.3 分钟 | 支付、登录、核心 API |
| L2 重要 | 99.95% | 21.6 分钟 | 订单、搜索、推荐 |
| L3 一般 | 99.9% | 43.2 分钟 | 报表、后台管理 |
| L4 辅助 | 99.5% | 216 分钟 | 内部工具、文档 |
总结
错误预算的消耗策略是 SRE 体系从"概念"走向"行动"的关键环节。一个好的消耗策略应该回答以下问题:
- 当前预算状态是什么? → 四色状态模型,一目了然
- 每个状态该做什么? → 明确的行动纲领,不留模糊空间
- 什么时候冻结发布? → 75% 消耗或消耗速率 > 5x,自动化执行
- 预算怎么滚存? → 部分滚存,上限 50%,平衡灵活性和纪律性
- 跨团队怎么协调? → 归因到根因服务,共享基础设施独立 SLO
错误预算的本质不是一个"扣分机制",而是一个资源分配框架。它让团队在稳定性和创新之间做出有数据支撑的权衡——这才是 SRE 的工程精神。
最后提醒:错误预算策略需要与组织文化匹配。在初期推行时,可以从宽松的阈值开始(比如红色设为 90% 而非 75%),随着团队成熟度提升逐步收紧。重要的是建立"预算驱动决策"的习惯,而非一开始就追求完善的策略设计。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Google SRE Book - Embracing Risk — Google SRE 团队,参考了Google SRE Book - Embracing Risk相关内容