Alertmanager · 从告警到通知

Prometheus 只管产生告警,把它变成「一条恰当的通知」是 Alertmanager 的活。这一章讲清它的四个核心机制——分组、抑制、静默、去重,以及为什么「分组」是防止通知风暴的关键。

作者 David更新于 第 35 篇(共 52 篇)

Alertmanager · 从告警到通知

一个容易被忽略的分工:Prometheus 只负责判断“告警该不该 firing”,它不发通知。把告警变成“一条恰当的、发给对的人的通知”是 Alertmanager(一个独立进程)的活。

为什么要拆成两个进程? 因为这是两件性质完全不同的事:前者是数据计算(周期性求值 PromQL),后者是通知路由与降噪(分组、去重、限流、对接十几种通知渠道)。拆开让 Prometheus 保持简单,也让多个 Prometheus 可以共用一套 Alertmanager——这在分片部署时是必需的。

四个核心机制

Alertmanager 干四件事,每件都是为了让人少收到通知,但不漏掉信息**:**

机制解决什么
分组(grouping)一次故障 = 一条通知,而不是 500 条
抑制(inhibition)根因响了,就别再报衍生问题
静默(silencing)维护窗口、已知问题,临时闭嘴
去重(dedup)多个冗余 Prometheus 报同一件事 → 只发一次

分组:最重要的那个

场景:一个机架断电,500 个实例同时不可达。

  • 没有分组:500 条 InstanceDown 通知,500 条短信。你的手机会爆炸,而且真正重要的信息(整个机架挂了)淹没在里面。
  • 有分组:1 条通知——“InstanceDown:500 个实例受影响,cluster=rack-7”。
route:
  group_by: ['alertname', 'cluster']    # 按这些标签分组
  group_wait: 30s        # 第一条到了先等 30s,看还有没有同组的
  group_interval: 5m     # 同组有新成员时,最多每 5m 再通知一次
  repeat_interval: 4h    # 同一组一直没解决,每 4h 提醒一次

三个时间参数的分工值得说清:

  • group_wait(30s) —— 第一条告警到达后,先憋一会儿,等同组的其他告警。这就是“500 条变 1 条”的关键:断电后 500 个实例的告警会在几十秒内陆续到达,憋 30 秒就能把它们攒成一组。代价是通知延迟 30 秒——值得。
  • group_interval(5m) —— 这一组已经通知过了,但又来了新成员(比如又挂了 20 台),多久通知一次更新。
  • repeat_interval(4h) —— 这一组啥也没变、一直没解决,多久再提醒一次。别设太短(会变成骚扰),也别太长(会被遗忘)。

group_by 选什么标签是个设计决策:

  • 太粗(group_by: ['alertname'])—— 不同集群的同名告警会被合并,你看不出是哪个集群。
  • 太细(group_by: ['alertname', 'instance'])—— 等于没分组(每个实例一组),500 条通知照旧。
  • 经验:按“同一个根因会同时触发的维度”分组——机架断电的共同点是 cluster,那就 ['alertname', 'cluster']。

group_by: [...] 里那个特殊值 '...' 表示“按所有标签分组”(即不分组),几乎总是错的——除非你真的想要每条告警一个通知。

抑制:别报衍生问题

场景:整个集群网络断了。于是:

ClusterDown          ← 根因
InstanceDown × 500   ← 衍生
HighErrorRate × 50   ← 衍生
LatencyHigh × 50     ← 衍生

人只需要知道第一条。 后面那 600 条是噪音,而且会掩盖根因。

inhibit_rules:
- source_matchers: [severity="critical", alertname="ClusterDown"]
  target_matchers: [severity="warning"]
  equal: ['cluster']       # 只抑制同一个 cluster 的

equal 是关键:它说“只有当两条告警的这些标签相同时,才抑制”。没有它,north 集群的 ClusterDown 会把 south 集群的告警也抑制掉——那就漏报了。

抑制是“告警拓扑知识”的编码:它要求你知道“什么会导致什么”。这也是它的局限——和“对原因告警”一样,你不可能列全所有因果链。所以抑制规则要少而精,只覆盖那些最明显、最常见的因果关系(集群 → 实例、实例 → 服务)。

静默与去重

静默(silencing) —— 临时闭嘴。典型用途:计划内维护、已知问题正在修、已经开了工单的告警。注意静默是有过期时间的——这是个好设计,防止“临时静默”变成永久失明(那是很多团队真实发生过的事故:某条告警被静默了两年,没人记得)。

去重(dedup) —— 生产环境通常跑多个冗余 Prometheus(它们抓同样的目标),于是同一条告警会被报好几次。Alertmanager 会按标签集去重,只发一次。这正是上一章说的“变化的值要放 annotations 不放 labels”的原因——放 labels 会破坏去重(每个 Prometheus 算出的 $value 略有不同 → 标签不同 → 被当成不同告警 → 你收到 N 条)。

Alertmanager 自己也支持集群模式(多个实例用 gossip 协议同步状态),保证它自己不是单点——监控链路上的每一环都不该是单点。

路由树:发给谁

route:
  receiver: 'default'
  group_by: ['alertname', 'cluster']
  routes:
  - matchers: [team="api"]        # API 团队的告警
    receiver: 'api-team'
    routes:                        # 嵌套:更细的规则
    - matchers: [severity="page"]
      receiver: 'api-oncall-phone'
  - matchers: [team="data"]
    receiver: 'data-team'

路由是一棵树**,告警从根往下走,匹配到就停(除非配了 continue: true)。**

这就是告警规则里那些 labels 的最终用途:team: api、severity: page 这些标签,在这里决定了这条告警去哪、通过什么渠道。

实战建议:让告警规则打上 team 标签,而不是在 Alertmanager 里维护一个巨大的“哪个 alertname 属于哪个团队”的映射。 前者是代码即配置(告警和归属写在一起,改服务时一起改),后者会迅速腐烂(没人记得更新那张表,新告警默认发给全世界)。

小结与下一章

Prometheus 判断告警,Alertmanager 送通知——拆开是因为这是两件性质不同的事,且能让多个 Prometheus 共用一套。四个机制:分组(最重要:机架断电 500 条 → 1 条;group_wait 憋一会儿攒同组、group_interval 有新成员时更新、repeat_interval 未解决时提醒;group_by 要按“同一根因会同时触发的维度”选,太细等于没分组)、抑制(根因响了就别报衍生的 600 条;equal 是关键,否则会跨集群误抑制 → 漏报;规则要少而精,因为你不可能列全因果链)、静默(有过期时间是好设计,防止“临时静默”变成永久失明)、去重(多个冗余 Prometheus 报同一件事;这正是“变化的值不能放 labels”的原因)。路由是树,匹配到就停——让告警规则自带 team 标签,别在 Alertmanager 里维护会腐烂的映射表。

机制都齐了。但还有个更根本的问题:告警多了,人就不看了。 下一章收尾第 5 部分。👉 告警哲学。

参考资料

  • [1] Alertmanager 官方文档:Configuration(route/group_by/inhibit_rules/silences)、HA 集群模式。
  • [2] 关联告警规则的 labels、阈值与分级。
  • [3] Prometheus 官方文档:Alerting overview。

本页目录