第4章 告警
基于 SLO 的告警体系
章节导读
凌晨三点,值班工程师的电话响了。他睡眼惺忪地打开笔记本,看到告警系统里躺着 217 条未读告警:CPU 使用率高、内存使用率高、磁盘 IO 慢、某个进程重启了三次、三个不同服务的延迟超标、还有一条数据库连接池告警。这些告警之间是什么关系?哪个是根因?哪个需要立即处理?他花了四十分钟梳理,最后发现问题只是上游一个服务发布导致的短暂抖动,早已自行恢复。而在这四十分钟里,一条真正重要的告警——支付成功率持续下跌——被埋在噪音里无人理会。这就是「告警风暴」的典型场景,也是无数运维团队的日常。Google 在蓝皮书第 4 章中直面这个问题,给出了系统性的解法:基于 SLO 的告警体系。要理解这个体系,先要理解告警的本质目的。告警不是「系统状态的广播」,而是「需要人类智能介入的信号」。Google 对合格告警的定义极为严格:每一条发出的紧急告警,都应该是可操作的(actionable)——收到告警的人必须能够且需要采取某种智能行动;都应该是紧迫的(urgent)——这个行动必须立即做,不能等到早上上班;都应该是真实的(real)——它代表用户正在或即将受损,而不是系统内部的正常波动。用这个标准衡量,大多数组织的告警系统都不及格:告警数量庞大、误报率高、真正的事故反而被淹没。Google 引用的行业数据触目惊心:许多运维团队收到的告警中,只有不到 4% 需要真实的紧急行动;一些团队的值班工程师每晚被叫醒多次,其中大部分醒来后发现什么都不用做。这种状态的危害不仅是睡眠剥夺:它会训练出「告警疲劳」——人对重复的假信号会逐渐麻木,等狼真的来了,也没人相信了。基于 SLO 的告警体系的核心思想是「从症状出发,而非从原因出发」。传统告警监控原因指标:CPU、内存、磁盘、进程状态。这些指标的问题在于与用户受损的相关性弱——CPU 90% 但服务响应正常的情况很常见,CPU 30% 但服务已经挂了的情况也存在(比如死锁)。症状告警直接监控用户体验:请求成功率、延迟、服务可用性。症状告警的好处是每条都直接对应「用户正在受损」,天然满足可操作性和紧迫性。但仅有症状告警还不够——等可用性 SLI 跌破 SLO 才告警,错误预算可能已经烧掉一大半。这就引出了 Google 告警体系最精妙的设计:多窗口多燃烧率告警(Multi-Window Multi-Burn-Rate Alerts)。燃烧率(burn rate)是错误预算的消耗速度:燃烧率 1 表示恰好按 SLO 允许的速度消耗预算,燃烧率 14.4 表示以 14.4 倍速度消耗(30 天预算 2 天烧完)。多窗口多燃烧率告警的思路是:为不同的燃烧速度配置不同的检测窗口组合。高速燃烧(如 14.4 倍)用短窗口(5 分钟 + 1 小时)快速检测,立即电话叫醒值班;中速燃烧(如 6 倍)用中窗口(30 分钟 + 6 小时),发紧急消息;低速燃烧(如 1 倍)用长窗口(2 小时 + 24 小时),建工单白天处理。双窗口的作用是去噪:短窗口灵敏但易误报,长窗口稳健但迟钝,只有两个窗口同时超标才触发告警,兼顾了速度与准确性。这套方法在 Google 内部经过大规模验证,被证明能把告警数量减少一个数量级,同时缩短真实事故的检测时间。本章还将讨论告警的完整生命周期管理:告警分级(P1 电话叫醒 / P2 立即处理但不叫醒 / P3 工单跟进)、告警路由(发给谁、如何升级)、告警文档(每条告警必须链接到值班手册的对应条目)、告警测试(如何验证告警在真实故障时会触发——第 10 章的混沌工程是终极测试手段)、告警审查(定期统计每条告警的「行动率」,长期无行动的告警删除或降级)。Google 有一条铁律:如果一条告警在过去一个月里没有导致任何人工行动,它就不配继续以紧急告警的形式存在。告警体系的终极目标可以用一句话概括:让值班工程师的每一次被叫醒都值得。这不仅是工程效率问题,更是对人的尊重——第 5 章讨论值班时会看到,告警质量直接决定了值班的可持续性和团队的士气。一个告警泛滥的团队留不住好工程师,而一个告警精准的系统让值班从折磨变成了可接受的职业责任。接下来的要点将深入每个技术细节:四种告警类型的划分、多窗口多燃烧率的数学原理、PromQL 实战、告警疲劳的量化治理,以及一个从告警风暴到精准告警的完整改造案例。
核心概念
- 多窗口多燃烧率
- 告警疲劳
- 症状告警
- PromQL
要点精析
告警的哲学:可操作、紧迫、真实。Google 对紧急告警设定了三条铁律,每条都值得展开。可操作性(Actionable):收到告警的人必须能够采取某种智能行动——重启服务、回滚发布、扩容、切换流量、启动降级。如果一条告警收到后唯一合理的反应是「知道了,等它自己恢复」,它就不应该是紧急告警,而应该降级为日志或仪表盘信息。紧迫性(Urgent):行动必须立即执行,拖延会导致损失扩大。「磁盘将在 30 天后写满」是重要信息但不是紧急告警,一张工单足矣;「磁盘将在 2 小时内写满导致服务崩溃」才值得叫醒人。真实性(Real):告警代表用户正在受损或即将受损,而不是系统内部的正常波动。计划内的滚动升级会让实例逐个重启,如果每次重启都告警,值班工程师很快会学会无视这类噪音,等真正的实例异常重启时反而分辨不出。这三条铁律的实践检验方法是「行动率审计」:定期统计每条告警触发后实际导致人工行动的比例。Google 的经验阈值是:行动率低于 10% 的紧急告警必须整改——要么修复其检测逻辑减少误报,要么降级为非紧急通道,要么直接删除。一个反直觉但重要的原则是「宁缺毋滥」:漏掉一条告警的代价是事故响应慢几分钟(可以用用户反馈、巡检等其他机制兜底),而告警疲劳的代价是整个告警体系的公信力崩溃——当值班工程师开始条件反射地关闭告警时,系统等于没有监控。治理告警数量的目标值:Google 建议每个值班工程师每班(12 小时)收到的紧急告警不超过 2 条,超过这个数说明告警体系需要瘦身。
基于 SLO 的告警:从症状出发而非从原因出发。症状告警与原因告警的区别是告警设计的第一分水岭。症状(symptom)是用户可感知的服务劣化:请求失败率上升、延迟变长、数据不新鲜。原因(cause)是系统内部的状态异常:CPU 高、内存满、进程崩溃、队列堆积。为什么症状优先?因为症状与用户受损直接对应——请求失败率上升必然意味着有用户受影响,告警必然可操作;而原因与用户受损的关系是或然的——CPU 95% 但服务正常的情况很常见(可能只是批处理任务在跑),进程崩溃但被自动拉起用户无感知的情况也常见。基于原因的告警天然误报率高,是告警疲劳的主要来源。基于 SLO 的症状告警还有一个结构性优势:告警阈值不再是拍脑袋定的魔法数字,而是从 SLO 和错误预算推导出来的。传统告警问「错误率超过多少算异常?」——答案往往是「5%?10%?谁知道呢」;SLO 告警问「当前的错误消耗速度会不会烧穿预算?」——答案是精确可计算的。这让告警体系与服务的质量承诺直接挂钩:SLO 调整,告警自动跟着调整,不存在「SLO 改了但告警阈值忘了同步」的脱节。原因指标并非无用,它们的正确角色是「诊断辅助」:症状告警把值班工程师叫醒,工程师打开仪表盘,通过 CPU、内存、队列、依赖状态等原因指标定位根因。也就是说,原因指标服务于告警之后的人工排查,而不是告警本身的触发。少数例外情况允许原因告警:某些原因指标是「即将发生的症状」的可靠前兆且没有更早的症状信号,比如「磁盘将在 2 小时后写满」——这类告警本质上是预测性症状告警,应该保留但严格控制数量。
告警的四种类型:可用性、延迟、错误预算燃烧率、原因。蓝皮书把告警体系分为四种类型,各有明确的角色分工。第一类:基于 SLO 的可用性告警。直接监控可用性 SLI 是否跌破 SLO,是最基本的用户受损信号。缺点是滞后——等 30 天窗口的 SLI 跌破 SLO,预算早已耗尽。因此可用性告警通常用短窗口实现(如 1 小时窗口成功率低于阈值),作为燃烧率告警的补充。第二类:延迟告警。监控延迟 SLI(快于阈值的请求比例),延迟劣化往往先于可用性劣化出现,是重要的早期信号。延迟告警的阈值设定要参考用户行为数据(用户在多慢时开始流失),而不是系统能力(系统最慢能忍多少)。第三类:错误预算燃烧率告警。这是 Google 推荐的主力告警类型,监控预算消耗速度而非绝对水平,通过多窗口多燃烧率组合实现分级响应(详见下一个要点)。燃烧率告警的最大优点是把「事故严重程度」和「响应紧急程度」用同一把尺子(预算消耗速度)统一起来。第四类:原因告警。针对特定基础设施或依赖的监控,如数据库主从延迟、消息队列堆积、证书过期倒计时。原因告警应该走非紧急通道(工单、工作时间消息),只有满足「可靠前兆」标准的少数原因告警(如磁盘即将写满)才允许紧急通道。四类告警的配比建议:紧急通道(页面/电话)只放燃烧率告警和少数关键可用性/延迟告警;工单通道放低速燃烧率告警和原因告警;仪表盘放全部原因指标。一个健康的告警体系中,紧急告警数量应该远少于工单告警,工单告警远少于仪表盘指标——金字塔结构,越往上越精炼。
多窗口多燃烧率告警:Google 的核心告警方法。这是蓝皮书告警章节的技术核心,值得完整理解其数学原理。燃烧率定义:burn rate = 实际错误率 ÷ SLO 允许的错误率。SLO 为 99.9% 时允许错误率是 0.1%,如果当前错误率是 1.44%,燃烧率就是 14.4。燃烧率的含义是预算耗尽时间的倒数关系:燃烧率 14.4 意味着 30 天预算将在 30÷14.4 ≈ 2 天内耗尽。多窗口多燃烧率的设计思想:不同严重程度的事故需要不同的检测策略。快速燃烧的事故(燃烧率 14.4,2 天烧穿预算)必须几分钟内检测到,用短窗口;慢速燃烧(燃烧率 1,恰好按 SLO 速度消耗)不急,用长窗口避免噪音。但短窗口单独使用误报率高(瞬时抖动就会触发),所以每个燃烧率等级都用「长窗口 + 短窗口」双重确认:长窗口(如 1 小时)确认问题持续存在,短窗口(如 5 分钟)确认问题仍在发生(已恢复的历史问题不该继续告警)。Google 推荐的四级配置:燃烧率 14.4,窗口 1h + 5m,预算 2 天耗尽,触发页面告警(电话叫醒);燃烧率 6,窗口 6h + 30m,预算 5 天耗尽,触发页面告警;燃烧率 3,窗口 3d(或 1d)+ 2h(或 5m),预算 10 天耗尽,触发工单;燃烧率 1,窗口 3d + 6h 级别的长窗口,触发工单或仅记录。这套配置的检测性能:对燃烧率 14.4 的事故,最坏检测延迟约 1 小时(长窗口需要积累),典型延迟几分钟(短窗口先满足且长窗口快速确认);误报率极低,因为需要两个独立窗口同时超标。实现上通常用 Prometheus recording rules 预计算各窗口的错误率,告警规则用 and 连接两个窗口的条件。Sloth 等工具可以自动生成这套配置。多窗口多燃烧率告警之所以优雅,在于它把告警的灵敏度、准确性和响应分级统一到一个数学框架里,所有参数(燃烧率、窗口)都从 SLO 和预算推导,没有魔法数字。
告警疲劳:如何避免狼来了效应。告警疲劳是渐进形成的组织病:起初每条告警都被认真处理,随着误报累积,工程师开始「先关掉再说」,最终告警声响起时无人抬头。量化诊断告警疲劳的指标:每班紧急告警数量(Google 建议 ≤2)、告警行动率(<10% 即危险)、告警确认时间趋势(越来越慢是麻木的信号)、静默规则数量(大量长期静默说明告警设计失败)、值班工程师的主观调查(「昨晚的告警是否影响了你的睡眠和白天状态」)。治理告警疲劳的系统方法分四步。第一步:止血。立即审查所有紧急告警,行动率低于 10% 的当场降级或删除。这一步通常能砍掉 50% 以上的紧急告警量。第二步:重建。基于 SLO 重新设计告警体系(多窗口多燃烧率),确保每条紧急告警都直接对应用户受损。第三步:守门。建立告警准入机制——新增紧急告警必须经过评审,回答三个问题:这条告警触发时值班应该做什么行动?这个行动能否自动化(能自动化就不该告警)?它与现有告警是否重叠?第四步:持续审计。每月跑一次告警统计报告,行动率、告警量、疲劳指标的趋势公开给全团队。Google 还有一个文化层面的实践:事后分析中如果发现有告警在事故期间触发但被忽略,讨论的重点不是「谁忽略了告警」,而是「为什么这条告警会被忽略」——答案几乎总是告警体系的问题(噪音太多、优先级不清、文档缺失),而不是人的问题。把告警疲劳当作系统缺陷而非个人失职来治理,是可持续的关键。
告警的分级与路由:P1/P2/P3 的定义与响应。告警分级解决「不同严重程度的告警用不同方式送达不同的人」的问题。Google 及业界通用的三级模型:P1(紧急/Page):用户正在显著受损,需要立即人工干预。送达方式是电话 + 强提醒,7×24 响应,目标确认时间 5 分钟内,未确认自动升级到备份值班再升级到团队负责人。典型 P1:错误预算燃烧率 14.4 持续、核心服务可用性跌破阈值、数据丢失风险。P2(高优/Ticket-urgent):用户受损较轻或即将受损,需要尽快处理但可以等到工作时间,或值班工程师远程处理即可。送达方式是即时消息 + 工单,目标响应时间 1 到 4 小时。典型 P2:中速燃烧率(6 倍)、单可用区故障但服务整体正常、容量将在数天内不足。P3(常规/Ticket):需要跟进但不紧急的问题。送达方式是工单系统,按正常排期处理。典型 P3:低速燃烧率(1 倍)、非关键依赖异常、证书 30 天后过期。分级的判定标准应该客观化,避免「我觉得这个挺急的」式主观升级。一个实用的判定框架:是否影响用户(否 → P3)?影响是否持续扩大(是 → P1)?是否有自动恢复机制在工作(是且有效 → 降一级)?路由设计的要点:告警必须路由到「有能力采取行动的人」而不是「名义上的负责人」——如果一线值班处理不了,路由规则应该自动带上升级路径;跨团队依赖的告警要发给服务 owner 而不是基础设施团队(用户受损的症状告警归服务团队,原因告警归基础设施团队);每个告警必须附带上下文链接:仪表盘、值班手册条目、最近变更记录,让接告警的人 30 秒内明白发生了什么。分级体系要定期校准:统计各级告警的实际处理时长,如果 P2 告警经常在 5 分钟内被处理说明它其实是 P1,如果 P1 告警经常拖到第二天说明分级虚高。
告警规则的编写:PromQL 实战要点。Prometheus 告警规则的编写有许多影响可靠性的细节。第一,for 子句的语义:expr 持续为真超过 for 时长才触发告警,这是最基本的去抖机制。但要注意 for 与查询窗口的叠加效应——rate(...[5m]) 配合 for: 5m 意味着问题需要持续约 10 分钟才告警(窗口积累 + 持续时间),设计时要算总检测延迟。第二,多窗口组合的写法:长窗口条件和短窗口条件用 and 连接,例如 rate(errors[1h])/rate(total[1h]) > threshold and rate(errors[5m])/rate(total[5m]) > threshold。and 的语义是「两个条件同时为真」,短窗口条件确保问题「仍在发生」,避免长窗口的历史数据让已恢复的故障持续告警。第三,labels 和 annotations 的规范:每条告警必须带 severity 标签(供 Alertmanager 路由)、summary 和 description 注解(供人阅读)、runbook_url 注解(链接值班手册)。description 中用模板变量注入当前值:错误率是多少、燃烧率多少倍、预计多久耗尽预算,让值班不用打开仪表盘就有基本判断。第四,避免除零和空结果:分母可能为零(低流量时段没有请求),PromQL 中 0/0 产生 NaN 不会触发比较条件,但 x/0 产生 +Inf 会触发——低流量服务的告警要特别处理,常用做法是加分母下限条件 and sum(rate(total[5m])) > 0.1。第五,recording rules 预聚合:告警表达式引用的中间结果(各窗口错误率)应该用 recording rules 预计算,告警规则只做简单比较,既降低评估开销又便于复用和测试。第六,规则测试:用 promtool 的单元测试功能,构造时间序列样本验证告警在预期时间点触发、在不该触发时保持沉默——告警规则也是代码,代码就要有测试。
告警的测试与演练:如何验证告警有效。一条从未被验证过的告警规则,等于不存在。告警测试分四个层次。第一层:规则单元测试。用 promtool test rules 编写测试用例:给定输入时间序列(模拟正常流量、模拟 5% 错误率注入),断言告警在预期时刻处于 pending 或 firing 状态。这层测试应该进 CI,每次修改告警规则自动运行。第二层:管道端到端测试。定期(如每周)注入一个已知无害的测试告警,验证它完整走过「Prometheus 评估 → Alertmanager 路由 → 送达值班手机」的全链路。更严格的做法是 dead man's switch:配置一条永远为真的心跳告警,如果值班系统在规定时间内收到它并自动静默,说明管道通畅;如果心跳消失,说明告警管道本身故障——用告警监控告警系统。第三层:故障注入演练。在预发环境(或生产的隔离角落)真实制造故障:杀掉实例、注入延迟、制造错误,观察告警的检测延迟(从故障发生到告警送达的时间)、告警内容的准确性(description 里的数字对不对)、路由的正确性(是不是发给了对的值班)。第 10 章的混沌工程应该把「告警验证」列为标准检查项。第四层:事故复盘校验。每次真实事故的事后分析必须回答:告警是否及时触发?如果没有,为什么(规则缺失?阈值太高?管道故障)?如果触发了但被忽略,为什么(噪音太多?分级不清)?把「告警改进项」作为事后分析行动项的标准类别。Google 的经验是,故障演练中发现的告警问题数量惊人——相当比例的告警规则存在指标名过时(服务重构后没人更新)、标签选择器失配(新环境的标签不同)、阈值失效(流量增长后原阈值永远触发或永远不触发)等问题。告警和代码一样会腐烂,定期演练就是防腐剂。
案例:从告警风暴到精准告警的改造过程。蓝皮书第 4 章记录了一个典型改造案例。初始状态:某服务团队每天产生约 300 条告警,其中紧急告警(页面级)每天 20 到 30 条,值班工程师平均每班被叫醒 4 次,行动率统计显示只有 3% 的紧急告警导致了真实行动。团队士气低落,值班轮换频繁出现「求换班」,两次真实事故中关键告警都被淹没在噪音里延迟处理。改造分五个阶段。阶段一(第 1 周):全面静默非关键告警,只保留 5 条最核心的可用性告警,先让值班能睡觉。这个激进动作的前提是与团队达成共识:漏报的风险由加强巡检和用户反馈渠道兜底。阶段二(第 2 到 3 周):定义 SLO。团队与产品方确定可用性 SLO 99.9%、延迟 SLO「95% 请求快于 500ms」,部署 SLI 采集。阶段三(第 4 到 6 周):构建多窗口多燃烧率告警。按 14.4/1h+5m、6/6h+30m、3/1d+2h、1/3d+6h 四级配置,紧急通道只接前两级。同时为每条告警编写 runbook 链接。阶段四(第 7 到 8 周):原因告警降级。原来的 CPU、内存、磁盘、进程告警全部转为工单或仪表盘,仅保留「磁盘 2 小时内写满」「数据库主从延迟超阈值且持续扩大」两条前兆型告警在紧急通道。阶段五(持续):建立月度告警审计,行动率低于 10% 的告警自动进入整改清单。改造后三个月的数据:紧急告警从每天 25 条降到每周 1 到 2 条,行动率从 3% 升到 78%,每班被叫醒次数从 4 次降到 0.2 次,两次真实事故的检测时间从平均 15 分钟缩短到 3 分钟以内(燃烧率告警比原来的阈值告警更灵敏)。这个案例的关键启示:改造的第一步不是加规则而是删规则——先止血再重建;SLO 是告警体系的地基,没有 SLO 的告警改造只是换个姿势噪音;行动率是唯一诚实的告警质量指标。
告警文档与值班手册的联动。每条告警都应该是一份「微型文档」的入口,这是告警可操作性的最后一公里。Google 要求每条告警规则携带 runbook_url 注解,链接到值班手册的对应条目。一个合格的告警 runbook 条目包含:告警含义(这条告警触发说明用户正在经历什么);快速诊断(前三个应该看的仪表盘和查询,按命中概率排序);常见原因与处置(历史上这条告警的触发原因分布:60% 是上游依赖 X 故障 → 处置步骤;25% 是发布导致 → 回滚步骤;15% 是流量突增 → 扩容步骤);升级路径(尝试 15 分钟无进展找谁,附带联系方式和备份联系方式);历史事故链接(过去触发这条告警的事故复盘文档)。runbook 的维护机制同样重要:每次事故复盘时,如果处置过程中发现 runbook 缺失或过时,「更新 runbook」必须作为行动项记录并跟踪关闭;每季度审查一次所有 runbook 的「最近验证时间」,超过半年的标记为待验证(通过演练或桌面推演验证)。一个实用的质量检验:让一个从未处理过该告警的工程师(新入职成员最合适)拿着 runbook 处理一次模拟告警,记录他卡住的每个地方——卡点就是 runbook 的改进点。告警文档与值班手册的联动还有反向价值:统计 runbook 的访问日志可以知道哪些告警最常被处理、哪些处置步骤最常被执行,前者提示需要根治的慢性问题,后者提示自动化候选。当某个 runbook 的处置步骤 90% 是同一套操作时,就是把它变成一键修复脚本的时机——这是第 5 章值班自动化主题的入口。
代码示例
/* 示例 1:多窗口多燃烧率告警规则(Prometheus YAML) */
groups:
- name: slo-burn-rate-alerts
rules:
/* P1: 燃烧率 14.4,1h + 5m 双窗口,预算 2 天耗尽 */
- alert: CheckoutAPIHighBurnRate
expr: |
(
slo:http_requests:ratio_rate1h{job="checkout-api"} < (1 - 14.4 * 0.001)
and
slo:http_requests:ratio_rate5m{job="checkout-api"} < (1 - 14.4 * 0.001)
)
for: 2m
labels: { severity: page }
annotations:
summary: "checkout-api 错误预算高速燃烧"
description: "过去1小时与5分钟错误率均超过 SLO 允许值的 14.4 倍,30天预算将在约2天内耗尽"
runbook_url: "https://wiki.internal/runbooks/checkout-api-burn-rate"
/* P2: 燃烧率 6,6h + 30m 双窗口,预算 5 天耗尽 */
- alert: CheckoutAPIMediumBurnRate
expr: |
(
slo:http_requests:ratio_rate6h{job="checkout-api"} < (1 - 6 * 0.001)
and
slo:http_requests:ratio_rate30m{job="checkout-api"} < (1 - 6 * 0.001)
)
for: 5m
labels: { severity: page }
annotations:
summary: "checkout-api 错误预算中速燃烧"
runbook_url: "https://wiki.internal/runbooks/checkout-api-burn-rate"这份配置是 Google 多窗口多燃烧率告警的标准实现。理解表达式的关键:slo:http_requests:ratio_rate1h 是预计算的成功率(recording rule),阈值 (1 - 14.4 × 0.001) = 0.9856 表示「成功率低于 98.56%」即错误率超过 1.44%,也就是 SLO 允许错误率(0.1%)的 14.4 倍。and 连接两个窗口:1 小时窗口确认问题已持续一段时间(过滤瞬时抖动),5 分钟窗口确认问题此刻仍在发生(避免已恢复故障因长窗口记忆而持续告警)。for: 2m 再加一层去抖。severity: page 标签供 Alertmanager 路由到电话通道。注意 P2 告警同样是 page 级别但燃烧率更低——是否叫醒人取决于组织文化,Google 的做法是 14.4 和 6 两档都页面告警,3 和 1 两档走工单。annotations 中的 runbook_url 是强制要求:没有处置文档的告警不允许上线。/* 示例 2:燃烧率与预算耗尽时间的换算表生成器 */
function burnRateTable(slo: number, windowDays: number) {
const budget = 1 - slo;
const rates = [1, 2, 3, 6, 14.4, 30, 100];
return rates.map((r) => ({
burnRate: r,
errorRate: (r * budget * 100).toFixed(3) + "%",
budgetExhaustedIn:
r <= 1 ? "不会耗尽(≤SLO 速度)"
: (windowDays / r).toFixed(1) + " 天",
recommendedResponse:
r >= 14.4 ? "P1 页面告警,立即响应" :
r >= 6 ? "P2 页面告警,尽快响应" :
r >= 2 ? "P3 工单,工作时间处理" : "记录观察",
}));
}
/* SLO=99.9%, 30天窗口输出示例:
燃烧率14.4 -> 错误率1.44% -> 2.1天耗尽 -> P1
燃烧率6 -> 错误率0.6% -> 5.0天耗尽 -> P2
燃烧率3 -> 错误率0.3% -> 10天耗尽 -> P3
燃烧率1 -> 错误率0.1% -> 30天耗尽 -> 观察 */这张换算表是告警设计的「罗塞塔石碑」,它把抽象的燃烧率翻译成三个直观量:当前错误率是多少、预算多久耗尽、应该用什么级别响应。设计告警时先从业务角度问「多快耗尽预算的事故值得叫醒人?」——如果答案是「2 天内耗尽必须立即知道」,反查表格得到燃烧率 14.4 和对应错误率阈值 1.44%,告警参数就确定了。这种「从响应需求反推告警阈值」的方法彻底取代了「错误率超过 5% 就告警」的拍脑袋传统。表格还揭示了一个重要事实:燃烧率 1(错误率恰好等于 SLO 允许值)意味着预算恰好在窗口末尾耗尽——这是「亚健康」状态,不紧急但值得建工单排查,因为任何轻微恶化都会导致超标。建议把这个生成器做成团队工具,SLO 调整时自动重算所有告警阈值,避免 SLO 与告警脱节。/* 示例 3:Alertmanager 路由与升级配置 */
route:
receiver: default-ticket
group_by: [alertname, service]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- match: { severity: page }
receiver: oncall-phone
repeat_interval: 10m /* 未确认则每10分钟重呼 */
- match: { severity: ticket }
receiver: ticket-system
repeat_interval: 24h
receivers:
- name: oncall-phone
pagerduty_configs:
- service_key: "<paging-service-key>"
- name: ticket-system
webhook_configs:
- url: "http://ticket-internal/api/create"
inhibit_rules:
/* P1 燃烧率告警触发时,抑制同服务的 P2/P3 告警,避免风暴 */
- source_match: { severity: page }
target_match: { severity: ticket }
equal: [service]这份 Alertmanager 配置展示了告警送达侧的三个关键机制。分组(group_by + group_wait):同一服务的多条相关告警聚合成一条通知,30 秒等待期让同时触发的告警合并,从源头减少通知轰炸。重复间隔的分级设计很讲究:page 级 10 分钟重呼确保值班不会睡过头漏掉告警(配合 PagerDuty 的升级链:主值班未确认自动转备份值班);ticket 级 24 小时提醒防止工单被遗忘。抑制规则(inhibit_rules)是防告警风暴的利器:当 P1 燃烧率告警已经触发(说明用户受损确认),同服务的 P3 工单告警自动静默——它们描述的是同一场事故,没必要重复通知。实践中还可以加反向依赖抑制:核心依赖的告警触发时,抑制所有下游服务的衍生告警,让值班聚焦根因。注意 repeat_interval 与告警恢复的关系:告警恢复后会发送 resolved 通知,如果 10 分钟内问题反复(flapping),分组机制会把它们合并,避免「触发-恢复-触发」循环轰炸。/* 示例 4:告警规则的单元测试(promtool) */
/* 文件: checkout-alerts_test.yml */
rule_files:
- checkout-alerts.yml
evaluation_interval: 1m
tests:
- interval: 1m
input_series:
/* 前 60 分钟正常:错误率 0 */
- series: 'slo:http_requests:ratio_rate1h{job="checkout-api"}'
values: "0.9999x60"
- series: 'slo:http_requests:ratio_rate5m{job="checkout-api"}'
values: "0.9999x60"
/* 之后注入故障:成功率跌到 0.98(燃烧率约 20) */
- series: 'slo:http_requests:ratio_rate1h{job="checkout-api"}'
values: "0.9999x60 0.98x60"
- series: 'slo:http_requests:ratio_rate5m{job="checkout-api"}'
values: "0.9999x60 0.98x60"
alert_rule_test:
- eval_time: 30m
alertname: CheckoutAPIHighBurnRate
exp_alerts: [] /* 正常期间不应告警 */
- eval_time: 65m
alertname: CheckoutAPIHighBurnRate
exp_alerts:
- exp_labels: { severity: page, job: "checkout-api" }告警规则是生产安全网,安全网本身必须先经过测试。promtool 的测试框架让你用声明式方式验证告警行为:input_series 定义模拟的时间序列数据(0.9999x60 表示连续 60 个采样点值为 0.9999),alert_rule_test 断言特定时刻的告警状态。这个测试用例验证两件事:正常期间告警保持沉默(防误报)、故障注入后 5 分钟内 P1 告警触发(防漏报)。建议为每条告警规则至少写三个用例:正常数据不触发、边界数据不触发(错误率恰好等于阈值减一)、故障数据按时触发。把测试文件与规则文件放在同一仓库,CI 中运行 promtool test rules,规则修改必须通过测试才能合并。Google 内部所有告警规则变更都要求附带测试证据——「我改了一行阈值」和「我改了一行阈值并验证了 12 个场景」是完全不同的变更质量。测试还能捕捉一类隐蔽 bug:recording rule 改名后告警规则引用了不存在的指标,Prometheus 不会报错,告警只是永远不触发——单元测试会立刻暴露这种「静默失效」。/* 示例 5:告警质量月度审计脚本 */
const alerts = loadAlertHistory("last_30d.json");
const stats = {};
for (const a of alerts) {
const key = a.alertname;
stats[key] ??= { fired: 0, acted: 0, ignored: 0 };
stats[key].fired++;
if (a.action_taken) stats[key].acted++;
else stats[key].ignored++;
}
const report = Object.entries(stats).map(([name, s]) => ({
alert: name,
fired: s.fired,
actionRate: (s.acted / s.fired * 100).toFixed(1) + "%",
verdict:
s.acted / s.fired < 0.1 ? "整改:降级或删除" :
s.fired > 50 ? "整改:修复根因或聚合" : "健康",
}));
report.sort((a, b) => parseFloat(a.actionRate) - parseFloat(b.actionRate));
console.table(report);这个脚本把「告警审计」从理念变成了每月例行公事。核心逻辑是计算每条告警的行动率并自动给出裁决建议:行动率低于 10% 的告警被标记为「整改:降级或删除」——它们消耗值班注意力却几乎从不导致行动,是告警疲劳的直接来源;月触发超过 50 次的告警即使行动率尚可也被标记——高频触发说明存在需要根治的慢性问题,或者告警粒度过细需要聚合(比如同一根因在 10 个实例上各触发一条,应该聚合为一条)。console.table 输出的报表按行动率升序排列,最差的告警排在最前面,团队月度告警评审会直接从第一行开始过。数据来源 action_taken 字段需要告警系统支持——PagerDuty 等工具天然记录「确认」「解决」动作,自建系统可以在告警通知里加一个「本次告警是否导致行动」的反馈按钮。坚持跑六个月这个审计,告警体系的信噪比会有数量级的提升,这是 Google 多个团队验证过的最便宜、最有效的告警治理手段。实验笔记
本实验的目标是把一个「告警泛滥」的系统改造为基于 SLO 的精准告警体系。实验分五个部分,预计 5 到 6 小时。第一部分:告警审计(约 1 小时)。导出你的告警系统(或模拟数据)过去 30 天的全部告警记录,逐条分类统计:每条告警的触发次数、导致的真实行动次数(重启、回滚、扩容、修 bug)、误报次数、无人理会次数。计算每条告警的「行动率」= 真实行动次数 ÷ 触发次数。产出三张清单:行动率高于 50% 的「好告警」、行动率低于 10% 的「噪音告警」、从未触发过的「僵尸告警」。检查清单:统计是否覆盖了所有告警渠道(电话、消息、邮件、工单)?「无人理会」的告警是否单独标记(它们是疲劳的直接证据)?第二部分:症状告警设计(约 1.5 小时)。为服务定义基于 SLO 的告警规则。先确定 SLO(沿用第 3 章实验的结果,如可用性 99.9%、30 天窗口),然后设计多窗口多燃烧率告警:P1 页面告警——燃烧率 14.4,窗口组合 1 小时 + 5 分钟(2 天耗尽预算的速度);P2 页面告警——燃烧率 6,窗口组合 6 小时 + 30 分钟;P3 工单告警——燃烧率 1,窗口组合 3 天 + 6 小时。为每条规则写出 PromQL 表达式,并计算验证:燃烧率 14.4 时,5 分钟窗口内的错误率应该是多少?(答案:14.4 × 0.1% = 1.44%)。第三部分:告警规则实现与测试(约 1.5 小时)。在实验环境部署 Prometheus + Alertmanager,把第二部分设计的规则写成 YAML 配置。然后进行故障注入测试:用脚本向服务注入 5% 的错误率,验证 P1 告警在几分钟内触发;注入 0.2% 的错误率,验证只有 P3 工单产生而 P1 不触发;注入 30 秒的瞬时错误脉冲,验证双窗口机制过滤了误报。记录每次测试的告警触发时间(从故障开始到告警送达的延迟)。第四部分:告警路由与文档(约 1 小时)。配置 Alertmanager 的路由规则:P1 → 电话 + 值班群,5 分钟未确认自动升级到备份值班;P2 → 即时消息,30 分钟未确认升级;P3 → 工单系统。为每条告警编写告警文档:这条告警意味着什么?第一步排查什么?链接到值班手册的哪个条目?历史上哪些事故触发过它?第五部分:改造效果评估与答辩(约 45 分钟)。对比改造前后:告警总数变化、页面告警(叫醒人的)数量变化、预计行动率变化。回答质询:「如果 SLO 定得太松,燃烧率告警会不会漏掉真实事故?」「双窗口机制最坏情况下的检测延迟是多少?」「如何防止告警规则本身有 bug(比如指标名写错导致永远不触发)?」最后一个问题的答案引出「告警的监控」:用 dead man's switch(心跳告警)验证告警管道本身活着——一个定期触发、如果被处理系统正常消化就静默的特殊告警,如果它停止触发说明管道断了。通过本实验你将掌握从审计、设计、实现到验证的完整告警工程闭环。
延伸阅读
- The Site Reliability Workbook 第 4 章 "Alerting on SLOs"——本章原始出处,给出了多窗口多燃烧率告警的完整数学推导和 Prometheus 实现,包含燃烧率与窗口组合的对照表(14.4/1h、6/6h、3/3d、1/3d 等),以及 Google 内部告警改造前后的量化对比数据。原文的推导部分值得逐行演算。
- SRE 黑皮书第 6 章 "Monitoring Distributed Systems"——监控哲学的奠基之作,提出了四个黄金信号(延迟、流量、错误、饱和度)、症状 vs 原因监控、告警疲劳的治理原则。「每条紧急告警都必须可操作」的铁律出自这一章,是理解蓝皮书告警设计的思想源头。
- Google Cloud Blog: Alerting on SLOs like Pros——Google Cloud 团队发表的实践文章,用具体案例演示了如何为真实服务配置多燃烧率告警,包含完整的 Prometheus 规则文件和常见配置错误的排查。比书籍更贴近动手实践,适合边读边配。
- Prometheus 官方文档:Alerting Rules 与 Alertmanager——告警规则语法、for 子句语义、标签处理、路由树配置、静默与抑制规则的权威参考。多窗口告警的实现依赖对这些机制的精确理解,尤其是 for 持续时间与窗口长度的配合。
- Sloth(github.com/slok/sloth)——SLO 即代码工具,输入简单的 SLO 定义 YAML,自动生成多窗口多燃烧率的 recording rules 和告警规则,内置 Google 推荐的燃烧率组合。对于不想手写复杂 PromQL 的团队,Sloth 是最佳起点,其生成结果也是极好的学习材料。`, 《My Philosophy on Alerting》(Rob Ewaschuk 的 Google SRE 随想)——Google SRE 工程师撰写的告警哲学笔记,提出了许多尖锐的观点:告警应该基于症状、每条告警都应该有文档、宁可漏报也不要疲劳(漏报可以用其他机制兜底,疲劳会摧毁整个体系)。短小精悍,是告警设计评审时的思想武器。
- PagerDuty Incident Response Documentation——值班响应侧的告警实践:告警分级标准、升级策略、告警聚合与去重的产品化思路。从「收到告警之后」的视角补充了本章「发出告警之前」的设计,两相结合才是完整的告警生命周期。
- The Art of SLO 课程中的告警模块——Google 官方培训中关于「从 SLO 到告警」的练习,包含互动式案例:给定一个服务的 SLI 数据和事故历史,让学员设计告警规则并评估其检测延迟与误报率。适合团队 workshop 使用。