AlertManager 告警收敛

解读

在国内一线互联网公司的 PHP 后端面试中,提到 AlertManager 通常不是考察你会不会写 PHP,而是考察你对“可观测性”与“稳定性保障”体系的理解深度。告警收敛(Alert Convergence)是 Prometheus + AlertManager 这套云原生监控栈里的核心概念,其本质目标是:在分布式、高并发、多服务、多集群的环境下,把海量、重复、风暴级的告警事件压缩成“少而精、可定位、可处理”的工单,避免“告警轰炸”导致研发麻木、错失真正的故障。PHP 作为 Web 主力语言,线上服务往往数量庞大、发布频繁,若告警策略设计不好,一次发布就可能触发几千条告警,因此面试官希望听到你“从业务代码到监控配置”都能闭环思考的答案。

知识点

  1. 告警收敛的四大手段:分组(Group)、抑制(Inhibit)、静默(Silence)、去重(Dedup)。
  2. AlertManager 配置结构:global、route、group_by、group_wait、group_interval、repeat_interval、inhibit_rules、silences。
  3. 收敛与“告警等级”挂钩:P0(核心业务不可用)、P1(功能受损)、P2(一般异常),不同等级对应不同的收敛策略。
  4. 标签设计规范:service、cluster、severity、instance、alertname 五元组;标签是收敛算法的唯一依据。
  5. 与 PHP 业务结合:
    • 在 Laravel 中通过 prometheus_exporter 中间件埋点,暴露 RED(Rate/Error/Duration)指标;
    • 利用 Swoole 协程 Hook 把错误码、慢日志实时写入 Redis Stream,供 Prometheus 拉取;
    • 上线灰度阶段自动写入带“canary=1”标签的指标,AlertManager 通过 inhibit_rules 让“canary”告警不通知到值班群,只记录。
  6. 国内落地细节:
    • 微信/飞书/钉钉群机器人只接收收敛后的“聚合卡片”,卡片里必须带“跳转链接→SLS 日志、Grafana 面板、Arms 追踪”;
    • 夜班禁止电话告警,需通过 AlertManager 的 time_interval 做夜间降权;
    • 金融合规要求告警留存 180 天,AlertManager 需开启持久化(amtool silence 导入 MySQL)。

答案

“告警收敛”指通过 AlertManager 把原始告警事件按业务维度压缩、去重、抑制,最终只产生可操作的、少量的高价值告警。具体分四步:

第一步,合理分组。
在 AlertManager 的 route 段设置
group_by: ['cluster', 'service', 'severity']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
含义:同一集群、同一服务、同一级别的新告警先等待 30s 看是否还有同类告警,5 分钟内再出现则直接附加到同一条通知,4 小时未恢复才重复发送。这样一次 MySQL 主库抖动只会收到一条聚合卡片,而不是 200 条“连接超时”。

第二步,抑制风暴。
使用 inhibit_rules:

  • source_match: {severity: 'critical', alertname: 'MySQLMasterDown'}
    target_match: {severity: 'warning'}
    equal: ['cluster']
    逻辑:只要 MySQL 主库宕机这一条 critical 告警出现,同一集群内所有 warning 级告警(如慢查询、连接池耗尽)全部静默,避免“一故障、千告警”。

第三步,灰度静默。
上线前通过 amtool silence create --start=$(date +%s) --duration=30m --comment='canary release' 'canary=1'
让灰度实例的告警只进 Prometheus 不进值班群,30 分钟观察期结束后自动解除。

第四步,通知模板收敛。
使用 Go Template 把同组告警聚合成一条飞书卡片:
标题:【P0】支付集群 3 个服务异常
内容:

  • 服务 A:99 条连接超时
  • 服务 B:12 条 5xx
  • 跳转:Grafana 面板、SLS 日志、Arms 追踪
    值班同学收到后 1 分钟就能定位到是 Redis 节点故障,而不是被 200 条短信轰炸。

通过上述四步,我们把一次故障产生的 1500+ 原始告警收敛成 1 条可操作卡片,告警处理时长从 45 分钟降到 5 分钟,同时值班满意度提升 40%。

拓展思考

  1. 多活架构下的“地域收敛”:
    在阿里云杭州+上海双活场景,若杭州机房出口光缆中断,AlertManager 会收到两地同时上报的“网络不可达”告警。此时可在 inhibit_rules 里增加
    equal: ['alertname', 'service']
    让上海机房告警抑制杭州同服务告警,避免“双机房、双通知”造成误判。

  2. 动态阈值与收敛联动:
    使用 Prometheus Adapter + HPA 做弹性伸缩时,阈值随 Pod 数动态变化。若阈值刚调整 30 秒内就触发告警,很可能是阈值漂移而非真故障。可在 PHP 业务代码里写一条 /metrics 接口暴露 current_threshold,AlertManager 通过标签匹配做“阈值变更静默”,30 秒后再评估是否发送。

  3. 告警收敛的“反向”思考——防止过度收敛:
    曾经出现“同一分组内隐藏了不同根因”的案例:

    • 00:01 Redis 超时导致缓存穿透;
    • 00:03 MySQL 慢查询导致连接池占满;
      由于都在同一服务、同一 severity,被收敛成一条,结果 RD 只修 Redis 没管 MySQL,30 分钟后再次告警。
      解决方式:把“根因标签”code_root_cause 也写进 group_by,确保不同根因分开发送。
  4. 与 PHP 业务可观测性深度整合:
    在 Laravel 的 ExceptionHandler 里统一捕获异常,把异常哈希(exception_hash)写进 Prometheus 标签,AlertManager 通过
    group_by: ['exception_hash']
    实现“同一异常只告警一次”,避免循环报错刷屏。

  5. 合规与审计:
    金融、政府项目要求告警操作可审计。可在 AlertManager 外再部署一个“告警网关”,所有收敛后的告警先写进 MySQL(字段:alert_id、收敛规则、操作人、时间),再飞书通知;网关提供 RESTful API 供合规平台拉取,PHP 端用 Guzzle 异步上报即可。