有些告警很准,却有害
一句话总结
告警的成功标准不是“是否检测到问题”,而是“现在是否需要人采取行动”。无法通过这一标准的告警,即使判断准确,也会造成伤害。
为什么需要它
每次发生事故就增加一条规则,两年后规则会达到 300 条,Slack 每天堆积 400 条消息。值班人员夜里被叫醒三次,三次都什么也没做就重新睡下。每天 400 条告警中,只有 4 条最终引发实际行动,精确率只有 1%。
在这种状态下,人学到的最优策略是“先忽略,之后再看”。正因为这种策略很合理,情况才更加危险。增加一条告警无法解决问题。
它如何运作
原则只有一个:只针对用户实际感受到的症状进行呼叫。CPU 达到 90% 不是症状。如果 CPU 为 90%,响应时间却正常,那么什么事故都没有发生;如果 CPU 只有 40%,响应却每次耗时 5 秒,那就是严重事故。针对原因指标发出呼叫,会在这两种情况下都作出错误判断。
应该呼叫的指标:可用性、延迟(超过阈值的比例)、吞吐量骤降、新鲜度、正确性。不应呼叫的指标:CPU、内存、磁盘 IOPS、Pod 重启次数、线程池使用率、GC 时间、副本数量。只有两个例外:不可逆且需要提前处理的问题(磁盘剩余空间、证书过期、配额耗尽),以及观测系统自身故障(up == 0 or absent(up))。如果没有后一类告警,其余告警可能会悄无声息地全部失效。
阈值也必须能够解释。“错误率超过 5% 就告警”中的 5% 从何而来?大多数情况下,它没有任何依据。无法解释的阈值会在每次事故后被一点点调高。
for: 要求表达式持续为真达到指定时间后才触发;中间只要有一次变为假,计时器就会归零。因此,“误报太多,把 for 延长到 30 分钟”是一种反模式。检测会延迟 30 分钟,如果事故在 25 分钟内结束,告警甚至完全不会触发。正确顺序是:① 使用长窗口 rate 进行平滑;② 使用短窗口 AND 确认问题仍在持续;③ 最后只给 for 设置 2~5 分钟。
生产现场中的表现
路由只有三个等级:页面呼叫(现在必须醒来)、工单(工作时间处理)、监控面板(不创建告警)。不存在第四个等级。“先只发到 Slack 频道”是最糟糕的折中方案——没有人负责,也没有人会关闭它。
分组最常见的错误,是在 group_by 中加入 instance。这样会按 Pod 数量产生告警。如果 severity=page 却没有 runbook_url,CI 就应该让构建失败。凌晨 3 点被叫醒的人并不处于能够发挥创造力的状态。不过,只有链接、内容却为空的运行手册,比没有链接更糟。
目标应以数字明确规定:以 12 小时轮班为基准,平均页面呼叫不超过 2 次,页面呼叫转化为实际行动的比例至少达到 70%。一旦超过这两个数字,就不该继续增加告警,而应开始删除。连续 3 个月没有引发行动的页面呼叫应降级或删除;持续静默超过 30 天的告警也应删除。静默是“这条告警有问题”最诚实的信号。
一条告警中必须包含什么
凌晨 3 点被叫醒的人,首先看到的是屏幕上的一行告警标题。这一行和正文必须能引导下一步行动,因此告警中应包含固定项目。
- **哪里出了问题,并使用用户语言描述。**应写“支付 API 错误率 12%”,而不是“hikari_pending > 0”。
- **问题有多严重。**同时写出当前值和阈值。12% 与 51% 所需的响应不同。
- **从何时开始。**刚刚开始与已经持续 40 分钟,是不同的情况。
- **发生在哪里。**包括服务、环境和必要范围。但如果细化到 Pod 名称,就会像前面所述,按 Pod 数量拆分告警。
- **下一步做什么。**提供运行手册链接和直接打开监控面板的链接。
还建议增加一项:提供一种方式记录**“该告警触发了,但无需采取任何行动”**。如果值班人员能当场点击一次留下记录,几周后就可以用数据判断哪些告警是噪声。这比复盘时依赖记忆争论,得出结论要快得多。
**创建告警时,必须同时确定删除它的条件。**增加规则时,如果同时写明“连续 3 个月没有引发行动就删除”,规则从一开始就不会增长到 300 条。规则容易增加,却很难删除,因此应在创建时就同时建立删除依据。
最后,还必须测试告警本身。编写规则后,要人为制造相应条件,确认它会真实触发;情况结束时,还要确认它会恢复。**无法恢复的告警,与无法触发的告警一样糟糕。**如果触发一次后永远保持红色,下次真正发生相同问题时,就不会有人察觉。
下一项实验要做什么
你将区分症状与原因,为告警规则填写必需字段,定义路由、抑制和分组,实际补全运行手册,最后亲自编写检查器,在 CI 中发现存在缺陷的规则。