SRE 工程实践:从 SLI/SLO 到错误预算的方法论

本文详细介绍SRE工程实践中的SLI/SLO与错误预算方法论,包括如何定义用户可感知的可用性指标、如何设定合理且可执行的可靠性目标,以及如何利用错误预算驱动发布和稳定性决策,帮助工程师避开落地过程中的常见误区,将可靠性管理真正融入日常开发与运维。

传统可用性为什么不够用?

很多负责线上系统的团队都会遇到这样一个时刻:监控面板上挂着几十个指标,从CPU、内存、请求数到错误率,每个看着都正常,但用户投诉却越来越多。是监控数据错了吗?不一定,而是缺少一把能衡量“服务到底够不够好”的尺子。传统可用性通常用系统运行时间或整体请求成功率来表示,粒度太粗,无法反映用户实际体验。

AI technology illustration

一个典型场景:某电商核心链路依赖第三方渠道,第三方超时增多导致支付完成率下降,但后端各应用实例并没有宕机。从传统可用性看,系统是99.95%可用的;从用户视角看,10笔交易里可能有2笔最终失败。这种落差不是监控不够,而是因为传统指标没有站在用户完整请求链路上定义质量。

SLI:拿什么来衡量系统质量?

SLI(Service Level Indicator)是一个可量化的服务质量指标。它不是拍脑袋想出来的,而是从用户可感知的行为中提取出来的。好的SLI要能被持续观测,并且当服务变差时有明显变化。

常见的几类SLI包括:

  • 请求成功率:成功的请求数除以总请求数,通常按HTTP状态码或业务状态判断。
  • 延迟满足:例如95%的请求延迟在200ms以下,而不是平均延迟。
  • 数据持久性:写入的数据能够被成功读取,常用于存储系统。
  • 批处理新鲜度:数据离上次成功更新过去多久,适合离线计算链路。

定义SLI时,一个容易被忽略的问题是噪声流量。健康检查、爬虫、预发布环境的请求都会污染指标。比较稳妥的方式是在指标层面排除非用户流量,或者单独建立观测标签。另一个常见误区是直接使用平均延迟。平均延迟会被少数慢请求大幅拉高,而且很难设定有效的告警阈值。

一个简单的计算示例

假设你想计算最近一个统计周期内的成功率,可以按下面这种方式处理:

def compute_sli(events):
    valid = 0
    total = len(events)
    for ev in events:
        if ev.status_code < 500:
            valid += 1
    return valid / total

生产环境很少用Python逐条处理日志,更常见的是用Prometheus这类指标系统聚合。成功率可以写成这样的PromQL:

sum(rate(http_requests_total{status_code!~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))

但不管用哪种实现,你需要先确定成功条件的边界。代码永远只是落地形式,SLI定义才是核心。

SLO:把可靠性定在一个具体目标上

SLO(Service Level Objective)是对SLI设定的目标值。它回答的是“我们允许服务在什么范围内波动”。很多人以为SLO越高越好,事实恰恰相反。把目标设成100%,意味着你必须在成本、速度和可靠性之间做出极端取舍,这在绝大多数业务里都不现实。

设定SLO前,至少需要做三件事:观察历史数据,让团队对当前水平有共识;梳理故障影响,明确不可用带来的业务代价;再与业务方讨论能接受的体验底线。观察期建议至少2到4周,否则很容易被偶发因素带偏。

下面这张表列出了不同SLO对应的理论年故障时长,方便建立直觉。

SLO 年故障时长 典型场景
99% 约3.65天 内部工具、无SLA约束
99.9% 约8.77小时 大多数线上业务
99.99% 约52.56分钟 支付、实时通信核心链路
99.999% 约5.26分钟 基础设施控制面、跨地域容灾

这个换算经常被引用,但不能机械套用。年故障时长是理论值,真实故障的影响并不均匀。比如在促销高峰期中断10分钟,和凌晨中断10分钟,对用户的影响完全不同。所以SLO只是目标,无法替代业务判断。

错误预算:让可靠性成为可消耗的资产

错误预算就是1减去SLO。假设SLO为99.9%,错误预算是0.1%。如果按一个季度计算,大约有13分钟的可容忍失败时间。这个预算被消耗掉以后,系统就不应该再进行有风险变更,而应该把资源投入到稳定性建设上。

这个机制最有价值的地方,是把“可靠性”从一句口号变成了产品决策的硬约束。每次发布新功能,本质上是消耗错误预算来换取产品进展。预算还剩多少,直接决定团队能做多激进的操作。

举一个实际场景:一个后端服务定义SLO是99.95%,季度错误预算约22分钟。团队在发布流程中加入了预算检查。当错误预算剩余不足10%,且当前消耗速率超过预测值时,发布会自动被暂停,然后由稳定性专项小组接手。过去这个决定要运维负责人拍板,现在数据自动给出答案。

如何估计预算消耗速度

你可以在看板上展示“按当前消耗速率,预计多久耗尽”。用一段简单的伪代码描述计算逻辑:

def estimated_exhaustion(budget_left, burn_rate):
    if burn_rate <= 0:
        return None
    return budget_left / burn_rate

这里burn_rate的单位是“每小时消耗的错误预算百分比”。如果预测时间小于某个阈值,比如8小时,就触发风险变更暂停。阈值取决于团队对响应时间的容忍度,没有统一标准。

错误预算和告警不是一回事

很多团队误以为SLO违反就应该立即告警。实际上,SLO是一个长期目标,短期突破不一定是事故。比如在非核心时段,某接口错误率短暂上升到0.2%,而SLO是99.9%,但如果持续时间很短,总预算并没有明显受损,这时候频繁告警反而会让团队麻木。

更有效的做法是基于消耗速率告警,而不是基于阈值告警。你可以设置两级:当错误预算消耗速率超过预测值的1.5倍并持续30分钟,触发一般告警;当按当前速率会在24小时内耗尽预算,触发紧急告警。这样既不会漏掉真正的风险,又避免了告警疲劳。

落地SLI/SLO需要避开的几个坑

在实践过程中,最容易出现问题的往往不是概念理解,而是执行方式。

  • SLO定得太高或直接照搬同行。 每个系统的用户期望和成本结构都不同。无先例时,先观察再调整。
  • SLI定义太多。 如果每个指标都成为SLO,团队会失去优先级。核心服务控制在3个以内。
  • 忽略长尾延迟。 平均延迟无法暴露少数高延迟请求的体验问题,建议用95分位或99分位。
  • 错误预算用完却没有任何动作。 预算归零时没有响应规则,跟没做SLO没区别。
  • 用SLO考核个人绩效。 SLO描述的是系统行为,不是个人指标。一旦和奖金挂钩,团队会因为害怕暴露问题而隐藏数据,反而破坏可靠性建设。

从一条核心链路开始落地

如果团队还没有任何SLI/SLO体系,不要试图一次覆盖所有服务。从一条最核心的用户链路开始,通常是最有共识的一环。

  1. 梳理核心用户旅程,找到一个能代表整体体验的请求类型。
  2. 为该请求定义1到2个SLI,例如成功率和延迟分位数。
  3. 观察2到4周数据,结合故障历史来设定SLO,避免拍脑袋。
  4. 搭建错误预算看板,实时展示剩余预算和消耗趋势。
  5. 把错误预算检查加入发布流程,至少做到半自动化。

示例中的“核心链路”不一定是请求量最大的接口,而是用户价值最高的接口。比如电商的“提交订单”、视频平台的“播放启动”、数据平台的“查询结果返回”。

多服务依赖下的整体可靠性

一个用户请求往往需要经过多个微服务。如果两个服务的SLO都是99.9%,理论上整条链路的可用性约为99.8%。这意味着在核心链路上,不能给所有服务都定同样的高SLO,否则整体目标无法达成,错误预算也会被放大。

比较好的做法是把服务分成两层:核心链路中的服务用严格SLO,周边服务用宽松SLO,甚至只观测不承诺。然后通过链路追踪数据确定整体SLO的分配。这里没有标准答案,但你可以从“用户可接受的整体失败率”倒推每个服务的允许失败率。

写在最后

SLI/SLO和错误预算并不会直接让系统更稳定,它带来的是一个持续反馈的框架。当错误预算用完,你知道该把精力花在稳定性上;当预算充足,团队可以更有信心地尝试新功能。这种共同语言,比任何运维制度都更能帮助团队建立可持续的可靠性文化。

如果你所在团队正在准备落地SRE实践,我的建议是不要追求复杂的平台,先从一个服务、两个指标和一块看板开始。真正重要的不是工具,而是团队是否愿意诚实地看待质量,并为质量分配必要的预算。

原创文章,作者:fudengji,如若转载,请注明出处:https://fudengji.cn/article/675/

(0)
上一篇 4小时前
下一篇 4小时前

相关推荐