3、告警去重与降噪:告警聚合策略、告警抑制规则、告警静默机制
说实话,做运维最怕什么?不是没告警,而是告警太多。
我见过最夸张的一次,某个客户的核心业务系统出了点小故障,结果五分钟内涌进来两万多条告警。值班同学直接懵了,根本分不清哪个是根因,哪个是连带反应。这就是典型的「告警风暴」——你想想看,如果每秒钟都在响警报,那跟没响有什么区别?
所以今天咱们聊聊告警去重与降噪。说白了,就是让告警系统变得「聪明」一点,别啥事都往上捅。
3.1 告警聚合策略:把相似的告警「拧」成一股绳
告警聚合,核心思路就一句话:把同一时间、同一维度、同一原因的告警合并成一条。
我个人习惯把聚合策略分成三个层次:
3.1.1 时间窗口聚合
举个例子,某个服务端口抖动,一秒内报了 50 次「连接超时」。如果每条都发出去,那就是灾难。
我的做法是:设置一个 60 秒的时间窗口。窗口内相同类型的告警,只发一条,但计数累加。
# 伪代码示例:时间窗口聚合逻辑
def aggregate_alerts(alert_stream, window_seconds=60):
cache = {}
for alert in alert_stream:
key = (alert.type, alert.host, alert.metric)
if key in cache and (now() - cache[key].first_seen) < window_seconds:
cache[key].count += 1
cache[key].last_seen = now()
else:
cache[key] = AlertGroup(first_seen=now(), count=1)
send_alert(alert) # 只发送第一条
嗯,这里要注意:窗口大小别设太死。我见过有人设 300 秒,结果一个故障都处理完了,告警才发出来。我个人建议窗口设在 30~120 秒之间,具体看你的业务容忍度。
3.1.2 字段维度聚合
有些告警,虽然内容不同,但根因是同一个。比如「磁盘使用率 95%」和「inode 耗尽」,其实都是磁盘满了。
我在项目中遇到过这种情况:某台数据库服务器磁盘满了,结果同时触发了磁盘告警、慢查询告警、备份失败告警。如果不聚合,值班人员得看三条告警才能拼出全貌。
我的做法是:按主机 + 故障域做聚合。只要告警来自同一台机器,且时间窗口重叠,就合并成一条「故障摘要」。
| 聚合维度 | 示例 | 合并规则 |
|---|---|---|
| 主机维度 | 同一台服务器的 CPU、内存、磁盘告警 | 合并为「主机异常」 |
| 服务维度 | 同一个微服务的多个实例告警 | 合并为「服务故障」 |
| 依赖维度 | 数据库告警 + 依赖该库的应用告警 | 只保留根因(数据库告警) |
3.1.3 相似度聚合
这个稍微高级一点。有些告警内容不完全一样,但语义相似。比如「连接池耗尽」和「获取连接超时」,其实是一回事。
我建议用 Levenshtein 距离或者简单的关键词匹配来做。别搞太复杂,生产环境里 80% 的相似告警都能用关键词覆盖。
3.2 告警抑制规则:别让「果」掩盖了「因」
告警抑制,说白了就是因果关系的处理。
你想想看,如果数据库挂了,那所有依赖它的应用都会报错。这时候如果每条应用告警都发出去,那数据库的根因告警反而被淹没了。
我曾经踩过一个坑:某次线上故障,数据库主从延迟导致前端大量报 502。结果告警系统发了 300 条「502 错误率过高」,而真正的「主从延迟」告警只发了一条,还被淹没了。值班同学花了 20 分钟才找到根因。
从那以后,我定了一条铁律:所有告警必须带上「依赖关系」标签。
3.2.1 层级抑制
最常见的抑制方式。比如:
- 如果「交换机宕机」告警触发,则抑制该交换机下所有服务器的「网络不可达」告警
- 如果「数据库不可用」告警触发,则抑制所有「应用连接超时」告警
# 抑制规则示例(Prometheus Alertmanager 风格)
groups:
- name: network
rules:
- alert: SwitchDown
expr: switch_status == 0
labels:
severity: critical
- alert: HostUnreachable
expr: probe_success == 0
labels:
severity: warning
# 如果 SwitchDown 已触发,则抑制 HostUnreachable
inhibit_rules:
- source_match:
alertname: SwitchDown
target_match:
alertname: HostUnreachable
3.2.2 频率抑制
有些告警会反复触发、恢复、再触发。比如网络抖动,可能每 5 分钟抖一次。如果每次都发告警,那值班人员就不用干别的了。
我的做法是:同一个告警在 1 小时内只发一次。如果再次触发,只更新「最近一次触发时间」,不重复发送。
3.3 告警静默机制:给告警系统「放个假」
告警静默,就是主动告诉系统:「这段时间别吵我,我知道有问题。」
我见过很多团队把静默当成「掩耳盗铃」——出了问题不想修,就静默掉。这其实是大忌。
3.3.1 计划内静默
比如凌晨 2 点的版本发布、周末的机房维护。这些操作肯定会触发告警,但都是预期内的。
我的习惯是:提前创建静默规则,带上明确的「原因」和「负责人」。
# 静默规则示例
{
"name": "版本发布静默",
"start": "2024-01-15 02:00:00",
"end": "2024-01-15 04:00:00",
"matchers": [
{"key": "service", "value": "order-service"},
{"key": "env", "value": "production"}
],
"reason": "v3.2.1 版本发布,预计影响订单服务",
"owner": "张三"
}
3.3.2 临时静默
有时候线上出了故障,但短时间内修不好。比如磁盘满了,但扩容需要 2 小时。这时候如果每 5 分钟报一次「磁盘告警」,那值班人员会疯掉。
我建议:临时静默一定要设置「自动过期」。最长不超过 4 小时。到期后如果问题还在,告警会重新触发,提醒你「嘿,这问题还没修呢」。
3.3.3 静默的审计与复盘
我个人要求团队:每次静默都要记录到工单系统。每周复盘一次,看看哪些静默是合理的,哪些是「懒得修」的。
嗯,这里有个小经验:如果某个告警被反复静默,那说明这个告警的阈值或者规则本身有问题。与其反复静默,不如直接调整告警策略。
3.4 三者如何配合?
聚合、抑制、静默,这三者不是孤立的。我一般这样搭配:
- 先聚合:把相似的告警合并,减少数量
- 再抑制:把因果关系的告警过滤掉,保留根因
- 最后静默:对已知的、计划内的问题,主动屏蔽
举个例子:某次数据库主从切换,会触发 100 条告警。经过聚合后变成 10 条,再经过抑制后剩下 3 条(主从切换、连接池变化、延迟告警)。如果这是计划内的切换,那直接静默掉,一条告警都不会发。
你看,经过这三层处理,值班人员收到的告警从 100 条变成了 0 条。但注意——不是「没有告警」,而是「不需要关注的告警被过滤了」。真正的根因告警,一条都不会少。
好了,这一章就聊到这儿。下一章咱们讲讲告警的升级策略——当告警没人处理时,系统该怎么「催」你?