SLO:把可靠性变成数学

上一篇,可观测性的四类信号——指标、日志、追踪、持续分析——全部就位了。你现在能看见系统的一切:多少、发生了什么、为什么慢、时间花在哪。但有一个最基本的问题,前面七篇一直没正面回答:

到底什么算”可靠”?什么程度的故障,值得在半夜叫醒一个人?

监控时代的答案是拍一个阈值——CPU 超 90%、错误率超 5%。但 03 我们就追问过:“5% 凭什么?” 这一篇要回收这个伏笔。SLO(Service Level Objective)给出的答案,是把可靠性从一个感性的、靠资历拍板的判断,变成一套数学——一套能算、能协商、能自动给告警分级的数学。这是云原生范式”工程化”子带的第一篇:信号都有了,现在学着用它们做决策。

一、可靠性的悖论:越多越好吗

信号齐了,但”可靠”还没定义

四类信号让你看得见系统,但看得见不等于知道该不该担心。错误率 0.3%,是事故还是正常波动?延迟 P99 涨了 50 毫秒,要不要叫人?没有一个明确的”什么算好”,再丰富的信号也只是一堆等待解读的数字。SLO 要做的,就是给”好”下一个可计算的定义。

100% 的陷阱

直觉会说:可靠性当然越高越好,目标就是 100%。这是个陷阱,而且是三重的:

  • 不可能:任何真实系统都依赖会坏的硬件、会抖的网络、会出 bug 的代码。100% 在物理上不存在。
  • 不经济:从 99.9% 到 99.99%,再到 99.999%,每多一个 9,成本指数级上升(冗余、多活、更复杂的架构)。最后那个 9 的钱,可能够你做十个新功能。
  • 没必要:你的用户根本感知不到 99.99% 和 100% 的区别——因为用户自己的网络、设备、运营商的不可靠,早就远大于这点差距。你把可靠性做到超过用户能感知的程度,那部分投入对用户是隐形的。

所以”越高越好”是错的。正确的问题不是”怎么达到 100%“,而是”多可靠才够”。

SLO 的根本动作:给可靠性设一个数字

SLO 的全部出发点,就是回答”多可靠才够”——给可靠性设一个明确的、低于 100% 的数字目标。 99.9%、99.95%、99.99%,挑一个。

这个看似简单的动作,是整篇文章一切后果的源头。因为一旦你说”目标是 99.9%“,你就同时、不可避免地承认了另一件事:剩下的 0.1%,是被允许的。 而”被允许的不可靠”一旦被量化,就变成了一个可以管理、可以消费的东西——这就是 error budget,第三章的主角。先把三件套理清楚。

二、三件套:SLI / SLO / SLA

SLO 周围有三个长得像、却常被搞混的缩写。理清它们,是理解后面一切的前提。

SLI:实测的可靠性

SLI(Service Level Indicator),是你实际测量到的可靠性——一个比例:

SLI = 好的事件数 / 总事件数
 
例:可用性 SLI = 成功的请求数 / 总请求数
    延迟 SLI   = 快于 300ms 的请求数 / 总请求数

SLI 是事实,是从 指标里算出来的客观数字。注意它通常是个比例(成功占比、达标占比),而不是一个绝对值——这让它天然落在 0% 到 100% 之间,可以和目标直接比较。

SLO:你给自己定的目标

SLO(Service Level Objective),是你给 SLI 设定的内部目标

SLO:可用性 SLI ≥ 99.9%(每个滚动 30 天窗口)

SLI 是”实际多少”,SLO 是”应该多少”。SLO 是你团队内部的承诺,是用来驱动工程决策的——它不对外,没有法律效力。它的唯一作用,是给”够不够可靠”提供一条线。

SLA:对客户的法律承诺

SLA(Service Level Agreement),是你和客户签的合同条款,承诺一个可靠性水平,达不到要赔偿(退款、补偿):

SLA:若月度可用性 < 99.5%,按比例退还服务费

SLA 是对外的、有法律和金钱后果的。

三者层层放松

关键的关系是:SLA 松于 SLO,SLO 严于 SLI 的底线。

内部目标 SLO (99.9%)  ──严于──>  对外承诺 SLA (99.5%)

为什么 SLO 要比 SLA 更严?因为你需要缓冲。如果你内部目标和对外承诺是同一个数(都 99.5%),那么你一旦触及内部目标,就已经在违约边缘了,没有任何反应时间。把 SLO 设得比 SLA 更高(99.9% vs 99.5%),意味着当你开始消耗内部预算时,离真正的违约还有很大余量——SLO 是你违约之前的预警线。SLA 是悬崖,SLO 是悬崖前的护栏。

怎么定义有意义的 SLI

最后一个、也最容易做错的问题:该测什么作为 SLI?

错误的做法,是从系统内部找指标——CPU、内存、磁盘。这是 监控范式的惯性,但 CPU 高低和用户体验没有可靠的对应关系(01 维度四早就说过)。

正确的做法,是从用户旅程出发:用户来你的服务,是为了完成某件事——加载页面、提交订单、看到搜索结果。SLI 应该测量这件事成没成、快不快。“结账请求的成功率”、“搜索响应快于 500ms 的占比”——这些才是有意义的 SLI,因为它们直接是用户的体验。SLI 要面向用户的目标,不是面向机器的状态。 这个视角转换,是 SLO 整套体系能反映真实可靠性的根基。

三、error budget:可靠性变成一笔预算

现在来到 SLO 最深刻、也最反直觉的发明。它把前面那个”被允许的不可靠”,变成了一种可以管理的资源。

99.9% 的另一面

盯着”99.9%“看久一点,你会看见它的另一面:它在明确地宣告,0.1% 的不可用是被批准的、计划之内的、不需要愧疚的。

这 0.1%,就是 error budget(错误预算)——一段时间内,你”被允许”消耗掉的不可靠额度。它不是失败,是预算。就像一笔钱,你本来就可以花。

算一笔账

把这个百分比换算成具体时间,它会变得很有体感。以 30 天为窗口:

SLO允许不可用月度停机预算日均预算
99%1%~7.2 小时~14 分钟
99.9%0.1%~43 分钟~1.4 分钟
99.95%0.05%~21 分钟~43 秒
99.99%0.01%~4.3 分钟~8.6 秒

看这张表,“多一个 9”的代价立刻具体了:从 99.9% 到 99.99%,你的月度容错空间从 43 分钟压缩到 4 分钟——这意味着一次稍微长一点的部署事故,就能烧光你整个月的预算。这就是 1.2 说的”每个 9 指数级昂贵”的数字版。

预算的用法:一个可以花的东西

error budget 真正的威力,在于它把可靠性变成了一个可以主动消费的决策工具

  • 预算充足时:尽管发布、尽管上风险高的新功能、尽管做激进的实验。你有 43 分钟的容错空间,没烧完就是浪费——追求过高的可靠性,等于把本可以用来迭代的预算闲置了。
  • 预算告罄时:自动触发”发布冻结”——停掉所有非必要的变更,全员转向稳定性,把预算还回来,直到下个窗口预算重置。

这套机制把一个长期争论变成了一条自动规则:“我们现在该求快还是求稳?“——看预算余额。 不是看谁嗓门大,看数字。

它化解了 dev 与 ops 的世仇

这里藏着 SLO 最深远的组织意义。传统上,开发团队想(多发布、多上功能),运维团队想(少变更、别出事)——这是一对结构性的、永恒的冲突,因为他们的 KPI 天然对立。

error budget 把这对冲突,变成了一场两边看着同一个数字的对话。可靠性不再是 ops 的道德高地,也不再是 dev 眼中的绊脚石——它是一笔共享的预算。预算够,dev 有理由快;预算光,ops 有理由喊停。可靠性第一次成了一种可量化、可交易的工程资源,而不是一场谁也说服不了谁的价值观争吵。 这是 SLO 在 "人与资源"这条线上最漂亮的一击——它给”人该花多少力气在稳定性上”这件事,装了一个客观的调节阀。

四、重新定义告警:从原因到 burn rate

有了 error budget,告警可以被彻底重写。这一章,我们终于回收那个追了三篇的伏笔。

回收”5% 凭什么”

03 里我们写下 错误率 > 0.05 时,追问过”这个 5% 凭什么?” 现在有答案了:5% 不该凭任何拍脑袋的直觉,它该从 error budget 的消耗速度反推出来。

正确的问题不是”错误率多少算高”,而是”按当前的错误率,我的 error budget 会多快烧光?” 如果按现在的速度,整月的预算 1 小时就烧没——那不管错误率是 5% 还是 0.5%,都该立刻告警。如果按现在的速度,预算要三天才烧完——那即便错误率看着吓人,也不必半夜叫人。判据从”原因的绝对值”,换成了”预算的烧损速度”。

原因告警 vs 症状告警

这正是 01 维度四那条线的兑现。两种告警哲学:

  • 基于原因(cause-based):CPU>90% 告警、磁盘>80% 告警。问题是 CPU 高不一定伤用户,用户受伤也不一定 CPU 高——双向失准(02 讲过)。
  • 基于症状(symptom-based):error budget 在以异常速度燃烧时告警。它直接就是用户体验的恶化,没有”原因到影响”之间那层不可靠的推断。

Google SRE 的结论很硬:只对症状告警,对原因不告警。 原因(CPU、内存)留作故障时的诊断线索,但不用它来触发对人的打扰。因为人的注意力是 最贵的稀缺品,只该为”用户真的受影响了”而消耗。

burn rate:烧损速率

把”烧得多快”量化,就是 burn rate(燃烧率)——error budget 的消耗速度,以”匀速烧完”为基准:

burn rate = 1    →  正好匀速燃烧,整个窗口结束时预算刚好用完(健康)
burn rate = 2    →  两倍速,预算会在半个窗口(15 天)时烧光
burn rate = 14.4 →  1 小时就烧掉整月预算的 2%(按此速度约 2 天清零,紧急!)

14.4 这个数不是魔法:它来自 Google SRE 的经典快速燃烧阈值——“1 小时内消耗掉整月预算的 2%“。匀速燃烧时,1 小时只该消耗整月预算的 1/720(约 0.14%);真烧到 2%,正好是 14.4 倍速。听起来 2% 不多,但照这个速度不到两天预算就清零,所以值得立刻 page。(顺带一提:真要”1 小时烧光整月预算”,burn rate 得到 720——那是灾难级。)burn rate 把”现在有多严重”翻译成了一个干净的倍数——它衡量的不是错误的大小,而是错误吃预算的速度,而后者才真正决定你还剩多少反应时间。

多窗口多燃烧率

但单看一个 burn rate 会出问题:只看短窗口(比如 5 分钟),一次瞬时抖动就会让 burn rate 飙到很高,引发误报;只看长窗口(比如 24 小时),一次急性大故障要拖很久才触发,太迟。Google SRE 的解法是多窗口多燃烧率(multi-window, multi-burn-rate)

  • 快窗口(如 1 小时)+ 高 burn rate 阈值:抓急性故障——短时间内疯狂烧预算,立刻 page。
  • 慢窗口(如 6 小时)+ 低 burn rate 阈值:抓慢性渗漏——不剧烈但持续地漏,慢慢累积成大问题,开工单处理。
  • 关键技巧:要求两个窗口同时超过阈值,才真正告警。短窗口负责”快速响应”,长窗口负责”确认这不是一次转瞬即逝的抖动”——两者一起,既快又准,大幅压低误报。

★ 故障剧场:同一套规则,自动分级

把这套机制的精妙之处,放到两个真实场景里看——同一套 burn rate 规则,不用人工判断,自动给出了完全不同的反应。

  • 场景一:凌晨 3 点,一次网络抖动让错误率在 90 秒内尖刺了一下,烧掉了 0.01% 的预算。短窗口的 burn rate 短暂飙高,但 90 秒后恢复,长窗口的 burn rate 根本没起来——两窗口没同时超标,不告警。 那个值班工程师,睡得好好的。这正是对的:一次自愈的瞬时抖动,不值得叫醒人。
  • 场景二:下午 2 点,一次坏部署让结账错误率稳定在 30%,预算正被疯狂消耗。短窗口 burn rate 瞬间冲过 14.4(一小时就烧掉整月预算 2% 以上、照此速度两天内清零),几分钟后长窗口也确认了——两窗口同时超标,立刻 page。 工程师在用户大规模受影响之前就被叫到了。

没有人去调阈值、去判断”这次算不算严重”——规则自己根据”预算烧多快”做了精确的分级。 这就是把”何时打扰人类”这个 最古老的运维判断,真正变成了数学。

五、实践与边界

落地:用 PromQL 算 SLI 和 burn rate

理论落到工具。SLO 几乎总是建立在 Prometheus 指标之上——这也是前面说”SLI 从指标算出来”的兑现:

groups:
  - name: checkout-slo
    rules:
      # 1. 先把 SLI(好请求占比)算成 recording rule
      - record: job:slo_errors:ratio_rate1h
        expr: |
          sum(rate(http_requests_total{job="checkout", status=~"5.."}[1h]))
            / sum(rate(http_requests_total{job="checkout"}[1h]))
 
      - record: job:slo_errors:ratio_rate6h
        expr: |
          sum(rate(http_requests_total{job="checkout", status=~"5.."}[6h]))
            / sum(rate(http_requests_total{job="checkout"}[6h]))
 
      # 2. 多窗口多燃烧率告警:SLO=99.9%,预算=0.001
      #    14.4 倍 = 1 小时烧掉整月预算的 2%(SRE 经典 page 阈值),要求 1h 和 6h 两窗口同时超标
      - alert: CheckoutErrorBudgetBurnFast
        expr: |
          job:slo_errors:ratio_rate1h > (14.4 * 0.001)
            and
          job:slo_errors:ratio_rate6h > (14.4 * 0.001)
        labels:
          severity: page
        annotations:
          summary: "结账 SLO 预算正在以 ≥14.4x 燃烧(1 小时已烧掉整月预算 2% 以上)"

读这段,4.4 的全部设计都在里面:两个不同窗口的 SLI(1h / 6h)、burn rate 阈值(14.4 * 预算)、以及那个关键的 and——两窗口同时超标才告警。把它和 02 的 Nagios 阈值、[[03-指标-时间序列的量化哲学|03 的 > 0.05]] 并排,你能清楚看到告警哲学这一路的进化:从”对原因拍一个绝对阈值”,到”对症状按预算烧损速度自动分级”。

边界一:SLO 说”是否异常”,不说”为什么”

SLO 是个强大的判断工具,但它只判断、不解释。当 burn rate 告警响起,SLO 告诉你”用户正在显著受影响、而且很急”——但它完全不知道为什么。为什么错误率涨了?是哪个版本、哪类用户、哪一跳?

这时候你要回到 可观测性:用宽事件下钻、用 追踪定位、用 持续分析找代码热点。SLO 定义”什么算正常”并决定”何时叫人”,可观测性回答”为什么不正常”——这是又一次清晰的分工。SLO 是告警的扳机,可观测性是扣下扳机之后的破案工具。

边界二:SLO 是社会契约,不只是技术

SLO 看起来全是数学,但它最难的部分根本不是数学,是:99.9% 还是 99.95%,谁有权决定?预算烧光了,真的敢冻结发布吗——还是被业务压力逼着继续发?SLA 违约的赔偿,谁来背?

这些都不是技术问题,是组织契约问题。一个没有管理层背书的 SLO,预算一烧光,“冻结发布”的规则就会被一句”这次先特批”架空,整套机制随之失效。SLO 的数学是容易的,难的是让一个组织真的按这套数学的结论去行动。 这部分——谁定、谁担责、怎么落地——属于运维的组织面,见 MOC 第五节「运维的人与经济」。

选 SLO 的纪律:少即是多

最后一条实践纪律:不要给每个指标都设 SLO。 给数据库连接数、给 CPU、给每个内部接口都设 SLO,你会得到几十个 SLO、几十个 burn rate 告警——然后回到 告警疲劳的老路。

SLO 的价值在于聚焦:只为少数几个用户真正在意的关键旅程(登录、下单、支付、搜索)设 SLO。这几个守住了,用户就是满意的;这几个之外的波动,用可观测性去观察、但不要用它来叫醒人。SLO 的数量,应该少到你能一口气说完。 多则滥,滥则废。

六、小结:坐标与去向

SLO 在五维标尺上的坐标

那把尺子给 SLO 定位。和前几篇不同,SLO 不是一类新信号,它主要拨动的是第四根标尺

维度SLO 的作用
① 故障预判权不直接涉及
② 存储汇率复用指标,无新增
③ 信号分工不产生新信号,而是给信号定义”好坏”
④ 注意力阀门核心——从”对原因拍阈值”升级到”对症状按 burn rate 自动分级”
⑤ 仪器化负担复用已有指标,几乎零新增

前面七篇都在扩展”看得见什么”(①②③⑤),SLO 是第一篇专门去拨动”何时该打扰人”(④)的文章。它把这根从 监控时代就最粗糙的标尺,第一次校准到了”精确对应用户影响”。那个贯穿全系列的问题——SLO 把天平推向了哪边?它把”人的注意力”这一端,校准到了最省的位置:只为真正伤害用户、且足够紧急的事,才花。

去向

可靠性数学化之后,“工程化与标准化”子带还剩两篇:

  • 四类信号、SLI、burn rate……这一切数据,都要先被采集上来。但每类信号一套 SDK、每个后端一套格式,仪器化既重又容易被厂商锁死。能不能一次插桩、喂养所有信号、后端随意更换?→ 09-OpenTelemetry-三个信号的统一语言
  • SLO 的组织面——谁定目标、谁为预算负责、冻结发布的纪律如何落地——→ MOC 第五节「运维的人与经济」。

下一篇,我们从”用信号做决策”退回到一个更基础的问题:这些信号本身,是怎么被高效、统一、不被锁死地采集上来的?答案是过去十年可观测性领域最重要的标准化运动——OpenTelemetry。

返回 可观测性与运维工程 MOC | 上一篇 07-持续分析-时间花在哪里的第四类信号 | 下一篇 09-OpenTelemetry-三个信号的统一语言