别再靠吵架定优先级了:错误预算才是产品决策的底牌
做运维这些年,我越来越觉得一件事:很多团队不是不知道系统需要稳定,而是不知道,为了稳定到底应该牺牲多少业务速度。
产品经理说,新功能必须这周上线,不然就赶不上业务节点;研发说,最近系统已经不太稳定了,再加需求迟早出事;运维说,监控天天报警,能不能先停下来把问题解决掉?
三方都觉得自己有道理。
最后往往是什么结果?谁声音大听谁的,谁承担事故责任谁就更谨慎,谁背着业务指标谁就更激进。等到线上真的出了问题,大家再来讨论为什么没有提前做好准备。
这就是很多团队在可靠性管理上最尴尬的地方:我们天天讨论稳定性,却很少把稳定性当成一笔可以计算、可以分配、也可以用于决策的成本。
SLA、SLO 和错误预算,就是拿来解决这个问题的。
但我想先把观点摆在前面:
错误预算不是允许系统出故障,而是让团队知道,为了更快交付业务,我们究竟愿意承担多少可靠性风险。
当它真正参与产品决策时,SRE 就不再只是那个天天催大家修 Bug 的人,而是能够拿着数据和产品经理一起讨论业务收益与技术风险的人。
一、先把 SLA、SLO 说清楚,别连目标都没对齐
这三个概念,经常被放在一起说,但它们解决的问题并不一样。
SLI:系统实际表现怎么样。
比如接口成功率、请求延迟、任务处理成功率。这些是拿来衡量服务质量的指标。
SLO:我们希望系统做到什么程度。
比如,过去 30 天内,核心 API 的成功率不低于 99.9%。
SLA:我们对客户承诺了什么。
比如,服务可用性需要达到合同约定的标准,否则可能需要提供服务抵扣或承担其他约定的责任。
换句话说,SLI 是实际成绩,SLO 是内部目标,SLA 是对外承诺。
举个例子。
假设公司做了一个订单服务:
- 实际成功率是 99.97%,这是 SLI 的测量结果。
- 内部目标是 99.9%,这是 SLO。
- 合同承诺是 99.5%,这是 SLA。
这时候,服务虽然没达到满分,但内部目标已经达成,对外承诺也没有失守。
当然,这只是一个示例。真实项目还要看统计窗口、排除条件、服务抵扣条款等具体定义。
这里最关键的一点是:SLO 不应该简单等于 SLA。
如果合同承诺是 99.5%,团队内部也只盯着 99.5%,那么只要服务勉强达到合同要求,大家就可能觉得没必要继续投入。
但对于核心交易系统来说,满足合同底线,并不意味着客户体验已经足够好。
反过来,如果内部 SLO 定得高得离谱,比如要求所有服务全年达到 99.9999%,却没有业务数据支撑,那么团队很可能把大量时间花在追逐一个昂贵、甚至没有必要的数字上。
所以,目标不是越高越好,而是应该和业务的重要性、故障的实际损失,以及实现目标所需要的成本相匹配。
这就引出了今天真正要聊的东西:错误预算。
二、错误预算,本质上就是给可靠性定一个可计算的成本
错误预算的计算公式其实很简单:
\[ \text{错误预算率}=1-\text{SLO} \]
假设我们规定一个订单 API,在过去 30 天内的成功率目标是 99.9%。
那么,错误预算率就是:
\[ 1-99.9\%=0.1\% \]
如果采用 30 天的统计窗口,那么整个窗口一共有 43,200 分钟。
在最简单的、按时间衡量可用性的模型中,99.9% 的目标对应:
\[ 43200\times0.1\%=43.2\text{分钟} \]
也就是说,如果把服务不可用时间作为衡量指标,那么这个服务在 30 天内大约有 43.2 分钟的不可用预算。
注意:这里假设所有分钟都计入统计,不排除维护时间,也没有考虑复杂的多区域可用性定义。实际错误预算应该根据团队定义的 SLI 计算,而不能一律按停机时间计算。
如果是按请求成功率衡量,计算方式就变了。
假设过去 30 天,订单服务处理了 100 万次有效请求,SLO 是 99.9%,那么允许的失败请求数就是:
\[ 1000000\times(1-99.9\%)=1000 \]
也就是说,在这个例子里,错误预算是 1,000 次失败请求。
如果已经出现了 700 次失败,那么还剩 300 次。
这才是错误预算真正有用的地方:它把抽象的“系统要稳定”,转换成了团队可以观察和讨论的数字。
以前产品经理提需求,研发说风险太高,产品经理可能会觉得研发在拖进度。
现在可以把话说清楚:
“过去 30 天,核心服务的错误预算已经消耗了 80%。最近三次发布中有两次引发了线上回滚。如果继续一次性上线这批高风险改动,我们需要先讨论清楚,剩下的可靠性空间是否足够。”
这不是谁反对谁,而是把可靠性成本摆到了桌面上。
三、错误预算最值钱的地方,不是计算,而是决定下一步做什么
假设现在有一个电商团队,订单 API 的 SLO 是 99.9%。
过去一段时间,系统比较稳定,错误预算只消耗了 20%。这时,产品准备上线一个新的促销功能。
这个功能预计能提高订单转化率,但同时会增加并发量,也会引入新的缓存逻辑。
此时有两种做法。
第一种,运维认为新功能有风险,建议先暂停。
第二种,产品认为业务机会很重要,坚持上线。
如果没有共同认可的衡量标准,双方可能一直争论下去。
但如果有错误预算,事情就不一样了。
假设错误预算还剩 80%,团队可以决定:允许新功能上线,但必须增加灰度发布、监控、回滚机制,并限制初期流量。
这背后的逻辑是:
当系统可靠性表现良好时,团队可以合理地承担一部分风险,换取业务增长。
那么,如果情况反过来呢?
假设最近一个月接连发生了几次线上事故,错误预算已经消耗了 95%,而且系统仍然频繁出现超时。
这时产品又提了一个新需求,预计可以提高 2% 的转化率。
问题来了:还要不要发?
我的看法是,除非这个需求涉及紧急业务、重大合规要求,或者能够直接降低更大的风险,否则应该暂停非必要的高风险变更,把资源优先投入故障治理。
原因很简单。
新功能带来的收益是预期的,但系统继续发生故障的风险已经有实际数据支撑。
如果故障导致用户无法下单,原本想通过新功能增加的订单,可能还没有新增多少,就已经被系统故障造成的损失抵消了。
所以,错误预算并不是告诉大家“今天允许坏几次”,而是帮助团队回答三个问题:
- 现在还有多少风险空间?
- 接下来投入时间,是做新功能更划算,还是修复可靠性问题更划算?
- 如果决定继续发布,应该采取多大的保护措施?
这才是它真正的经济学意义。
四、别把错误预算做成一张报表,要让它真正影响发布决策
很多团队的问题是,监控平台上有 SLO,周报里面有错误预算,复盘会议也会讨论可靠性,但这些东西和日常研发流程完全脱节。
产品继续排需求,研发继续发版本,运维继续救火。
等月底统计发现错误预算已经耗尽时,大家才后知后觉地说一句:“这个月系统稳定性确实不太好。”
这就失去了错误预算的价值。
我的建议是:先建立一个简单、清晰、可执行的发布策略。
下面给一个 Python 示例。假设系统已经根据真实请求数据算出了最近 30 天的错误预算消耗比例。
def get_release_policy(budget_consumed: float) -> dict: """ budget_consumed:错误预算已消耗比例,范围 0~1。 例如 0.8 表示已经消耗了 80%。 这些阈值是示例,需要结合业务风险调整。 """ if budget_consumed >= 1.0: return { "level": "FREEZE", "action": "暂停非必要高风险发布,优先处理可靠性问题" } if budget_consumed >= 0.8: return { "level": "RESTRICT", "action": "限制高风险变更,发布需额外评审" } if budget_consumed >= 0.5: return { "level": "CAUTION", "action": "允许发布,但需要灰度、监控和回滚预案" } return { "level": "NORMAL", "action": "按常规流程发布,持续监控错误预算" }result = get_release_policy(0.85)print(result)
这个示例没有什么复杂算法,但我认为比单纯做一张漂亮的稳定性仪表盘更有价值。
因为它把观测结果和下一步行动连接起来了。
需要注意,代码中的 50%、80% 和 100% 都不是行业统一标准,只是为了演示策略如何落地。对于支付、医疗或工业控制等高风险系统,阈值和发布规则可能需要更加严格。
另外,实际系统不能只盯着“预算消耗了多少”。
假设这个月刚开始两天,错误预算就消耗了 80%,那么即使剩余预算暂时还有 20%,也不代表系统可以继续按正常节奏运行。
这时候就需要引入另一个指标:错误预算消耗速度,也就是 Burn Rate。
五、预算还没用完,不代表你还能继续折腾
这是我认为很多团队最容易忽略的一点。
错误预算描述的是还有多少空间,消耗速度描述的是按照当前情况发展下去,会不会很快把空间用光。
假设我们的 SLO 是 99.9%,那么允许的错误率是 0.1%。
如果最近一段时间实际错误率达到了 1%,那么消耗速度就是:
\[ \text{Burn Rate}=\frac{1\%}{0.1\%}=10 \]
也就是说,错误预算正以目标允许速度的 10 倍被消耗。
如果这种状态持续下去,预算显然撑不了多久。
可以用下面的代码简单演示:
def calculate_burn_rate( slo: float, actual_error_rate: float) -> float: """ slo:目标成功率,例如 0.999。 actual_error_rate:实际错误率,例如 0.01。 """ allowed_error_rate = 1 - slo if not 0 < allowed_error_rate < 1: raise ValueError("SLO 必须大于 0 且小于 1") if not 0 <= actual_error_rate <= 1: raise ValueError("实际错误率必须在 0 到 1 之间") return actual_error_rate / allowed_error_rateburn_rate = calculate_burn_rate( slo=0.999, actual_error_rate=0.01)print(f"错误预算消耗速度:{burn_rate:.1f} 倍")
但这里必须强调:燃烧速度的判断一定要结合观察窗口。
一次持续几十秒的偶发错误,与连续半小时的大规模失败,业务影响完全不同。
实际生产中,可以分别观察较短窗口和较长窗口的错误预算消耗速度。短窗口帮助快速发现突发事故,长窗口则有助于过滤瞬时噪声,避免一次小波动就让整个团队进入发布冻结状态。
更重要的是,错误率也不能脱离流量看。
如果某个 API 平时每分钟只有几十个请求,一个请求失败就可能让错误率剧烈波动。对于低流量服务,应结合失败请求数量、用户影响、统计置信度等因素判断,避免机械地按百分比做决策。
这也是为什么我不建议直接把一个 Burn Rate 数值写死在发布系统里,然后让它自动决定所有服务是否允许上线。
工具可以帮我们识别风险,但真正的发布策略仍然需要理解业务场景。
六、算清楚可靠性这笔账,别只算开发成本
这里还有一个更深的问题。
为什么产品经理往往愿意投入大量资源开发新功能,却不愿意花时间治理稳定性?
原因不一定是他们不重视稳定性,而是新功能的收益更容易被量化。
例如:
新功能预计开发两周,可以增加 3% 的订单转化率。
这笔收益虽然只是预测,但可以放进业务模型中进行讨论。
而修复一个偶尔发生的超时问题,需要研发投入一周时间。至于这周到底能减少多少损失,很多时候没人说得清楚。
于是,开发新功能更容易获得资源,稳定性治理就成了“有时间再做”的事情。
这时候,SLO 和错误预算能够帮助团队把可靠性成本逐步量化。
举个简单的例子。
假设一个订单系统每天处理 10 万笔订单,每笔成功订单平均贡献 20 元毛利。
假设我们观察到,某类故障平均每天影响 0.5% 的订单,而且影响到的订单都无法正常完成交易。
那么,仅从这部分毛利估算,每天的潜在损失就是:
daily_orders = 100_000affected_rate = 0.005gross_profit_per_order = 20daily_lost_profit = ( daily_orders * affected_rate * gross_profit_per_order)print(f"估算的每日毛利损失:{daily_lost_profit:,.0f} 元")
这里算出的金额,只是基于假设的估算,并不代表真实故障造成的最终损失。
实际评估时,还需要考虑订单是否能够重试成功、客户是否会稍后回来下单、故障影响是否与其他业务因素重叠,以及退款、赔偿和客户流失等间接成本。
但即使只是粗略估算,也比一句“系统稳定性很重要”更有说服力。
如果一类重复出现的故障,每个月造成的预期损失已经超过治理所需成本,那么优先处理它就不只是技术团队的诉求,也是一项有经济依据的业务决策。
反过来,如果某个内部低流量工具偶尔出现轻微延迟,对业务几乎没有影响,那么把它从 99.9% 提升到 99.99%,未必值得投入大量资源。
可靠性没有脱离业务价值的绝对高低,只有与业务风险是否匹配。
当然,也不能把所有问题都折算成短期收入。数据丢失、安全事故、合规风险和对用户造成的严重损害,不能简单用眼前的毛利损失去衡量。它们可能需要独立且不可妥协的控制标准。
错误预算能够帮助我们合理分配风险,但不应该成为突破安全底线的借口。
七、错误预算也有一个坑:千万别把它变成新的 KPI
最后,我想提醒一个特别现实的问题。
当管理层发现错误预算能够量化可靠性后,很容易产生一个新想法:
既然错误预算能够衡量稳定性,那就把它设成团队考核指标。
于是,团队开始想办法让数字更好看。
有的团队只选择容易达成的 SLI,把影响用户体验的错误排除在统计之外;有的团队不愿意发布新功能,因为任何新变更都可能影响预算;还有的团队只关心预算有没有超标,却不愿意主动发现那些尚未触发告警、但已经影响用户的问题。
最后,报表漂亮了,系统却不一定更可靠。
问题出在哪里?
出在我们把一个帮助决策的工具,变成了一个必须完成的考核数字。
我一直认为,错误预算首先应该服务于团队,而不是用来给团队贴标签。
它的价值是帮助大家在需求、交付速度和可靠性之间做出更理性的选择,而不是证明某个团队优秀,或者证明某个人没有做好工作。
所以,在落地时,我更建议团队关注这些问题:
- 错误预算为什么被消耗?是业务流量增长、系统容量不足,还是某次变更引发了故障?
- 预算消耗是否集中在特定接口、特定时间段或特定版本?
- 哪些可靠性投入真正减少了用户影响?
- 什么时候可以加快交付,什么时候应该降低发布风险?
比起月底宣布“错误预算完成率不达标”,这些问题更能推动系统变好。
还有一点,团队应该提前明确错误预算的统计窗口和恢复规则。月度窗口结束,并不意味着历史故障造成的风险自动消失。如果采用滚动 30 天窗口,就应该根据真实的窗口数据持续计算,而不是每个月 1 号简单清零。
只有当统计口径稳定、数据来源可信、行动规则透明时,错误预算才能真正成为共同遵守的决策机制。
写在最后
我觉得,SLA、SLO 和错误预算最有意义的地方,并不是让运维多了几张报表,也不是让研发多了一套流程。
而是让团队终于有机会用同一种语言,讨论过去总是说不清楚的事情。
产品希望更快上线,这没有错;研发希望减少技术债务,这也没有错;运维希望系统稳定,更没有错。
真正的问题在于,当这些目标发生冲突时,我们过去往往只能依赖经验、情绪和职位来决定谁让步。
错误预算提供了一个更公平的起点:先看业务目标,再看实际可靠性表现,然后根据剩余风险空间决定下一步行动。
预算充足时,就有计划地尝试新功能,用灰度、监控和回滚机制换取业务速度;预算快速消耗时,就主动降低变更风险,把资源投入最有价值的故障治理;当预算耗尽时,就应该认真评估是否需要暂停非必要的高风险变更,而不是假装什么都没发生。
但也别走向另一个极端。错误预算不是鼓励大家把预算用光,更不是鼓励大家为了交付速度牺牲用户利益。
它真正想解决的,是让每一次冒险都有依据,让每一次保守也说得清楚。
作为运维,我越来越觉得,可靠性工程的成熟,不是把系统做成永远不会出错的样子,而是让团队知道哪些错误可以接受、哪些风险必须避免,以及当风险开始失控时,大家应该如何共同做出选择。
毕竟,技术团队存在的目的,不是守着系统不让任何人碰。
我们的任务,是在业务不断向前的同时,让系统始终处于一个能够被理解、被衡量,也能够被控制的风险范围内。