告警哲学 · 少即是多
告警系统真正的失败模式不是「漏报」,而是「太吵以至于没人看」。这一章讲告警疲劳的机制、每条告警都该通过的检验、如何用 ALERTS 指标做告警质量审计,以及一个反直觉的结论:删掉告警通常比加告警更能提升可靠性。
告警哲学 · 少即是多
技术都讲完了:规则怎么写、阈值怎么定、燃烧率怎么算、通知怎么送。
这一章讲最难的部分——它不是技术问题,是判断力问题。
真正的失败模式
大多数人以为告警系统的失败是“漏报”。错了。真正的失败是“太吵,以至于没人看”——而这必然导致漏报。
这是一条可预测的死亡螺旋:
注意这个循环的残酷之处:事故复盘的结论往往是“我们应该加个告警”——于是回到第一步,螺旋加速。
打破它的唯一方法是反过来:删告警。
告警疲劳是生理的,不是态度问题
别把“值班的人不看告警”归咎于态度。这是人的注意力机制决定的:
- 如果一条告警 90% 的时候是误报,人必然会开始忽略它 —— 这不是懒,是理性的适应:99 次狼来了之后,不去看是期望收益更高的选择。
- 一旦形成“先忽略再说”的习惯,它会泛化到所有告警 —— 包括那 10% 真的。这是最可怕的部分:一条吵的告警会污染整个告警系统的可信度。
- 半夜被叫醒的代价是复利的 —— 睡眠被打断的人,第二天的判断力显著下降。一次无谓的 page,损害的不只是那一晚。
所以“多一条告警不会有坏处”这个直觉是彻底错的:每条噪音告警都在消耗整个系统的信噪比,而信噪比是公共资源——它被消耗掉之后,所有告警都失效了。
推论:一条误报率高的告警,比没有告警更糟。 因为没有告警时你知道自己是瞎的,会去想别的办法;有个吵的告警时,你以为自己有覆盖,实际上没有。
每条告警的检验
在加任何一条 page 级告警前,逐条过这个筛子(第 3 章提过,这里给完整版):
□ 1. 用户在受影响吗?
└─ 不是 → 不该 page(放看板/ticket)
□ 2. 需要人立刻介入吗?
└─ 系统能自愈(重试/failover/扩容) → 不该 page
□ 3. 有 runbook 吗?
└─ 没有 → 你只是把一个人叫醒,然后让他从零开始猜
□ 4. 我凌晨 3 点会为它起床吗?
└─ 不会 → 它凭什么是 page?
□ 5. 它误报的概率有多大?
└─ 高 → 先去修阈值(用[燃烧率](/manual/observability/slo-burn-rate)),别急着上线
□ 6. 它和已有的告警重复吗?
└─ 重复 → 删掉一个(通常该留症状那条)第 3 条(“有 runbook 吗”)最常被跳过,但它极其重要。 一条没有 runbook 的 page 意味着:被叫醒的人(可能不是写这条告警的人、可能对这个服务不熟)要在凌晨、脑子不清醒、有时间压力的情况下,从零开始理解发生了什么。
runbook 不需要很长,但必须回答三件事:
- 这条告警是什么意思(用人话,不是重复表达式)。
- 最可能的原因是什么(按概率排序)。
- 第一步该做什么(哪怕是“看这个面板”)。
用 ALERTS 做告警质量审计
这是本章最实用的部分。还记得告警本身也是指标吗?用它来审计你的告警系统。
定期(比如每月)跑这几个查询——它们会告诉你哪些告警该删:
# ① 最吵的告警 Top 10(过去 30 天 firing 的时长)
topk(10,
sum by (alertname) (count_over_time(ALERTS{alertstate="firing"}[30d]))
)
# ② 从没响过的告警(死规则)—— 需要和规则列表比对
# 先看哪些响过:
count by (alertname) (count_over_time(ALERTS[90d]))
# 规则里有、但这里没有的 → 90 天没响过 → 它真的有用吗?
# ③ 响得太频繁(震荡)的告警
topk(10,
sum by (alertname) (
changes(ALERTS_FOR_STATE[7d]) # 状态变化次数
)
)
# ④ pending 但从没 firing 的(说明 for 太长,或者阈值在边界抖动)
count by (alertname) (ALERTS{alertstate="pending"})每个查询对应一个该采取的行动:
| 发现 | 说明 | 行动 |
|---|---|---|
| 某条告警响了 500 次 | 它要么是误报,要么是慢性问题没人修 | 修阈值,或修系统,或删掉 |
| 某条 90 天没响过 | 要么系统很健康,要么这条规则是死的(写错了/永远不会触发) | 验证它能触发,否则删 |
| 某条状态反复横跳 | 震荡 | 加 keep_firing_for,或调阈值 |
| 总是 pending 从不 firing | for 太长,或阈值卡在边界 | 调 for 或阈值 |
“90 天没响过的告警”这条特别值得警惕: 你无法区分“系统健康”和“这条规则根本不工作”。没被验证过能触发的告警,等于不存在——第 9 部分会讲怎么测试告警规则。
反直觉的结论:删告警能提升可靠性
一个让人不舒服、但真实的结论:大多数团队该做的第一件事,是删掉一半的告警。
为什么这提升而非降低可靠性?
- 信噪比是公共资源 —— 删掉噪音,剩下的告警才会被认真对待。10 条每条都可信的告警,远胜于 100 条没人看的。
- 被删掉的大多是“原因”类告警 —— 它们本来就该在看板上而不是电话里。删掉它们,不会失去任何信息——那些指标还在,你排障时照样能看。
- 少数几条 SLO 告警覆盖面更广 —— 一条基于燃烧率的告警,覆盖了所有你没想到的故障模式;而 50 条基于原因的告警,只覆盖了你想到的那 50 种。
一个可执行的做法:
① 列出所有 page 级告警
② 每条问:"过去 6 个月,它响的时候有几次真的需要人立刻做事?"
└─ 0 次 → 立刻降级成 ticket 或删掉
└─ 少数几次 → 降级
③ 剩下的,用第 4 章的筛子再过一遍
④ 用[燃烧率告警](/manual/observability/slo-burn-rate)补上覆盖理想状态:一个服务的 page 级告警应该是个位数——基本就是几条 SLO 燃烧率告警 + 服务完全不可达 + 少数几个“一次就致命”的条件(证书过期、数据损坏)。如果你有 50 条 page 级告警,几乎可以肯定其中 40 条不该是 page。
小结与下一部分
告警系统真正的失败不是漏报,而是太吵以至于没人看——这会形成死亡螺旋(加告警 → 误报 → 忽略 → 静音 → 漏报 → 事故 → “该加个告警” → 循环),而事故复盘的常见结论恰恰在加速它。告警疲劳是生理的不是态度问题:90% 误报时忽略它是理性选择;而这个习惯会泛化污染所有告警——信噪比是公共资源。推论:误报率高的告警比没有告警更糟(你以为有覆盖,实际是瞎的)。每条 page 过六条筛子:用户受影响 / 需人立刻介入 / 有 runbook / 凌晨会起床 / 误报率低 / 不重复;runbook 必须回答“什么意思 / 最可能的原因 / 第一步做什么”。用 ALERTS 指标做月度审计:找最吵的(修或删)、90 天没响的(无法区分“健康”和“规则是死的”,必须验证能触发)、震荡的(加 keep_firing_for)、总 pending 的。反直觉的结论:删掉一半告警能提升可靠性——删的大多是“原因”类(本就该在看板上,删了不丢信息),而少数几条 SLO 告警覆盖面更广。理想状态:一个服务的 page 级告警应是个位数。
Prometheus 那条线到此讲完了(采集 → 指标体系 → 存储 → 查询 → 告警)。接下来换一个主角:三大支柱怎么被同一条管道运送?👉 第 6 部分 · OpenTelemetry Collector。