上一篇把该看的指标收敛到了十条以内,规则也按 RED / USE / 黄金信号归好了类。但规则配完只算走完一半,响不响、响几条、响给谁、多久响一次,全在 Alertmanager 这一层。
真实故障里更常见的场景:一台宿主机过载,五分钟内手机响了四十七次。前三次还看,第四次开始只划掉,第七次干脆把告警群静音。而等到真正需要处理的那一条进来时,已经没人看了。
问题不在告警规则,也不在采集链路。告警太多和没有告警,最终结果是一样的。
Alertmanager 提供了三种收敛手段:
| 对比维度 | 分组(group_by) | 抑制(inhibit_rules) | 静默(silence) |
|---|---|---|---|
| 解决的问题 | 同类告警合并成一条 | 根因告警发出时压住次生告警 | 计划内维护期间不通知 |
| 作用时机 | 通知前聚合 | 通知前拦截 | 通知前拦截 |
| 生效条件 | 标签相同即自动分组 | 源告警存在且标签匹配 | 时间窗口 + 标签匹配 |
| 生命周期 | 配置固化,长期生效 | 配置固化,长期生效 | 有明确起止时间,自动过期 |
| 典型场景 | 同一 job 的 10 个实例同时抖动 | 主机宕机时压住其上应用的连带告警 | 升级发版、数据库迁库 |
| 主要边界 | 只能合并,不能消除 | 只影响通知,不阻止告警产生 | 需人工创建,忘记清理会漏报 |
本篇只讲 Alertmanager,包括部署、路由树、分组、抑制、静默、通知模板,以及最后落到「怎么让这套体系长期不腐化」。告警规则详见前几篇告警表,本篇不再展开。
一、部署与最小可用配置
1、启动服务
第1篇已经给过启动命令:
bash
$ docker run -d --name alertmanager \
--rm \
--restart=no \
--network my-bridge \
-p 9093:9093 \
-e TZ=Asia/Shanghai \
-v /data/volumes/alertmanager:/etc/alertmanager \
prom/alertmanager:v0.33.1
注意:
-v挂载的是整个目录,不是单个 yml 文件。 目前包含templates等依赖文件,可简化挂载配置。- ⚠️
-e TZ=Asia/Shanghai不影响模板里的时间输出。 该参数只改容器系统时区,Go 模板里的.StartsAt仍然按 UTC 渲染。要让邮件显示北京时间,需要在模板里加偏移量,如{``{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}。
2、最小可用配置
参考示例:
yaml
# /etc/alertmanager/alertmanager.yml
global:
# 距上次收到告警超过该时间,即判定为已恢复
resolve_timeout: 5m
# SMTP 全局配置:465 是隐式 TLS 端口,require_tls 必须为 false
smtp_smarthost: 'smtp.aliyun.com:465'
smtp_from: '*****@aliyun.com'
smtp_auth_username: '*****@aliyun.com'
smtp_auth_password: '******'
smtp_require_tls: false
route:
# 根路由:兜底接收器,未被子路由匹配的告警都走这里
receiver: 'default'
group_by: ['job', 'svc']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receivers:
- name: 'default'
email_configs:
- to: '******@aliyun.com'
send_resolved: true
headers:
Subject: '[Prometheus][{{ .GroupLabels.severity }}] {{ .GroupLabels.job }} 告警'
html: |
{{ range .Alerts }}
告警名称: {{ .Labels.alertname }}
Job: {{ .Labels.job }}
Instance: {{ .Labels.instance }}
级别: {{ .Labels.severity }}
摘要: {{ .Annotations.summary }}
开始时间: {{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}
{{ end }}
3、验证
bash
$ docker exec -it alertmanager amtool check-config /etc/alertmanager/alertmanager.yml
Checking '/etc/alertmanager/alertmanager.yml' SUCCESS
Found:
- global config
- route
- 1 inhibit rules
- 2 receivers
- 1 templates
SUCCESS
- 该命令会把路由、接收器、抑制规则、模板文件一起校验。 输出里出现
- 1 templates说明模板文件被找到了;如果templates:指向的路径没挂载进容器,这里会直接报错,这一行输出等于一次挂载检查。 - 有错误时会打印行号和原因,并以非 0 退出码退出,可以直接塞进 CI。
4、热加载
bash
$ curl -XPOST http://127.0.0.1:9093/-/reload
Alertmanager 的热加载默认就开着。 这和 Prometheus 不同。
二、路由树:匹配、分组与三个时间参数
1、route 是一棵树
Alertmanager 的路由是一棵从根节点向下匹配的树:从上往下走,第一个匹配上的子路由决定这条告警走哪个接收器。
示例:
yaml
route:
receiver: 'default'
group_by: ['job', 'svc']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
# ============================================================
# 第一层:severity = critical
# 响应快、通道即时(企业微信/电话/Slack)
# ============================================================
- matchers:
- severity = "critical"
# 关键告警缩短首次等待和重复间隔
group_wait: 10s
group_interval: 3m
repeat_interval: 1h
receiver: "slack-all"
提示:match / match_re 自 v0.22.0 起已标记为 deprecated ,官方推荐改用 matchers。新语法支持四种运算符,可读性更好:
yaml
matchers:
- severity = "critical" # 等于
- job =~ "nginx|node" # 正则匹配
- env != "dev" # 不等于
- instance !~ "10\.0\.2\..*" # 正则不匹配
2、三个时间参数
以下三个参数是告警量的总闸门,配错任意一个都会让收敛效果打折。
| 参数 | 作用 | 默认值 | 生产建议值 | 风险点 |
|---|---|---|---|---|
group_wait |
首次告警后等多久再发,用于攒齐同组告警 | 30s | 30s(critical 可 10s) | 设太短会把同一波故障拆成好几条;设太长会延迟通知 |
group_interval |
同组有新告警时,多久发一次更新 | 5m | 5m | 设太小会持续刷屏;设太大会漏掉后续新增的实例 |
repeat_interval |
同一条告警未恢复时,多久重复提醒一次 | 4h | warning 4h,critical 1h | 必须 ≥ group_interval,否则重复提醒会被组间隔吃掉 |
⚠️ 注意:
repeat_interval小于group_interval不会报错,但会失效。 把repeat_interval设成 30s、group_interval保持 5m,实际重发间隔仍然是 5m。check-config不会拦这种配置,它只在语义上说不通。group_wait和group_interval的差别:前者管「第一次发之前等多久」,后者管「发完之后下次什么时候发」。
3、group_by 该写什么
group_by 决定了「哪些告警会被合并成一条通知」,是收敛效果的关键:
| 写法 | 效果 | 适用场景 | 副作用 |
|---|---|---|---|
['alertname'] |
同类型告警全部合并 | 单主机、单应用的小环境 | 多台机器一起出问题时信息被压缩掉,看不到具体是哪台 |
['job'] |
同一系统合并 | 按系统划分职责的团队 | 一个 job 下多个 svc 混在一起 |
['job', 'svc'] |
按系统 + 服务合并 | 多应用共用一套监控 | 本系列推荐,与第2篇标签约定一致 |
['job', 'svc', 'instance'] |
不合并 | 需要精确到实例的场景 | 等于放弃分组,只剩路由和抑制在收敛 |
group_by: ['...']这种特殊写法表示不分组,每条告警单独通知。除非告警量极小,否则不建议。group_by锚定在job+svc上,是这套体系里性价比最高的选择。根因是故障的边界通常就是系统的边界,而不是机器的边界。按 instance 分组等于按机器切分故障现场,看到的永远是碎片。
4、验证路由
1)配置改完先过语法
bash
$ docker exec -it alertmanager amtool check-config /etc/alertmanager/alertmanager.yml
Checking '/etc/alertmanager/alertmanager.yml' SUCCESS
Found:
- global config
- route
- 3 inhibit rules
- 6 receivers
- 1 templates
SUCCESS
2)再用 amtool 把整棵树打出来核对:
bash
# 查看最终生效的路由树(含从父路由继承的参数)
$ docker exec -it alertmanager amtool config routes show --config.file /etc/alertmanager/alertmanager.yml
3)测试一条告警会落到哪个接收器
bash
# 基本语法
amtool config routes test --config.file=/path/to/alertmanager.yml <label1>=<value1> <label2>=<value2> ...
# 测试一条告警会落到哪个接收器
# 示例
$ docker exec -it alertmanager \
amtool config routes test \
--config.file=/etc/alertmanager/alertmanager.yml \
severity=critical \
job=cim \
svc=nodb
提示:amtool config routes test 是这个环节最省事的工具。 不用真的触发告警,直接传一组标签就能看到会命中哪个 receiver。路由设计的正确性,应该在被验证之后才上线,而不是等故障时才发现走错了通道。
4)验证是否命中预期接收器
使用 --verify.receivers 标志,可以断言告警是否只命中指定的接收器。如果实际结果与预期不符,命令会返回非零退出码(错误码 1),非常适合在 CI/CD 或脚本中做自动化校验。
bash
$ docker exec -it alertmanager \
amtool config routes test \
--config.file=/etc/alertmanager/alertmanager.yml \
--verify.receivers=email-critical-cim \
severity=critical \
job=cim \
svc=nodb
slack-all
WARNING: Expected receivers did not match resolved receivers.
5、分析要点
- 树深度控制在 3 层以内:
severity→job/svc→type,再深就会出现「谁也说不清这条会走到哪」 - 根路由必须配一个兜底
receiver,否则未匹配的告警会被静默丢弃 - 子路由只覆盖需要改的参数,其余继承父路由。继承不是复制,父路由改了子路由会跟着变
continue: true会让匹配后继续尝试兄弟路由,默认 false。不用多通道冗余就别开,开了容易出现重复通知- 每层都设
repeat_interval是常见错误,只在需要区分级别时设
判断 :路由树的复杂度应该和团队规模匹配。三人团队用「critical 走企业微信、warning 走邮件」两层就够了;路由层数超过告警分级数,通常说明在设计组织架构,不是在设计告警。
三、抑制与静默:把告警量降下来
1、inhibit_rules 三要素
抑制规则只有三个部分,但每一部分都有讲究:
yaml
inhibit_rules:
# 当 source_match 匹配的告警存在时,压制 target_match 匹配的告警
- source_matchers:
- severity = "critical"
target_matchers:
- severity = "warning"
# 只有这些标签完全相等时才抑制
equal: ['job', 'instance']
- source:作为「根因」的告警,它存在时才有抑制
- target:被压制的告警
- equal:必须完全一致的标签集合,是抑制能否命中的关键
2、跨层抑制:告警治理里最值钱的一条
真正制造告警风暴的是跨层连锁:宿主机一挂,上面的应用、依赖它的服务、拨测到它域名的探针,会在几分钟内全线告警。
示例:
yaml
inhibit_rules:
# 规则 1:主机层 critical 压住同 job+svc 的应用层 warning
# 主机都趴了,应用告警是必然结果
- source_matchers:
- severity = "critical"
- type = "node"
target_matchers:
- severity = "warning"
equal: ['job', 'svc']
# 规则 2:拨测失败时,压住该服务自身的接口告警
- source_matchers:
- alertname = "ProbeTargetDown"
target_matchers:
- severity = "warning"
equal: ['job', 'svc']
# 规则 3:critical 压住同 job+instance 的 warning
- source_matchers:
- severity = "critical"
target_matchers:
- severity = "warning"
equal: ['job', 'instance']
⚠️ 注意:equal 里的标签少打一个,抑制规则就永远不匹配,而且是静默失败。
验证抑制是否生效,最快的方式是直接查 Alertmanager 的 API:
bash
# 查看当前活跃告警,以及每条被哪些规则抑制了
$ curl -s http://127.0.0.1:9093/api/v2/alerts | python3 -m json.tool | grep -E '"status"|"inhibitedBy"'
3、静默:计划内维护的正确做法
抑制解决的是「已知的因果连锁」,静默解决的是「已知的、有时间边界的人为变更」。
bash
# 创建 2 小时的静默(升级发版场景)
$ docker exec -it alertmanager amtool silence add \
alertname="HostHighCpuLoad" \
--duration=2h \
--comment="主机升级,预计 30 分钟"
# 根据 Job 静默
$ docker exec -it alertmanager amtool silence add \
job="<你的job名称>" \
--duration=2h \
--comment="<你的job名称> 应用发版,预计 30 分钟"
# 查看当前所有静默
$ docker exec -it alertmanager amtool silence query --alertmanager.url http://localhost:9093/
静默的两个强制要求
| 参数 | 作用 |
|---|---|
--duration |
必须带。让静默自动过期,避免忘记清理造成永久漏报 |
--comment |
必须带。让三个月后复盘时有人知道当时为什么静默 |
⚠️ 重点:静默比抑制危险得多。 抑制规则由标签驱动,根因消失就自动失效;静默是人工创建的时间窗口,窗口期内该告警彻底不响,包括真实的故障。所以静默的粒度必须窄到具体 alertname 或 svc,不能用宽泛的标签一次静掉一批。
4、三种手段的取舍
| 场景 | 用哪个 | 为什么 |
|---|---|---|
| 同一 job 下 10 个实例同时抖动 | 分组 | 标签相同,自动合并,无需人工干预 |
| 主机宕机引发其上应用告警 | 抑制 | 因果明确、可固化,长期有效 |
| 升级发版、迁库、压测 | 静默 | 有明确起止时间,且是人工计划 |
| 上游依赖故障引发下游连锁 | 抑制 | 本质是跨层因果关系 |
| 某条规则阈值定得不对、频繁误报 | 都不是 | 该去改规则或降级,不该用收敛手段掩盖 |
如果一条告警需要长期靠静默压着,那它本身就该被删掉或降级。用静默兜住错误配置,只是把问题隐藏了。
5、常见踩坑
- 抑制不阻止告警产生,只影响通知。 被抑制的告警在 Prometheus 和 Alertmanager 里都正常存在,
/api/v2/alerts能查到,inhibitedBy字段会标出压制它的规则。排查「为什么没收到通知」时,先查这一项。 - 源告警恢复后,被抑制的告警会立刻恢复通知。 这通常符合预期,但如果触发条件还在(比如主机还没起但 CPU 告警已经 resolved),会出现一次抖动通知。靠
for参数和resolve_timeout一起兜。 - 抑制规则之间也会互相影响。 规则 1 和规则 3 的 target 范围有重叠,实际会按「任一 source 命中即抑制」生效。多重抑制叠加时,告警可能比预期更安静,要用
amtool实测确认。
四、通知模板:让告警在 3 秒内读完
1、挂载与引用
yaml
templates:
- '/etc/alertmanager/templates/*.tmpl'
2、模板变量
| 变量 | 含义 | 典型用法 |
|---|---|---|
.Status |
告警状态,firing / resolved |
标题里标 `[{``{ .Status |
.Alerts |
本组全部告警 | range 遍历 |
.Alerts.Firing |
仅未恢复的告警 | len 判空,避免空白通知 |
.Alerts.Resolved |
仅已恢复的告警 | 恢复通知单独排版 |
.GroupLabels |
分组维度的标签值 | 标题里显示是哪个 job、哪个 svc |
.CommonLabels |
组内所有告警共有的标签 | 提取 severity、env |
.CommonAnnotations |
共有的注解 | 提取 summary |
.Labels / .Annotations |
单条告警的标签与注解 | 正文逐条展开 |
.ExternalURL |
Alertmanager 访问地址 | 附上跳转链接,方便点进去看 |
3、邮件模板
第1篇已给过一个示例,下面再提供一份邮件模板供参考。
email.html
html
<html>
<head>
<title>{{ .CommonLabels.alertname }}</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.alert-box { border: 2px solid; padding: 15px; margin: 10px 0; border-radius: 5px; }
.firing { border-color: #ff4d4d; background-color: #fff5f5; }
.resolved { border-color: #52c41a; background-color: #f6ffed; }
table { width: 100%; border-collapse: collapse; margin: 10px 0; }
th, td { padding: 8px 12px; text-align: left; border-bottom: 1px solid #ddd; }
th { background-color: #f5f5f5; font-weight: bold; }
</style>
</head>
<body>
{{ if eq .Status "firing" }}
<h2 style="color: #ff4d4d;">🔥 监控报警(故障告警通知)</h2>
<table>
<tr><th>告警级别</th><td>{{ .CommonLabels.severity }} 级</td></tr>
<tr><th>告警名称</th><td>{{ .CommonLabels.alertname }}</td></tr>
<tr><th>告警应用</th><td>{{ .CommonLabels.job }}</td></tr>
<tr><th>告警服务</th><td>{{ .CommonLabels.svc | default "unknown" }}</td></tr>
<tr><th>故障节点</th><td>{{ .CommonLabels.instance }}</td></tr>
<tr><th>告警摘要</th><td>{{ .CommonAnnotations.summary }}</td></tr>
<tr><th>触发时间(北京时间)</th><td>{{ (.StartsAt.Add 28800e9).Format "2006-01-02 15:04:05" }}</td></tr>
</table>
<div class="alert-box firing">
<p>{{ replace .CommonAnnotations.description "\n" "<br>" | safeHTML }}</p>
</div>
{{ else }}
<h2 style="color: #52c41a;">✅ 告警恢复通知</h2>
<table>
<tr><th>告警名称</th><td>{{ .CommonLabels.alertname }}</td></tr>
<tr><th>恢复节点</th><td>{{ .CommonLabels.instance }}</td></tr>
</table>
{{ end }}
</body>
</html>
{{ end }}
.StartsAt.Add 28800e9用于将 UTC 时间转换为北京时间 (UTC+8)
Alertmanager 配置引用:
yaml
templates:
- "/etc/alertmanager/templates/*"
receivers:
- name: 'gmail-alerts'
email_configs:
- to: 'your-email@gmail.com'
html: '{{ template "email.html" . }}'
4、企业微信与 Slack
Alertmanager 原生支持 wechat_configs(企业微信应用消息)和 slack_configs。Slack 配置示例:
receivers:
- name: 'slack-all'
slack_configs:
- channel: '#ph'
send_resolved: true
username: 'Alertmanager'
title: '{{ template "slack.title" . }}'
color: '{{ template "slack.color" . }}'
icon_emoji: '{{ template "slack.icon" . }}'
text: '{{ template "slack.text" . }}'
slack 模板
html
{{/* ---------- 标题 ---------- */}}
{{ define "slack.title" }}
{{- if eq .Status "firing" -}}
{{- if eq .CommonLabels.severity "critical" -}}🔥 严重告警
{{- else if eq .CommonLabels.severity "warning" -}}⚠️ 警告通知
{{- else -}}ℹ️ 信息通知{{- end -}}
{{- else -}}
{{- if eq .CommonLabels.severity "critical" -}}✅ 严重告警已恢复
{{- else if eq .CommonLabels.severity "warning" -}}✅ 警告已恢复
{{- else -}}✅ 信息已恢复{{- end -}}
{{- end }} - {{ .CommonLabels.alertname }}
{{- end }}
{{/* ---------- 颜色 ---------- */}}
{{ define "slack.color" -}}
{{- if eq .Status "firing" -}}
{{- if eq .CommonLabels.severity "critical" -}}danger
{{- else if eq .CommonLabels.severity "warning" -}}warning
{{- else -}}#439FE0{{- end -}}
{{- else -}}good{{- end }}
{{- end }}
{{/* ---------- 图标 ---------- */}}
{{ define "slack.icon" -}}
{{- if eq .Status "firing" -}}
{{- if eq .CommonLabels.severity "critical" -}}:fire:
{{- else if eq .CommonLabels.severity "warning" -}}:warning:
{{- else -}}:information_source:{{- end -}}
{{- else -}}:white_check_mark:{{- end }}
{{- end }}
{{/* ---------- 正文 ---------- */}}
{{ define "slack.text" -}}
{{ range .Alerts }}
*告警名:* {{ .Labels.alertname }}
*系统/应用:* {{ .Labels.job_alias }}
*服务:* {{ .Labels.svc }}
*实例:* {{ .Labels.instance }}
*级别:* {{ .Labels.severity }}
*摘要:* {{ .Annotations.summary }}
*描述:* {{ .Annotations.description }}
{{ if eq .Status "firing" }}
*告警时间:* {{ .StartsAt.Format "2006-01-02 15:04:05" }}
{{ else }}
*恢复时间:* {{ .EndsAt.Format "2006-01-02 15:04:05" }}
{{ end }}
{{ end }}
{{ end }}
企业微信、Slack 区分方式:
| 通道 | 对应配置 | 需要的凭据 |
|---|---|---|
| 企业微信应用消息 | wechat_configs |
corpid、corpsecret、agentid |
| 企业微信群机器人 | webhook_configs + 中继服务 |
webhook key(在中继服务里) |
| Slack | slack_configs |
Incoming Webhook URL |
5、常见踩坑
⚠️ 模板渲染失败会导致整封通知不发,而 Alertmanager 却不一定会在日志里报错。 最常见的原因是引用了不存在的标签,比如模板里写了 {``{ .Labels.svc }},但某条告警没打 svc 标签,模板直接渲染失败,通知静默丢失。
因此,给标签加默认值是防这类问题的标准做法。
{{ .Labels.svc | default "unknown" }}
模板改动后一定要用 amtool check-config 复验。校验能拦掉大部分语法错误,但拦不住标签缺失导致的运行时失败,标签缺失只能靠真实告警触发验证。
注意 ,模板的唯一目标不是好看,是让值班的人在不打开 Grafana 的情况下判断要不要立刻动手。做不到这一点的模板,无论排版多精细都是无效信息。
五、告警治理:从「配完规则」到「可持续」
1、分级:只分三层,不要更多
| 级别 | 含义 | 通知通道 | 响应时限 | 典型规则 |
|---|---|---|---|---|
| critical | 用户已受影响,必须立刻处理 | 企业微信 / 电话 | 15 分钟内响应 | 5xx 错误率 > 1%、probe_success == 0、OOM Kill |
| warning | 趋势不对,当天处理即可 | 邮件 / 群消息 | 工作时间内处理 | CPU > 70%、磁盘 > 80%、堆内存 > 80% |
| info | 仅登记,用于复盘与容量规划 | 日报 / 不通知 | 无需响应 | QPS 环比波动、证书剩余天数 < 30 天 |
severity 在 Alertmanager 里只是一个标签,分级靠的是路由匹配。 即分级是否真的生效,取决于路由树里有没有对应的 matchers 。打了 severity: critical 却没有任何路由匹配它,这条告警会落到根路由的兜底接收器,和 info 级别一起进邮件。
2、一条告警的完整生命周期
把前面几节串起来,一条告警从产生到关闭会经过这些环节:
规则触发
↓(Prometheus 根据 for 字段判定,生成告警存入内部告警队列)
推送到 Alertmanager
↓(由 Prometheus alerting.alertmanagers 配置决定推送地址)
去重与分组
↓(按 group_by 标签合并告警;group_wait 等待攒齐同组告警)
抑制判定
├─ 命中抑制规则 → 标记 inhibitedBy,不发送通知
└─ 未命中 → 进入静默判定
静默判定
├─ 匹配有效静默规则 → 不发送通知
└─ 未匹配 → 通知发送
通知发送
↓(匹配路由找到 receiver,渲染通知模板发送)
告警状态判断
├─ 告警持续未恢复 → 按 repeat_interval 周期性重发告警通知
└─ 超过 resolve_timeout 不再收到告警 → 标记 resolved,发送恢复通知
3、治理动作:三个可执行的检查
以下是从本套体系的实际运行经验里总结的建议基线,不是实测数据,供校准自己的阈值:
- 告警量与处理量对比 :一周内触发的告警条数,与真正需要人工动作的条数之比。如果一条规则 30 天内 90% 以上的触发都不需要任何动作,它就该被降级或删除,而不是继续留在 critical 里。
- 重复率检查 :定期拉
amtool alert query的输出,看被抑制的告警占比。抑制比例长期低于 10%,说明抑制规则可能根本没匹配上。 - 静默残留检查 :按季度执行
amtool silence query。存在超过 7 天未过期的静默,几乎一定是忘记清理,它掩盖的告警需要单独复盘。
另外,告警规则要纳入版本管理。 规则文件和应用代码放在同一个仓库,改阈值走 Code Review,比在服务器上 vi 一份 YAML 可靠得多。
4、和 SLO 的关系
第7篇的后续方向里提过用黄金信号划 SLO。告警和 SLO 的分工:
| 对比维度 | 阈值告警 | SLO / Error Budget |
|---|---|---|
| 判断依据 | 资源水位或指标绝对值 | 用户可感知的错误预算消耗速度 |
| 触发条件 | 超过预设数字 | 错误预算消耗速率超过承受能力 |
| 优点 | 配置简单、延迟低 | 直接对齐业务影响,天然抑制噪声 |
| 缺点 | 阈值本身是拍出来的,容易误报 | 需要先有明确的 SLI 定义和基线数据 |
两者不是替代关系,而是分工关系 。资源类阈值告警负责「提前发现」,SLO 负责「判断严重程度」。同一条告警,配在 CPU 上是提醒,配在错误预算上是决策依据。
5、常见踩坑
- 告警恢复不代表故障结束。
resolve_timeout: 5m意味着指标恢复正常 5 分钟后才发恢复通知。如果故障是高频抖动的,会反复触发/恢复,产生大量通知。此时该去调规则的for,而不是调resolve_timeout。 - Alertmanager 自身也要被监控。 Alertmanager 是告警链路的单点,挂了之后所有告警静默失效。
⚠️ 不要让 Alertmanager 成为唯一通知路径。 生产环境里至少保留一条独立通道(比如另一个告警系统或平台侧监控),用来发现「Alertmanager 本身失效」这件事。
六、小结
至此,我们完整走过了告警体系从部署、路由、收敛到治理的全部流程。
配置上不必求全。Alertmanager 的核心能力就是四件事:
- 路由决定去哪里
- 分组决定合并多少
- 抑制决定压住什么
- 静默决定什么时候不响
日常真正需要长期维护的,通常是一棵三层以内的路由树、三条左右的抑制规则,和一套能看清四项信息的通知模板。静默应当是偶发动作,而不是常驻配置。
落地建议,先验通道再设计路由,先固化标签再做抑制。svc 和 type 这两个标签看着是第2篇里的命名规范,同时也是分组维度、抑制主键和跨层下钻的索引。标签规范是这套告警体系的隐藏地基,改了它,上面所有规则都会悄悄失效。
告警系统的目标不是不漏报,而是每一条通知都值得被处理。