🔍 登录 注册

SLO 与错误预算 把稳定性变成可交易的额度

Z ZeroIsStart 2026-10-23 👁 0 ⏱ 5min

SLO 与错误预算 把稳定性变成可交易的额度

"我们要做到 99.9%"——这句话没有任何操作性,因为没人回答谁在消耗这个承诺、谁负责、超了怎么办。错误预算的价值在于,它把稳定性从模糊口号变成一份可以花钱的预算:有余量就多发布功能,超支就冻结变更去修可靠性。

一、SLO 必须由 SLI 支撑

三层关系要分清:SLO 是承诺,SLI 是测量方式,错误预算是 SLO 允许失败的量。

SLO:  订单查询接口月度可用性不低于 99.5%
SLI:  好请求数 ÷ 总请求数,其中"好"等于状态码低于 500 且延迟低于 500ms
预算: 30 天 × 0.5%,即约 216 分钟不可用,或等价的坏请求配额

四个实践要求:

  1. SLI 必须站在用户视角测量。服务自己日志里统计的成功率不算,要从入口(网关或前端)测;
  2. 延迟 SLI 用比例阈值型("95% 的请求快于 300ms"),不要用平均延迟;
  3. SLO 数量要少,3 到 7 个覆盖核心用户旅程即可,给每个接口都配 SLO 等于没有 SLO;
  4. 承诺对象是用户而不是上级。写进 SLO 的数字必须能够公开解释。
service: order-api
objectives:
  - name: availability
    sli: good_requests / total_requests
    target: 99.5
    window: 30d
    burn_rate_alerts:
      - { short: 5m,  long: 1h, budget_burn: 14.4 }    # page
      - { short: 30m, long: 6h, budget_burn: 6 }       # page
      - { short: 6h,  long: 3d, budget_burn: 1 }       # ticket
  - name: latency
    sli: "latency below 500ms"
    target: 95
    window: 30d

二、多窗口多速率告警

单窗口告警要么太吵要么太迟钝。正确形态是短窗口确认加长期窗防抖,判据是"预算燃烧速率"。

sum(rate(requests_total{code=~"5.."}[5m])) / sum(rate(requests_total[5m]))
  / (1 - 0.995) > 14.4

为什么是 14.4:30 天窗口下允许 0.5% 失败,若以 14.4 倍速率燃烧,约 2 天就会耗尽整个月预算。

组合规则要同时满足才 page:

5m 窗口 burn > 14.4  AND  1h  窗口 burn > 14.4     # 快速大规模故障
30m 窗口 burn > 6    AND  6h  窗口 burn > 6        # 中速持续消耗
6h  窗口 burn > 1    AND  3d  窗口 burn > 1        # 慢性漏,转工单

这套机制的价值在于:既能在几分钟内叫醒人(短窗口),又不会因为一次抖动就 page(长窗口 AND),还能在预算被慢性耗尽时提前预警,而不是月底才发现预算已经花光。

三、预算视图要足够直白

预算必须有一个所有人都能看到的单一视图,否则不会改变任何行为。

type Budget struct {
    Objective   string
    Window      time.Duration
    Target      float64     // 0.995
    Consumed    float64     // 窗口内已消耗的坏请求比例
    Remaining   float64     // 剩余预算百分比
    DepletionAt time.Time   // 按当前速率预计耗尽时间
}

func (b *Budget) BurnRate(elapsed time.Duration) float64 {
    expected := b.Consumed / (elapsed.Seconds() / b.Window.Seconds())
    allowed := 1 - b.Target
    return expected / allowed
}

看板上最有效的三个数字是:剩余预算百分比(大字号)、预计耗尽时间、当前燃烧速率。它们比任何时序图都更能影响决策。

四、政策:预算花完会发生什么

没有后果的 SLO 是装饰品。政策要事前写死、事后严格执行。一份可抄的最小政策:

剩余 > 50%      正常发布节奏,允许高风险变更
剩余 20~50%     高风险变更需 SRE 会签
剩余 0~20%      仅允许修复类与安全类变更,功能发布暂停
剩余 < 0        特性冻结,本迭代全部投入可靠性工作
                恢复条件:连续 7 天滚动 burn_rate 低于 1

三条执行要点:冻结必须真的执行,第一次超支不冻结,这项制度从此作废;冻结要有明确出口(比如连续 7 天 burn 低于 1),否则会变成无限期惩罚,团队的第一反应就是把 SLO 数字改松;例外走一次性授权,业务必须发布时由负责人书面承担风险并记录,事后回顾。

五、归因:把预算变成排期工具

只报"还剩多少预算"不够,要回答"预算花在哪了",这决定下个迭代做什么。

预算消耗归因(30 天)
  依赖超时(下游 payment-svc 抖动)        38%
  发布相关(3 次发布造成的抖动)           24%
  容量不足(峰值 CPU 打满)                18%
  未捕获 panic                             12%
  其他                                      8%

有了这张表,排期决策变得简单:优先做支付超时兜底,而不是继续扩容。数据来源是把每次故障的预算消耗分钟数记进事故记录并打标签,季度汇总。

六、五个常见反模式

  1. 给内部依赖定 SLO 而不管用户旅程。数据库 99.99% 但用户下单成功率只有 92%,这是一套失败的 SLO 体系;
  2. 数字定得太高。一开始写 99.99% 的团队,三个月后要么改数字要么放弃机制,宁可从 99% 起步稳定后再收紧;
  3. 与个人绩效挂钩。结果一定是把 SLI 测量方式改松(放宽超时阈值、缩小 5xx 判定范围);
  4. 只测服务端不测前端。用户感知的失败中,CDN、DNS、浏览器与弱网占比常被严重低估;
  5. 窗口太短。7 天窗口的 SLO 波动过大,会造成频繁冻结与解冻,团队无法工作。30 天是常见选择,成熟后再看 90 天趋势。

正确的收紧节奏是:季度回顾时看上季度 burn 分布,若长期低于 0.3,下个季度提一档。SLO 是长期承诺,不是当期考核指标。

小结

错误预算制度要跑起来需要五块拼齐:用户视角的 SLI、少而关键的目标、多窗口多速率告警、事前写死且有出口的政策、预算消耗归因。前三者是技术工作,后两者是组织约定,真正的难点在这里——政策必须被执行一次,机制才有信用。落地建议从一个核心用户旅程起步,跑两个完整预算周期,把冻结与归因各执行一次,机制立住之后再扩展到其他目标。

读完了?给作者一点支持

本文采用 CC BY 4.0 协议转载 · © ZeroIsStart

评论(0)

登录 后参与评论。

  • 还没有评论,来抢沙发。

🔗 相关文章