跳到主要内容

抑制、静默与免打扰

上一篇路由与接收器解决了「告警发给谁、按什么节奏发」,这篇解决另一半问题:什么时候干脆不发。节点宕机连带出的几十条服务告警、发版窗口的误报、凌晨三点不重要的 warning——这些场景的正确解法都不是删规则或关监控,而是 Alertmanager 提供的三种「闭嘴」机制:抑制(inhibit)、静默(silence)、免打扰时段(mute timings)。示例基于 Alertmanager 0.27+。

三种「闭嘴」机制的分工​

三者都能让通知停下来,但触发方式、作用范围、使用场景完全不同,别混用:

机制触发方式典型场景生命周期
抑制 inhibit_rules自动:另一条告警着火时压住本告警根因压住衍生:节点 down 压住该节点上的服务告警配置常驻,条件成立即生效
静默 silence人工:手动或流水线创建,带起止时间发版窗口、计划内维护、已知问题修复中到期自动过期
免打扰 mute timings自动:按固定时间表生效夜间/周末不发 warning、工作时间才发通知配置常驻,周期性生效

一句话区分:抑制是告警压告警,静默是人按时间段圈一批告警消音,免打扰是按日历表捂嘴。

别再用删规则消音

「这条告警太吵,把规则注释掉」是最常见的反模式:规则删了连评估都没了,事故复盘时那段时间是监控盲区,而且注释掉的规则九成概率忘了加回来。觉得吵就用上面三种机制,告警照常评估、照常能在页面看到,只是不发通知——「知道它在报」和「被它吵」是两回事。

抑制 inhibit_rules​

抑制规则的逻辑:当「根因告警」正在着火时,同范围的「衍生告警」就别发了——通知里只留下能定位根因的那条,其余噪音压住。语法三要素:

  • source_match / source_matchers:谁是根因(满足条件的告警着火时,压制生效);
  • target_match / target_matchers:谁被压制;
  • equal:源和目标必须在哪些标签上取值相同才构成「同一事件」。

*_match 是旧写法(值可以是正则),*_matchers 是 0.22+ 的新写法(= != =~ !~ 四种操作符),新配置统一用后者。

实战一:critical 压住同名 warning​

最经典的抑制:同一实例上同名告警已经报了 critical,对应的 warning 就是重复噪音:

inhibit_rules:
- source_matchers:
- severity="critical"
target_matchers:
- severity="warning"
equal: ["alertname", "instance"]

读法:存在一条 severity 为 critical 的 firing 告警,且目标告警与它 alertname、instance 都相同,则这条 warning 被压制。同一个错误率指标打了 warning/critical 两档阈值(>5% 警告、>20% 紧急)时,破了 critical 之后 warning 那条就不用再吵一遍。

实战二:节点 down 压住节点上所有服务告警​

inhibit_rules:
- source_matchers:
- alertname="NodeDown"
target_matchers:
- severity=~"warning|critical"
equal: ["instance"]

节点宕机时,这台机器上的磁盘告警、进程告警、服务延迟告警会成片触发——但根因就一个:机器没了。上面这条规则让 NodeDown 着火期间,同 instance 上的其他告警全部压住,值班人只收到一条「10.0.1.11 节点失联」,而不是四十条衍生告警。

equal 的标签名两边必须严格一致

抑制不生效的第一大原因:equal: ["instance"] 要求源告警和目标告警都有 instance 标签且值相同。常见翻车:NodeDown 告警上的标签叫 instance,但应用告警上对应的是 pod 或 host——标签名对不上,一条都压不住,故障时照样被刷屏。配置前先在 Prometheus 的 Alerts 页面把两边告警的实际标签集抄下来对一遍。

抑制是在通知发送前生效的:被压制的告警照样 firing、照样在 Alertmanager 页面可见,状态显示为 suppressed,只是不进通知。根因告警恢复后,衍生告警如果还在着火,会恢复通知——行为完全自动,不用人管。

静默 silence​

静默是人工圈一批告警、指定起止时间,期间不发通知,到期自动失效。与抑制的区别:抑制由另一条告警自动触发,静默由人显式创建。

页面创建​

Alertmanager Web UI(默认 9093 端口)→ Silences → New Silence,填四样东西:

  1. 匹配器:标签条件,圈定静默范围,多条是 AND 关系,支持正则;
  2. 起止时间:Starts at / Ends at,结束后自动过期;
  3. 创建人和备注:备注必填,写清楚为什么静默——半年后翻历史全靠它。

页面上的 Preview 功能会实时显示当前哪些告警会被这条静默罩住,建之前先看一眼预览,确认没多圈。

amtool 命令行​

发布流水线、值班交接这类自动化场景用 amtool(Alertmanager 自带 CLI,--alertmanager.url 指定地址):

# 创建:shop 命名空间静默 2 小时
amtool silence add namespace="shop" \
--alertmanager.url=http://alertmanager:9093 \
--duration=2h \
--author=deploy-bot \
--comment="shop 服务例行发布窗口"

# 正则匹配:web- 开头的所有实例
amtool silence add 'instance=~"web-.*"' severity="warning" \
--alertmanager.url=http://alertmanager:9093 \
--duration=1h --author=sre --comment="web 集群滚动升级"

# 查询当前生效的静默
amtool silence query --alertmanager.url=http://alertmanager:9093

# 提前结束某条静默(参数是 query 出来的 silence ID)
amtool silence expire 3d4f5a6b-1111-2222-3333-444455556666 \
--alertmanager.url=http://alertmanager:9093

silence 是实例级状态,不是配置:它存在运行中的 Alertmanager 里,写在配置文件的只有路由和抑制。HA 集群(多实例 gossip 组网)里 silence 会通过 gossip 协议同步到所有节点,在任意一个节点上创建即可,不用每个节点建一遍。

维护窗口的正确姿势​

发版、变更、计划内维护的标准流程是:发布流水线在动手前自动打静默,窗口结束自动过期。以按 service 标签圈定为例,发版脚本里加一步:

amtool silence add service="order-api" environment="prod" \
--alertmanager.url=http://alertmanager:9093 \
--duration=1h \
--author=ci-pipeline \
--comment="order-api v2.3.1 发布,预计 30 分钟"

要点:

  • 按标签尽量圈小:能按 service 圈就别按 namespace 圈,能按 namespace 圈就别整个集群全静默——静默范围之外的告警在窗口期还得正常工作,隔壁服务真炸了不能陪着一起哑;
  • duration 留点余量但别离谱:预计 30 分钟的发布静默 1 小时合理,直接静默一天等于当天裸奔;
  • 发布提前结束可以 amtool silence expire 手动收掉,不收也会自己过期,无妨。

反面教材列两个:发布前把 Prometheus 停掉(整个监控瞎了,不只是告警停了);把告警规则注释掉发完再加回来(总会有一天忘了加回来)。

免打扰时段 mute timings​

免打扰解决的是周期性的捂嘴:「warning 夜里别发,白天再说」「周末不处理非紧急告警」。分两步:先在 time_intervals 里定义时段,再把时段挂到路由的 mute_time_intervals 上:

time_intervals:
- name: offhours # 夜间 + 周末:非工作时间
time_intervals:
- times:
- start_time: 00:00
end_time: 09:00
- start_time: 21:00
end_time: 24:00
weekdays: ["monday:friday"]
location: Asia/Shanghai
- weekdays: ["saturday", "sunday"]
location: Asia/Shanghai

route:
receiver: default
routes:
# warning 在非工作时间静音,上班时间正常发
- matchers: [severity="warning"]
receiver: warning-dingtalk
mute_time_intervals: ["offhours"]
# critical 不受免打扰影响,任何时候都发
- matchers: [severity="critical"]
receiver: oncall-phone

时段定义的字段说明:

  • times:一天内的时间段,可多个区间,HH:MM 格式;
  • weekdays:星期几生效(monday:friday 区间写法或逐天列出);
  • days_of_month、months、years:更细的日历控制,一般场景用不上;
  • location:时区,IANA 格式。不显式写就是 UTC,国内环境务必写 Asia/Shanghai,不然「晚 9 点」实际是凌晨 5 点。

注意免打扰期间告警不是延迟到白天补发,而是这段时间内根本不通知——9 点时段结束那一刻还在 firing 的告警,会按组的正常节奏(group_interval/repeat_interval)发出,相当于「白天补上了昨晚的存量」,但凌晨 3 点触发、5 点恢复的短告警就永远发不出去了。所以免打扰只挂 warning 这种「不需要半夜处理」的级别,critical 永远直通。

active_time_intervals 是反向用法:指定时段内才发,其余时间静音。比如「只在工作时间发业务报表类告警」,挂一个 active_time_intervals: ["workhours"] 即可。它和 mute_time_intervals 可以同时挂在一个路由上,两个条件都满足才发(既在活跃时段内、又不在静音时段内)。

路由节点上的时段是继承的

mute_time_intervals 挂在父路由,子路由默认继承。想让 critical 不被夜间静音影响,要么别把时段挂根路由,要么在 critical 子路由上显式置空覆盖。

常见坑​

silence 到期后的「复活惊吓」。 静默只是按住通知,没按住评估。建了一条 2 小时的静默去修问题,2 小时到了问题没修完,告警立刻「复活」炸出来。预防办法:duration 按保守估计给,窗口内没修完就提前续一条新的静默,别赌「应该能修完」。

inhibit 的 equal 标签两边不一致。 前面强调过,这里再敲一次:这是抑制规则配了跟没配一样的头号原因。排查路径固定为——Alertmanager 页面看目标告警状态是不是 suppressed;不是的话,把源告警和目标告警的完整标签集拉出来,逐字段对照 equal 里列的每个标签:名字是否都存在、值是否完全一致(注意 instance="10.0.1.11:9100" 和 instance="10.0.1.11" 不算相同)。

mute timing 的时区。 时段默认按 UTC 算,配置里不写 location 字段,国内「夜间」的定义会整体偏 8 小时。所有 time_intervals 一律显式写 location: Asia/Shanghai,写完之后用 amtool config show 或 reload 后看 Web UI 的 Status 页确认时段解析结果。

silence 建太宽把别的告警吞了。 图省事写 severity="warning" 单条件静默,把窗口期内所有 warning 都捂住了——隔壁团队数据库连接池打满也在里面,没人知道。建静默时匹配器至少两个维度(服务/命名空间 + 环境),并且必须先看页面预览确认覆盖范围,预览里出现不该静默的告警就收窄匹配器。

小结​

  • 三种机制分工:抑制是告警压告警(自动、常驻)、静默是人圈范围定时消音(人工、有期限)、免打扰是按日历周期性捂嘴(自动、常驻)。
  • 抑制三要素:source_matchers 定根因、target_matchers 定被压者、equal 定对齐标签;equal 的标签名和值两边严格一致才生效。
  • 静默是实例级状态,gossip 集群自动同步;维护窗口的正确姿势是流水线自动 amtool silence add,按标签圈小范围,看预览再提交。
  • 免打扰走 time_intervals 定义时段、路由挂 mute_time_intervals;location 显式写时区;免打扰是「不发」不是「延迟发」,只挂 warning,critical 直通。
  • 无论哪种机制,告警本身照常评估照常 firing,只是不发通知——复盘时「没收到告警」不等于「没有问题」。

至此 Alertmanager 的告警处理三板斧(路由、抑制/静默/免打扰)都齐了。下一篇讲通知模板:默认的告警文案太简陋,用 Go template 把通知渲染成人话。