监控系统一旦覆盖到基础设施、中间件、应用和外部依赖,告警数量会迅速上涨。一个核心交换机丢包,可能同时触发数据库连接超时、缓存读写失败、消息积压、多个服务健康检查不通过,并在几分钟内产生上百条通知。告警风暴的根源通常不是监控项太多,而是缺少有效的告警收敛机制:每条告警单独发送、派生告警不被屏蔽、维护窗口仍然持续报警。最终结果是值班人员对告警麻木,真正关键的故障被淹没在大量重复信息里。要解决这个问题,需要从分组、抑制和静默三个方向同时治理。

一、告警风暴为什么会产生
告警风暴并非单一原因造成,它通常是多个配置缺陷叠加后的结果。第一个常见原因是告警阈值设置过细。例如对CPU使用率同时设置了超过70%警告、超过80%严重、超过90%紧急,当CPU短暂飙升到85%时,可能同时触发三条告警。第二个原因是派生告警没有做关联处理。当一台物理机宕机时,运行在上面的数据库、缓存、服务实例都会出现不可达告警,这些告警虽然数量多,但根因只有一个。第三是重复通知策略不合理,如果没有设置重复间隔,或者间隔太短,同一个故障会每隔几分钟发送一次完整列表,进一步放大通知量。
从告警链路看,Prometheus负责产生告警,Alertmanager负责处理通知。真正能压缩通知量的是Alertmanager的路由分组、抑制规则和静默机制。Prometheus的告警规则只决定哪些条件触发告警,它本身并不能判断多条告警之间是否存在因果关系。因此解决告警风暴的关键在于把告警从产生层推进到处理层之后,使用分组和抑制策略对原始告警流进行重组,而不是简单地在采集端减少告警规则。若只依赖减少监控项,虽然通知数量下降,但会丢失对系统状态的可见性。
一个常见的错误是在应用层把所有异常都包装成独立告警,比如每个接口错误率单独生成一个通知。这样的设计看起来精确,实际上忽略了同一个下游依赖故障会同时影响几十个接口。更好的做法是先定义一层聚合维度,例如按服务、集群、实例或告警类型进行归并,再决定是否通知。告警风暴治理不是让告警变少,而是让告警在到达人之前已经完成归类、过滤和优先级排序。
二、告警分组:用group_by合并通知流
分组机制的核心思想是把同一时间段内、带有相同标签组合的告警合并成一条通知。Alertmanager的group_by参数决定按哪些标签进行聚合。比如设置group_by: ['alertname', 'cluster']后,所有告警名称相同且集群标签相同的告警会进入同一个组。这样当某个集群中的多个节点同时触发磁盘使用率告警时,值班人员只会收到一条包含多个实例的通知,而不是每个实例各发一条。
分组并不是越粗越好,也不是越细越好。如果只按alertname分组,那么不同集群之间的同名告警会被错误地合到一起,接收者可能无法快速判断故障范围。如果按instance分组,又会导致每个实例单独通知,分组效果很差。比较合理的做法是结合团队职责和故障影响范围来选择分组键。例如基础设施团队接收节点级告警时,可以按datacenter和rack分组;应用团队接收服务级告警时,可以按service和environment分组。分组键需要稳定,避免使用每次变更的标签,否则会导致同一故障不断创建新组。
除了分组键,三个时间参数也会直接影响通知节奏。下面的配置展示了一个典型的分组路由:
route:
group_by: ['alertname', 'cluster']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: team-devops
routes:
- match:
severity: critical
group_by: ['instance']
repeat_interval: 1h
group_wait表示首次收到该组告警后等待多久再发送,目的是等待短时间内陆续到达的同组告警。如果设置过短,后续告警会形成新的通知;如果设置过长,又会拖延关键告警的触达。group_interval表示该组已经发送过一次通知后,组内新增告警再次触发发送的最小间隔。repeat_interval则是完全没有新增告警时,是否重复提醒的间隔。针对严重级别告警,可以单独缩短重复间隔,避免重要故障只通知一次后被忽略。
分组能够显著减少通知数量,但它不会删除任何告警信息。当一组告警被发送时,Alertmanager会列出该组内所有告警的当前状态,因此接收者仍然能看到具体涉及哪些实例。这一点非常重要:分组只是改变了告警呈现形式,而不是丢弃信息。如果团队中某些成员只关心特定实例,可以在接收端再做一次过滤,但不应破坏发送前的聚合关系。
三、告警抑制:屏蔽低优先级派生告警
抑制机制处理的是因果关系明确的告警。当高级别告警已经触发时,低级别派生告警通常不需要发送,因为处理根因问题的同时,派生问题也会随之恢复。例如节点宕机会触发该节点上所有服务的不可达告警,但接收者只需要知道节点宕机即可,继续发送几十条服务不可达通知并没有额外价值。抑制规则可以让Alertmanager在源告警存在期间,自动屏蔽匹配目标条件的告警。
在Alertmanager中,抑制由inhibit_rules定义。每条规则包含源匹配、目标匹配和相等标签。源匹配用来识别根因告警,目标匹配用来识别需要被屏蔽的派生告警,equal则要求源告警和目标告警在这些标签上完全相同,抑制才会生效。下面是一个常见配置:
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['instance']
这段配置的含义是:当同一实例上存在严重级别的告警时,该实例上所有警告级别的告警都会被抑制。比如实例因为内存耗尽触发严重告警,同时该实例上的应用心跳超时触发警告,那么心跳超时通知就不会再发送。值班人员只需要处理内存耗尽这个根源。抑制规则可以设置多条,例如数据库整机宕机抑制所有依赖数据库的服务告警,也可以从网络设备告警抑制其下联主机的不可达告警。
使用抑制时需要特别小心两个问题。第一是源告警必须具备足够的可靠性。如果源告警因为规则错误或采集抖动经常误触发,那么它抑制掉的目标告警也会随之被错误隐藏。第二是抑制范围不能过宽。例如使用severity作为唯一匹配条件时,可能把不同业务、不同实例的告警都抑制掉。通常需要通过equal或更精细的matchers将抑制范围限制在同一实例、同一集群或同一服务内,防止根因范围扩大化。
与分组相比,抑制是真正丢弃通知,因此它更适合有明确因果链路的场景。分组解决的是多条无关告警同时到达的问题,抑制解决的是多条关联告警重复表达同一故障的问题。两者可以同时使用:先通过抑制过滤掉派生告警,再通过分组把剩余告警合并发送,通知量会大幅下降。
四、告警静默:在计划窗口主动关闭通知
静默机制用于计划内变更或已知故障处理期间,主动阻止特定告警发送。与抑制不同,静默不依赖源告警是否存在,而是基于时间窗口和标签匹配直接暂停通知。例如数据库迁移安排在凌晨2点到4点,这期间数据库连接失败是预期现象,不应该继续打扰值班人员。通过创建静默规则匹配相关数据库实例标签,并设置起止时间,Alertmanager会在窗口内完全忽略这些告警。
静默可以通过Alertmanager的Web界面创建,也可以通过API进行自动化操作。在自动化运维平台中,通常会在发布系统或变更系统里集成静默API,当变更开始前自动创建静默,变更结束后自动过期。下面是一个使用curl创建静默的示例:
curl -X POST http://alertmanager:9093/api/v2/silences \
-H "Content-Type: application/json" \
-d '{
"matchers": [
{"name": "cluster", "value": "prod", "isRegex": false},
{"name": "service", "value": "order", "isRegex": false}
],
"startsAt": "2025-01-01T22:00:00Z",
"endsAt": "2025-01-01T23:30:00Z",
"createdBy": "变更平台",
"comment": "订单服务数据库维护窗口"
}'
静默的匹配条件由一组matchers构成,每个匹配器指定标签名、标签值和是否启用正则。上面的示例只匹配生产集群中的订单服务,不会影响其他服务。静默窗口不宜设置得太长,否则可能掩盖计划外的新故障。如果一个静默窗口覆盖了一整天,而其中前两个小时维护已经结束,那么剩余时间内即使真的出现数据库故障也不会有人收到通知。因此静默应当尽量贴近变更时间,并在变更完成后及时取消。
静默的另一个常见用途是处理重复出现的已知问题。当某个告警已经被确认并进入排期修复阶段,可以先创建静默避免持续打扰。但这种方式不应成为常态,如果同一个静默规则反复创建,说明问题本身还没有得到彻底解决。应该通过标签和注释记录静默原因,并定期检查长期静默是否仍然合理。Alertmanager会保留静默记录,可以用于审计变更窗口是否与告警情况一致。
五、三者协同:设计可落地的告警策略
分组、抑制和静默并不是互相替代的关系,它们在告警链路中分别承担不同职责。实际治理告警风暴时,最有效的路径是先梳理告警标签体系,再基于标签体系配置分组和抑制,最后根据变更流程规范静默操作。标签是连接三者的基础,如果cluster、service、instance这些标签值不一致或缺失,分组就会漏掉同类告警,抑制规则也无法准确匹配,静默范围更难以控制。因此第一步不是直接修改Alertmanager配置,而是统一所有采集端和告警规则中的标签命名。
在标签统一的前提下,可以按照以下顺序逐步落地。首先为每个接收团队定义默认分组键,保证同类告警能够聚合。然后根据基础设施和应用的依赖关系建立抑制规则,先覆盖最明确的因果链路,例如节点宕机抑制其上所有服务告警、数据库集群主节点故障抑制从节点和连接池告警。最后将静默操作纳入变更流程,要求所有计划内维护在开始前创建静默。这样即使一次变更导致大量预期告警,也不会触发通知风暴。
还需要在接收端设置合理的重复提醒策略。对于严重告警,可以设置较短的repeat_interval,比如1小时重复一次,但对于警告级别告警,重复间隔可以拉长到4小时甚至更长。不要把所有告警都标记为严重,否则分组和抑制的效果会被高优先级通知淹没。比较好的做法是使用三个级别:严重、警告、提示。严重告警必须立即处理,警告告警在工作时间内处理,提示告警只进入看板,不发送即时通知。告警级别应根据业务影响而不是技术指标绝对值来定义。
最后要持续观察告警统计指标,例如每天通知总数、重复通知比例、抑制命中次数、静默窗口覆盖时间等。通过分析这些数据可以判断哪些分组键划分不合理、哪些抑制规则没有生效、哪些静默窗口持续时间过长。告警治理是一个迭代过程,随着系统规模变化,依赖关系也会变化,分组键和抑制规则需要定期评估调整。只有把分组、抑制与静默结合起来,才能让告警系统在真实故障中保持清晰、有序和可操作。