跳到主要内容

Alertmanager 核心概念与架构

Prometheus 系列的告警规则与 Alertmanager一篇讲过告警规则的写法和 alertmanager.yml 的入门配置,这篇开始换个主角:把 Alertmanager 本体拆开,讲清楚它到底在告警链路里扮演什么角色、内部是怎么处理一条告警的。后续的配置细节、高可用部署都建立在这篇的心智模型上。

为什么告警需要「调度台」​

先想象一下没有 Alertmanager 的时代:Prometheus 评估出告警,直接发邮件。这套直连方案在小团队能凑合,规模一上来立刻崩坏:

  • 告警轰炸:一台宿主机宕机,上面 20 个 Pod 各自触发一条告警,值班人瞬间收到 21 封邮件。每一条单独看都没错,合在一起就是噪音。
  • 重复发送:Prometheus 每次评估都推一次 firing 状态的告警,不做收敛的话,一条不恢复的告警按评估间隔不停轰炸。
  • 没有分级:磁盘 85% 和数据库主库挂了走同一个通道、同一个音量。什么都急等于什么都不急。
  • 没有豁免机制:计划内发布、维护窗口,只能把告警规则整个删掉,事后还经常忘记加回来。

Alertmanager 就是为解决这些问题生的。它的定位一句话:Prometheus 只管「该不该报」,Alertmanager 决定「报给谁、怎么报、什么时候闭嘴」。它是告警链路上的调度台——上游来的原始告警在这里被分组、去重、按路由分发、该压制的压制,最后组装成一条像样的通知发出去。

与 Prometheus 的分工​

这是最容易被初学者搞混的地方,先把边界划死:

职责PrometheusAlertmanager
评估告警规则是,按 evaluation_interval 周期性求值 PromQL否,完全不管表达式
维护告警状态是,inactive → pending → firing 都在它内存里否,只接收已经 firing 的告警
接收告警推送方接收方,暴露 /api/v2/alerts 等接口
分组与去重否是,核心职责
决定发给谁否是,路由树按标签分发
抑制与静默否是
发通知否是,邮件、webhook、PagerDuty 等

记住这个分工,排障时就不容易找错地方:触发条件不对去查 Prometheus 的规则,通知行为不对去查 alertmanager.yml。

值得一提的是,Alertmanager 的入口并不只对 Prometheus 开放——任何能按 API 格式 POST 告警的系统都能接入。Grafana 自研的告警引擎就可以把通知交给统一的 Alertmanager 处理(或者直接用它内嵌的 Alertmanager 实现),多个告警源头共用一个调度台,路由和静默只维护一份,详见 Grafana 告警与通知。

一个入口的好处

告警源可以有很多个(多套 Prometheus、Grafana、业务自研检测),但通知策略只应该有一份。所有告警汇到同一个 Alertmanager(集群),分组、抑制、静默全局生效,不会出现「Prometheus 的告警静默了,Grafana 的还在响」这种分裂状态。

整体架构​

Alertmanager 内部的处理流水线一图看懂:

Alertmanager 架构

一条告警从进来到出去,数据流是这样的:

  1. 接收:上游(Prometheus、Grafana、任何客户端)把 firing/resolved 告警 POST 到 /api/v2/alerts。Alertmanager 不做任何条件判断,照单全收。
  2. 去重:多条相同标签集的告警合并成一条(多副本 Prometheus 场景的关键,下文细讲)。
  3. 分组:按路由节点上的 group_by 标签,把告警归进不同的组。一组共享同一条通知。
  4. 路由:沿 route 树从上到下匹配,决定这条告警归哪个接收器(receiver)、走哪个分组节奏。
  5. 抑制与静默:通知发出前的最后两道闸——被抑制的告警不发声,命中静默规则的告警不发声。
  6. 通知:组内告警攒够等待时间(group_wait)后,组装成消息发给接收器;之后按 group_interval 和 repeat_interval 的节奏续发。

整个过程可以理解为一条流水线:告警是原料,路由树是分拣线,分组是打包,抑制和静默是质检拦截,receiver 是快递发货。

Alertmanager 是无状态的调度器

Alertmanager 自己不存指标、不评估表达式,它的全部「记忆」就是当前活跃告警的集合、静默列表和通知日志(nflog,记录哪组告警发到什么阶段了)。这几个状态通过 gossip 在集群内同步,下文高可用一节细说。

五大核心机制​

这五个机制是 Alertmanager 的全部精华,后面所有配置文章都是在给它们填参数。逐个建立直觉:

分组(group_by)​

把「标签相似」的告警打包成一条通知。路由节点上配 group_by: ["alertname", "namespace"],同一告警名、同一命名空间下的 N 条告警合成一条消息发出。

一句话例子:K8s 集群里 shop 命名空间同时 15 个 Pod 报 KubePodCrashLooping,不分组是 15 条消息,按 namespace 分组后值班人只收到一条「shop 命名空间 15 个 Pod 崩溃」的汇总。

去重(dedup)​

多个 Prometheus 副本(HA 部署)会向 Alertmanager 推送完全相同的告警。Alertmanager 按告警的标签集指纹判断:同一条告警来几次只处理一次,通知只发一份。

一句话例子:两台 Prometheus 各自评估出 NodeDown 告警推过来,值班人只收到一条通知,而不是重复的两条。

去重的前提:标签完全一致

两个 Prometheus 副本如果外部标签(external_labels)配置不同,推来的告警标签集就有差异,指纹不同,去重失效,一条告警变两条。HA 场景下 external_labels 要保持一致——这是部署篇会展开的坑。

路由(route 树)​

一棵按标签匹配的路由树,从根节点开始自上而下匹配:每个节点声明匹配条件(matchers)和目标接收器,告警标签命中哪个节点就走哪个接收器。默认命中即停,continue: true 可以让一条告警继续往下匹配、同时进入多个渠道。

一句话例子:severity=critical 的走电话值班群,severity=warning 的走 IM 普通群,team=infra 的不管级别都抄送基础设施组——三层逻辑就是路由树上的三个分支。

抑制(inhibit)​

「根因已经报了,衍生的就别报了」。inhibit_rules 定义:当满足 source 条件的告警正在 firing 时,压掉满足 target 条件的告警,两边按 equal 里列的标签对齐到同一对象上。

一句话例子:NodeDown(critical)已经着火,那么同一台机器上的 DiskUsageHigh(warning)就没有再报的必要——机器都没了,磁盘高不高级别没意义。

静默(silence)​

「我知道它在报,这段时间别烦我」。按标签匹配器圈一批告警,在起止时间内照常评估、照常 firing,但不发通知,过期自动失效。

一句话例子:周末凌晨给 shop 命名空间做发布,提前打一条 2 小时的静默,发布期间的告警全部消音,窗口结束自动恢复——不用改任何配置,也不会忘。

抑制 vs 静默

容易混。一句话区分:抑制是告警压告警(A 在着火,所以 B 闭嘴),静默是人主动消音(我知道 B 会响,先捂上)。抑制是自动的逻辑关系,静默是手动的运维操作。

高可用与 gossip​

单点 Alertmanager 是告警链路的隐形单点:Prometheus 做了双副本、Exporter 层层监控,结果 Alertmanager 只有一台——它挂了,所有告警静默失败,你以为系统岁月静好,其实是没人报信。

Alertmanager 的高可用方案很简单:起多个实例组成集群,用 gossip 协议互相同步状态。同步的内容包括:

  • 通知日志(nflog):哪个告警组已经发过通知、发到哪个阶段。保证一条通知不会被集群里多个实例重复发出。
  • 静默列表:在任何一个实例上创建的静默,几秒内扩散到整个集群。

每个实例独立接收告警、独立做分组路由,靠 nflog 的 gossip 收敛避免重复通知。Prometheus 侧把所有实例地址都配上,推给任何一个(或全部)都行。

gossip 不是强一致

gossip 是最终一致的:静默创建后有几秒的扩散窗口,极端情况下个别实例可能短暂多发一条通知。对告警场景来说,「偶尔多发一条」远好过「一条都不发」,这是有意的取舍。

告警的生命周期​

把 Prometheus 侧和 Alertmanager 侧串起来,一条告警的完整一生:

Prometheus 侧:
表达式不成立 → inactive
表达式成立,未满 for → pending(不推送给 Alertmanager)
持续满 for → firing(开始推送)

Alertmanager 侧:
接收 firing 告警
→ 去重合并
→ 按 group_by 归组,等待 group_wait(攒同组告警)
→ 路由匹配,选定接收器
→ 检查抑制规则和静默(命中则本次不发声)
→ 发出通知

恢复:
表达式不再成立 → resolved 状态推给 Alertmanager
→ 接收器配了 send_resolved 则发一条「已恢复」通知
→ 告警从活跃列表移除

几个容易记错的点:

  • pending 不会推送:只有 firing 状态的告警才会发给 Alertmanager,所以 for 期间的「预热」在 Alertmanager 侧是完全不可见的。
  • inactive/pending/firing 是 Prometheus 侧的状态机,规则评估、for 防抖的细节见 告警规则与 Alertmanager,这里不重复。
  • resolved 通知默认不开:每种接收器类型有自己的默认值(webhook 默认发、邮件默认不发之类),想要「恢复提醒」就在接收器上显式配 send_resolved: true。
通知延迟排障口诀

告警 10:00:00 触发、10:00:45 才收到通知,先别慌:去 Prometheus 的 Alerts 页确认 firing 时间点,差值对得上 group_wait(默认 30s)就是分组攒单的正常等待,不是链路故障。

小结​

  • 直连发邮件的时代死于轰炸、重复、没分级;Alertmanager 是告警调度台:Prometheus 管「该不该报」,它管「报给谁、怎么报、什么时候闭嘴」。
  • 分工边界:规则评估和状态机归 Prometheus,分组、去重、路由、抑制、静默、发通知归 Alertmanager;改通知行为别去动规则文件。
  • 数据流一条线:接收 → 去重 → 分组 → 路由 → 抑制/静默 → 通知。
  • 五大机制一句话:分组打包、去重去重、路由分拣、抑制根因压衍生、静默手动消音。
  • 单点 Alertmanager 是告警链路的隐形单点;多实例靠 gossip 同步通知日志和静默列表,最终一致。
  • 生命周期里 pending 不推送、resolved 默认不发恢复通知,通知延迟先对 group_wait。

概念齐了,下一篇安装与快速上手直接把 Alertmanager 跑起来:最小配置、连上 Prometheus、端到端发出第一条测试告警。