凌晨2点17分,核心支付链路挂了。

MySQL主库连接超时,订单服务全面降级,交易成功率从99.9%断崖式跌到67%。系统里告警在疯转——Prometheus的Alertmanager队列里塞了47条待推送告警。但钉钉群安安静静,没有一条消息响起来。

40分钟后,值班同学被业务方电话叫醒。事故复盘时才发现:Alertmanager的钉钉webhook在凌晨1点52分就因为网络抖动断连了,重试策略配了但没生效,告警全部卡在队列里,一条都没推出去。

一、为什么告警会“漏推”?

那次事故的根因不复杂,但暴露的问题很典型。

Alertmanager本身不原生支持钉钉webhook,需要通过prometheus-webhook-dingtalk等中间件做转发。一旦中间件挂掉、网络闪断、或者webhook地址被钉钉限流,告警就卡在半路。

更麻烦的是,当时我们只配了一个钉钉机器人——单点,没有备用通道-。Alertmanager默认的重试机制在特定场景下会失效,告警就安静地躺在后台,等运维发现的时候故障已经持续了很长时间。

那次P0事故之后,我们重新设计了基于Hermes+Alertmanager的钉钉告警体系。以下是落地过程中总结的几个关键点。

二、分级路由:P0走三通道,P3走钉钉群

告警分级是告警体系的第一道防线-。我们的分级逻辑如下:

  • P0(严重) :服务不可用、核心链路中断、数据丢失风险。必须同时走钉钉+电话+短信三通道,确保凌晨也能触达值班人
  • P1(高) :服务降级、错误率突增、大量超时。走钉钉@值班人+企业微信群
  • P2(中) :性能劣化、资源水位偏高。走钉钉群消息
  • P3(低) :日常提醒、非紧急告警。走钉钉群消息,不@任何人

Alertmanager的route配置核心是按severity标签做路由分发

route:
  group_by: ['alertname', 'severity']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 12h
  receiver: 'default'
  routes:
  - match:
      severity: 'p0'
    receiver: 'p0-receiver'
    continue: true
  - match:
      severity: 'p1'
    receiver: 'p1-receiver'

P0走单独的高优先级receiver,配置钉钉+电话+短信三个渠道-。P1以下走常规钉钉群。

三、去重与收敛:别让告警风暴把钉钉群炸了

一个核心服务挂掉,可能会触发上下游几十个关联告警-。如果不做收敛,钉钉群会在几分钟内被几百条消息刷屏——真正重要的那条反而被淹没了。

Alertmanager的收敛靠三个参数控制:

  • group_by:按什么维度聚合。我们配置group_by: ['alertname', 'cluster'],同一个告警名称+同一个集群的告警合并成一条推送
  • group_wait:首次告警触发后等待多久再发送。配10秒,让同一批告警有时间聚合
  • group_interval:同一组告警的发送间隔。配5分钟,避免同一故障反复刷屏

另外配了inhibit_rules(抑制规则)——如果某个服务已经触发了P0级别的“服务不可用”告警,那么同服务相关的P2/P3告警自动被抑制,不重复推送。

四、多通道备份:不能让钉钉成为单点故障

那次P0事故最痛的教训就是单点。现在的方案是:

  • 主通道:钉钉群机器人(prometheus-webhook-dingtalk中间件转发)
  • 备用通道:企业微信群机器人
  • 应急通道:电话告警(仅P0)

Alertmanager配置多个receiver,路由规则里P0同时推送给钉钉和企业微信两个渠道。任何一个渠道挂了,另一个还能顶上。

五、消息模板:让告警一眼就能看懂

告警消息如果只是一堆JSON,值班人还要花时间解读——40分钟里哪怕省下5分钟解读时间都可能是救命稻草。

我们重写了Alertmanager的钉钉消息模板,每条告警必须包含:

  • 告警名称和严重等级(P0/P1/P2)
  • 故障实例(哪个服务、哪台机器)
  • 触发时间与持续时长
  • 当前指标值(比如错误率从1%飙到23%)
  • 直达Grafana面板的链接

用Go template写模板,钉钉消息渲染成卡片式富文本,一眼就能看清问题在哪。

六、健康检查:告警通道本身也要被监控

这是最后一道防线——告警通道本身挂了怎么办?

我们在prometheus-webhook-dingtalk服务上额外暴露了一个健康检查端点,用Blackbox Exporter定期探测。如果中间件服务不可用,触发一条独立的“告警通道异常”告警,走备用渠道通知运维。

另外,在钉钉群里配置了一个“心跳告警”——每天早上8点自动推送一条测试告警。如果哪天没收到,说明通道有问题,不等故障发生就能提前发现。

七、写在最后

那次40分钟的P0漏推事故之后,我们花了三周时间把告警体系从头到尾重构了一遍。现在回头看,告警建设的核心其实就三件事:

第一,分级。P0和P3不能走同一条路。越紧急的告警,通道越冗余。

第二,收敛。不要让告警风暴把真正重要的信息淹没。group_by、group_wait、inhibit_rules配好,比堆更多的告警规则管用得多。

第三,备份。钉钉不是万能的,任何单一渠道都可能出问题。多通道冗余是P0告警的底线。

告警系统的价值不在于“能发出去多少条”,而在于“该收到的人是否在第一时间收到了”。如果40分钟后才被业务方电话叫醒,那这套告警系统跟没有也没什么区别。