想象一个家庭立下约定:30 天里孩子至少有 27 天能去公园;剩余 3 天是大家默默接受的“坏天气额度”。每次暴雨、临时加班、游乐场关闭,都会从额度里扣掉一天。当额度低于安全线,全家人会暂停新增计划,集中应对可能到来的下个坏天气。 软件系统也是同样的道理。无论系统做得多好,总会有故障、慢请求、超时。与其向用户承诺 100% 可用,不如约定一个 99.9% 的 SLO,并把 0.1% 的容忍空间换算成“错误预算”。每次发布新功能,都可能消耗预算;当预算快用完,发布门禁就会拦住高风险部署,逼团队先恢复稳定性。这样,开发速度与系统稳定不再是零和博弈,而是同一张信用卡上的两道约束:想花钱,先看余额。

岗位关联

对 AI 从业者而言,掌握错误预算与发布门禁,意味着你不仅会训练模型,还能设计出“既敢发布又不失控”的软件系统——这是 SRE、MLOps 与平台工程的共同核心能力。

目录
  1. 可视化演示
  2. 错误预算定义
  3. 消耗计量监控
  4. 门禁规则设计
  5. 耗尽应急策略
  6. 自动化集成实践
  7. 协作与文化建设
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示30天窗口内错误预算的消耗过程、发布门禁根据预算状态做出的决策,以及不同发布类型的风险控制,可切换三种模式观察。

本节配有交互动效(进入视口后自动加载,约 3KB)。

错误预算消耗 预算 = 30天窗口 × (1 - SLO),是允许的故障量 实际错误率与预算比例对比得到消耗量 预算 = 43200 × (1 - SLO) 消耗越高,发布门禁越严格。

错误预算定义

一家三口约定:30 天里至少有 27 天能带孩子去公园,剩下 3 天是全家愿意接受的坏天气额度。这里的“27 天”就是 SLO,“3 天”就是错误预算。软件也一样,我们不能要求系统永不犯错,但可以和用户约定一个可接受的下限:比如一个月内 99.9% 的请求都成功。

有了 SLO 之后,错误预算就是“允许系统失败的额度”。它由周期总时长乘以不可用比例得到。比如 30 天的周期,最多允许 0.1% 的时间不可用,算下来就是 43.2 分钟。这段时间里发生的故障、超时、发布事故,都会从预算里扣减。预算变成了一笔可以记账、可以跟踪、可以控制的风险资金。

所以错误预算不是越多越好,它是「可以出错但不会违约」的有限资源。每一次线上事故,都像一场突如其来的暴雨,会从额度里扣掉一整天。

Bbudget=Tperiod×(1−SSLO)\displaystyle B_{\text{budget}} = T_{\text{period}} \times (1 - S_{\text{SLO}})
这个等式的意思是:用周期的“总时长”乘上“用户能容忍的失败比例”,得到的就是你手里可以花的故障额度。
  1. 周期总时长先把 30 天换算成秒,作为预算的“总量盘子”。
    Tperiod=30×24×3600=2,592,000 秒\displaystyle T_{\text{period}} = 30 \times 24 \times 3600 = 2{,}592{,}000\ \text{秒}
  2. 不可用比例用 1 减去 SLO,得到容忍失败的比例。
    1−SSLO=1−0.999=0.001\displaystyle 1 - S_{\text{SLO}} = 1 - 0.999 = 0.001
  3. 两者相乘把比例落到具体秒数上。
    Bbudget=2,592,000×0.001=2,592 秒≈43.2 分钟\displaystyle B_{\text{budget}} = 2{,}592{,}000 \times 0.001 = 2{,}592\ \text{秒} \approx 43.2\ \text{分钟}

消耗计量监控

预算最怕悄悄流失。家里用一块白板记录本月坏天气天数,系统则需要每分钟监控“消耗速率”。只看月底剩余多少往往太晚,SRE 更关心的是:当前错误正在以多快的速度吃掉预算。

消耗速率也叫 burn rate,表示实际消耗速度与计划消耗速度的比值。如果 30 天的预算被 5 天用完,消耗速率就是 6 倍。速率超过 1 意味着系统正在以比计划更快的速度燃烧预算,需要触发告警。为了同时抓住突发和持续问题,SRE 会使用多窗口燃烧告警:短窗口如 5 分钟捕捉瞬时突刺,长窗口如 1 小时确认风险是否持续。

白板上的余额每天都在变,但真正决定要不要收伞的,是暴雨来临的速度。多窗口燃烧告警就像同时看“此刻雨有多大”和“这场雨下了多久”,让我们不会被一场五分钟的阵雨吓到,也不会错过连续三天的小雨。

Rburn=DerrorTwindow\displaystyle R_{\text{burn}} = \frac{D_{\text{error}}}{T_{\text{window}}}
消耗速率等于 1,表示按计划燃烧;等于 6,表示原本想用 30 天消耗的预算,5 天就烧完了。
  1. 错误时长统计窗口内实际不可用的秒数。
    Derror=∑错误事件持续时长\displaystyle D_{\text{error}} = \sum \text{错误事件持续时长}
  2. 窗口时长用来计算速率的观察期。
    Twindow=300 秒(5 分钟)\displaystyle T_{\text{window}} = 300\ \text{秒(5 分钟)}
  3. 相除得速率每秒平均消耗多少故障时间。
    Rburn=DerrorTwindow\displaystyle R_{\text{burn}} = \frac{D_{\text{error}}}{T_{\text{window}}}

门禁规则设计

当余额降到 20% 以下,家里的规则是停止安排新的长途旅行——这就是发布门禁。发布门禁不是一刀切地禁止发布,而是根据剩余预算和风险等级决定:这次变更能不能上生产。

门禁的核心是一个阈值判断:如果剩余预算高于某个风险阈值,放行;低于或等于阈值,拦截。风险越高的服务,阈值也越高。比如核心支付服务可能需要 50% 以上的剩余预算才允许发布,而低风险内部工具可能 10% 就够了。为了不误伤紧急修复,门禁还会设计人工干预通道,让值班负责人能临时豁免。

门禁不是不让出去玩,而是提醒我们别在暴风雨马上要来的时候上高速。通过分段放行和风险分级,团队既能保持发布速度,又能在最危险的时候自动踩刹车。

G={PASS,Bremaining>τriskBLOCK,Bremaining≤τrisk\displaystyle G = \begin{cases} \text{PASS}, & B_{\text{remaining}} > \tau_{\text{risk}} \\ \text{BLOCK}, & B_{\text{remaining}} \le \tau_{\text{risk}} \end{cases}
这道门不是“能不能发布”的裁判,而是“现在发是否划算”的风险计算器。
  1. 剩余预算当前还剩下多少故障额度。
    Bremaining=Bbudget−Dconsumed\displaystyle B_{\text{remaining}} = B_{\text{budget}} - D_{\text{consumed}}
  2. 风险阈值不同服务根据自己的重要程度设定的门禁线。
    τrisk=20%×Bbudget\displaystyle \tau_{\text{risk}} = 20\% \times B_{\text{budget}}
  3. 分段判断超过阈值放行,否则拦截。
    G={PASS,Bremaining>τriskBLOCK,Bremaining≤τrisk\displaystyle G = \begin{cases} \text{PASS}, & B_{\text{remaining}} > \tau_{\text{risk}} \\ \text{BLOCK}, & B_{\text{remaining}} \le \tau_{\text{risk}} \end{cases}

耗尽应急策略

万一预算真的见底,家庭会暂停出游、修好漏水的屋顶,而不是继续冒险。系统也一样:当错误预算被耗尽,团队必须进入应急模式,把“新增功能”切换为“恢复稳定”。

应急策略通常按优先级推进:首先停止所有非必要发布,避免新变更继续扩大故障面;然后由事件负责人牵头定位根因,判断是回滚、降级还是热修;最后在系统恢复稳定后,通过持续低于目标消耗速率的表现,让预算慢慢回血。

预算恢复不是等月底自动刷新,而是让系统跑得比承诺更好。就像暴风雨过后,只有连续几天晴天,家庭才会重新允许孩子去远处的新公园。

Bt+Δt=Bt+(Rtarget−Rburn)×Δt\displaystyle B_{t + \Delta t} = B_t + (R_{\text{target}} - R_{\text{burn}}) \times \Delta t
当实际消耗速率低于目标速率,预算余额会慢慢恢复;反过来,它就继续失血。
  1. 当前余额进入应急时刻的剩余预算。
    Bt=Bremaining(t)\displaystyle B_t = B_{\text{remaining}}(t)
  2. 目标速率不违背 SLO 所允许的平均消耗速率。
    Rtarget=1−SSLO\displaystyle R_{\text{target}} = 1 - S_{\text{SLO}}
  3. 实际速率当前系统正在发生的错误消耗速率。
    Rburn=DerrorTwindow\displaystyle R_{\text{burn}} = \frac{D_{\text{error}}}{T_{\text{window}}}
  4. 时间步长每隔多久结算一次恢复量。
    Δt=监控结算周期\displaystyle \Delta t = \text{监控结算周期}

自动化集成实践

家里可以装一台自动门禁机器人:门锁和白板余额连接,余额不足时新玩具快递自动退回。软件世界里,这台机器人就是 CI/CD 流水线里的自动门禁。

发布流水线在真正的部署步骤之前,增加一个“门禁裁定”步骤。门禁服务读取实时错误预算,再结合 CI 代码检查结果,决定是否放行。如果预算不足,流水线立即失败并通知负责人。除了发布门禁,告警系统也要联动:预算低于阈值时自动建故障单、拉群、路由给值班人;服务出现连续错误时甚至可以自动熔断,拦截新流量。

自动门禁最有价值的地方是“不依赖人眼”。门禁服务不可用时,系统也应默认拦截发布,也就是 fail-closed,宁可拦住一次正常发布,也不要把不可用风险扩散到生产。

Gauto=CCI∧(Bremaining>τ)\displaystyle G_{\text{auto}} = C_{\text{CI}} \land (B_{\text{remaining}} > \tau)
CI 相当于“车辆年检”,预算条件是“油箱还有油”,两个都满足才放行。
  1. CI 结果代码检查、测试、构建是否全部通过。
    CCI∈{0,1}\displaystyle C_{\text{CI}} \in \{0, 1\}
  2. 预算条件剩余预算是否高于门禁阈值。
    Bremaining>τ\displaystyle B_{\text{remaining}} > \tau
  3. 逻辑与两个条件同时成立才为真。
    Gauto=CCI∧(Bremaining>τ)\displaystyle G_{\text{auto}} = C_{\text{CI}} \land (B_{\text{remaining}} > \tau)

协作与文化建设

一次周末暴雨把预算用光后,全家人不互相指责,而是坐下来商量:这个月的目标是否现实?下次要不要把长途旅行安排在天气更稳定的季节?这就是错误预算背后的文化:它把可靠性从技术问题变成了业务决策。

SRE 会和产品、研发一起给不同服务分级,不同服务分配不同的 SLO 和错误预算。复盘时坚持无指责,重点看流程哪里断了、门禁阈值是否合理、监控是否足够早地发现问题。预算耗尽时的决策权也要提前约定:谁能批准紧急热修、谁有权调整门禁阈值、什么时候回到正常发布节奏。

门禁只是机制,真正让机制转起来的是跨团队信任。没有无指责文化,团队会隐瞒错误;没有决策权约定,预算耗尽时会互相踢皮球。错误预算最终不是数字游戏,而是一家人共同制定、共同遵守的天气预报。

∑i=1NBi≤Btotal\displaystyle \sum_{i=1}^{N} B_i \le B_{\text{total}}
多个服务分到的错误预算之和,不能超过整体对外承诺的总预算,就像每个家庭成员报的坏天气天数不能超过全家额度。
  1. 单服务预算每个服务根据自己的重要性分到的故障额度。
    Bi=Tperiod×(1−Si)\displaystyle B_i = T_{\text{period}} \times (1 - S_i)
  2. 求和把所有服务的预算加总。
    ∑i=1NBi\displaystyle \sum_{i=1}^{N} B_i
  3. 总预算约束总和不能大于整体可用预算。
    ∑i=1NBi≤Btotal\displaystyle \sum_{i=1}^{N} B_i \le B_{\text{total}}

AI 应用场景

发布流水线接入错误预算门禁

阶段:部署与发布阶段

解决的问题:传统发布依赖人工查看监控,容易漏判;预算耗尽后仍然发版,导致故障面扩大。AI 平台项目通常模型迭代频繁,发布速度与线上稳定冲突更明显。

具体案例:某在线推荐系统把错误预算 API 接入 CI/CD,每次模型版本发布前自动检查剩余预算。一次广告模型上线导致超时率飙升,预算余额瞬间低于阈值,流水线自动拦截,避免事故扩散到所有用户。

多窗口燃烧告警驱动值班响应

阶段:监控与告警阶段

解决的问题:静态阈值告警太迟钝,故障发生后几分钟才被人工发现。多窗口燃烧告警把错误预算变化变成“雨量计”,让值班人更早介入。

具体案例:某语音识别服务使用 5 分钟和 1 小时双窗口燃烧告警。一个第三方依赖抖动导致错误率快速上升,5 分钟窗口立刻触发高优告警;值班工程师在预算耗尽前完成了降级切换。

预算耗尽后的无指责复盘与目标重定

阶段:改进与迭代阶段

解决的问题:事故结束后团队常常找不到明确责任人,下次仍踩同一个坑。错误预算提供了一个中立的数据坐标系,让产品、研发、SRE 一起讨论目标是否合理。

具体案例:某 SaaS 平台一个月内连续三次发布事故,错误预算见底。团队按门禁规则冻结发布,完成根因修复后,产品负责人与 SRE 重新协商,把核心 API 的 SLO 从 99.99% 调整到 99.95%,换来更平稳的发布节奏。

Python 演示

python42 行
class ReleaseGate:    def __init__(self, slo=0.999, window_days=30):        self.slo = slo        self.window_seconds = window_days * 24 * 60 * 60        self.allowed_error_seconds = self.window_seconds * (1 - slo)        self.remaining_seconds = self.allowed_error_seconds        self.threshold = 0.2 * self.allowed_error_seconds        self.history = []     def consume(self, error_seconds):        self.remaining_seconds -= error_seconds        self.remaining_seconds = max(0.0, self.remaining_seconds)     def allow_release(self):        return self.remaining_seconds > self.threshold     def apply_release(self, estimated_error_seconds, name):        old = self.remaining_seconds        if self.allow_release():            self.consume(estimated_error_seconds)            self.history.append(("PASS", name, old, self.remaining_seconds))        else:            self.history.append(("BLOCK", name, old, self.remaining_seconds)) def main():    gate = ReleaseGate(slo=0.999, window_days=30)    releases = [        (120, "normal feature"),        (400, "campaign"),        (900, "hotfix"),        (700, "small feature"),        (200, "non-critical change"),    ]    for cost, name in releases:        gate.apply_release(cost, name)        decision, _, before, after = gate.history[-1]        print(name, decision, "before=", round(before, 1), "after=", round(after, 1))    remain_ratio = gate.remaining_seconds / gate.allowed_error_seconds * 100    print("remaining budget ratio:", round(remain_ratio, 2), "%") if __name__ == "__main__":    main()
目的:模拟一个 30 天窗口、SLO 为 99.9% 的系统,逐个评估每次发布的预算消耗与门禁裁定结果。
  1. self.allowed_error_seconds = self.window_seconds * (1 - slo)用周期总时长乘以不可用比例,算出错误预算总量。
  2. self.threshold = 0.2 * self.allowed_error_seconds把预算的 20% 设为门禁阈值,低于它就不允许发布。
  3. def allow_release(self):门禁核心判断:当前剩余预算是否高于阈值。
  4. self.consume(estimated_error_seconds)每次发布消耗掉预计的故障秒数。
  5. if self.allow_release():只有通过门禁才会扣减预算;否则记录为 BLOCK。
  6. gate = ReleaseGate(slo=0.999, window_days=30)模拟线上核心服务常用的默认参数。

常见误区

误区错误预算当然是越多越好,最好把 SLO 定得很低,这样出问题也不会被批评。

正解预算大只说明用户能容忍更长时间的不可用,不代表业务目标正确。SLO 应该由用户和业务决定,错误预算的价值在于约束,而不是奖励。如果把预算设成 30 天里允许 27 天坏天气,孩子根本没法好好成长。

为何常见:人们习惯把“预算”理解成可自由支配的钱,钱越多越宽裕;但错误预算本质是“可以出错的额度”,它的存在是为了逼迫团队在风险变大时停下来。

误区SLO 设成 100%,系统就永远不会有预算被消耗,最安全。

正解100% SLO 意味着零错误预算,任何一次抖动都会违反承诺,发布门禁会一直拦截,团队最后不敢发布任何变更。SRE 的实践是选择可量化的高可用目标,并留出预算给发布和实验,就像承诺 30 天全晴天的家庭会被一场小雨逼疯。

为何常见:从直觉上,要求越高系统越可靠,但忽略了成本、发布速度和创新的平衡。

误区预算耗尽说明线上已经出问题了,最稳妥的办法是把所有服务都回滚。

正解预算耗尽的第一步是停止新增风险,而不是盲目回滚。回滚本身也是一次高风险变更,如果目标不明确,反而会造成更大故障。正确做法是先冻结发布,定位根因,再决定是回滚、热修还是降级。

为何常见:把门禁理解成“不行就全退”,忽略了回滚也是一次代码变更,同样需要风险控制。

误区SLO、SLI 听起来很技术,错误预算应该是 SRE 团队内部的指标,和产品、研发无关。

正解错误预算本质是业务决策:目标定多高、预算怎么分、耗尽时谁拍板,需要产品、研发、SRE 一起商定。无指责复盘也不是为了找责任人,而是让所有角色看到系统性的改进点。

为何常见:因为错误预算由监控数据计算而来,容易被误认为纯技术指标,忽略了它对发布效率和用户体验的影响。

面试题

Q1. 某服务月度 SLO 为 99.9%,30 天窗口内大约允许多久的不可用时间?

单选题 · easy

  • A.约 43 分钟← 正确答案
  • B.约 4.3 小时
  • C.约 7.2 小时
  • D.约 432 秒

答案:A。30 天共 2,592,000 秒,乘以 0.1% 得到约 2,592 秒,即约 43 分钟。4.3 小时和 7.2 小时都高估了可用性;432 秒只有 7.2 分钟,低估了容忍范围。这就像 30 天里只有 0.3 天坏天气,而不是 4 天。

Q2. 错误预算公式 \(1 - \text{SLO}\) 代表的含义是什么?

单选题 · easy

  • A.允许故障的比例← 正确答案
  • B.必须成功的比例
  • C.系统预算的余额
  • D.监控采样的频率

答案:A。1 减去 SLO 得到的是用户能容忍的不可用比例,比如 99.9% 对应 0.1%。“必须成功的比例”是 SLO 本身;“预算余额”是后续消耗后的结果;监控频率与公式无关。

Q3. 当剩余错误预算跌破门禁阈值时,发布门禁最合理的动作是什么?

单选题 · mid

  • A.自动拦截高风险发布← 正确答案
  • B.允许所有紧急发布
  • C.忽略预算等待月底
  • D.调高服务 SLO 数值

答案:A。门禁存在的意义就是在预算不足时自动拦截高风险变更,避免故障面扩大。允许所有紧急发布会让门禁形同虚设;忽略预算或临时调高 SLO 都是在掩盖风险。

Q4. 多窗口燃烧告警的主要优势是什么?

单选题 · mid

  • A.兼顾瞬时与持续风险← 正确答案
  • B.只关注平均响应时长
  • C.减少采集日志数量
  • D.替代所有可用性监控

答案:A。短窗口捕捉突发性错误,长窗口确认风险是否持续,二者结合避免误报和漏报。它不是为了减少日志,也不能替代所有监控,双窗口只是错误预算监控中的一环。

Q5. 错误预算耗尽后,团队应当优先执行哪项动作?

单选题 · mid

  • A.冻结非必要的新版本← 正确答案
  • B.立刻回滚全部服务
  • C.删除全部告警规则
  • D.临时调高故障阈值

答案:A。预算耗尽意味着系统已经消耗了过多容忍空间,第一步是停止新增风险,即冻结非必要发布。盲目回滚可能引入新的问题;删除告警和调高阈值都是在逃避问题。

Q6. 自动化发布门禁中,\(G = C_{\text{CI}} \land (B_{\text{remaining}} > \tau)\) 表示什么?

单选题 · mid

  • A.CI 通过且预算充足才放行← 正确答案
  • B.预算耗尽时也照常放行
  • C.CI 失败时也有权放行
  • D.只检查预算不检查 CI

答案:A。逻辑与要求两个条件同时为真:CI 检查通过,同时剩余预算高于阈值。这就像出门既要天气晴朗,又要家庭预算充足,缺一不可。

Q7. 无指责复盘最重要的关注点是什么?

单选题 · easy

  • A.系统流程与改进点← 正确答案
  • B.定位个人责任大小
  • C.隐藏事故相关信息
  • D.无限延长发布冻结

答案:A。无指责复盘的目标是发现流程、监控、门禁中的系统性问题,而不是追责个人。隐藏信息会阻碍改进;无限延长冻结会让业务停滞,不是复盘的目的。

Q8. 关于错误预算的用途,下列说法正确的有哪些?

多选题 · mid

  • A.用来平衡速度与稳定← 正确答案
  • B.可直接作为发布门禁← 正确答案
  • C.可完全替代容量规划
  • D.能帮助排定故障优先级← 正确答案

答案:ABD。错误预算让团队在发布速度和可靠性之间做量化决策,并能作为门禁依据;预算耗尽时也能指导先修哪些故障。但它不能替代容量规划,容量规划关注资源上限,两者维度不同。

Q9. 设计发布门禁规则时,哪些因素应该纳入?

多选题 · hard

  • A.服务自身风险等级← 正确答案
  • B.当前剩余预算比例← 正确答案
  • C.紧急热修的豁免通道← 正确答案
  • D.日志文件编码格式

答案:ABC。不同风险等级的服务需要不同阈值,剩余预算决定当前是否放行,紧急热修需要人工豁免通道,三者共同构成可落地的门禁策略。日志编码格式和门禁风险判断没有关系。

Q10. 关于 fail-closed 门禁设计的正确理解有哪些?

多选题 · extreme

  • A.门禁系统不可用时默认拦截← 正确答案
  • B.避免故障影响同类服务← 正确答案
  • C.保留人工审批兜底通道← 正确答案
  • D.提高每次发布成功率

答案:ABC。fail-closed 的核心是宁可错拦,也不放行,门禁自身故障时默认拒绝发布,从而避免把风险扩散到更多服务;同时保留人工审批作为兜底。它的目的不是提高单次发布成功率,而是降低整体可用性风险。