告警通知模板与最佳实践
系列的收尾篇。前面五篇把告警「发出去」这件事讲透了,这篇关注发出去的内容本身:通知长什么样由模板决定,写得好不好由规范决定。一篇讲清两件事——怎么用 Go template 自定义通知内容,以及一套可以直接照抄的告警内容规范和最佳实践清单。
文中示例基于 Alertmanager 0.27+。模板语法示例均为 Go text/template,只出现在代码块中,可直接照抄到配置文件和模板文件里。
通知模板是什么
receiver 配置里,凡是消息内容字段(邮件主题、Slack 文案、webhook 转出去的正文)都支持 Go template 渲染:Alertmanager 在发送前把告警数据填进模板,渲染出最终文本。默认模板能用但很丑,生产上几乎都会自定义。
模板能拿到什么数据
每次通知,Alertmanager 传入一个顶层数据对象,常用字段:
| 字段 | 含义 |
|---|---|
.Receiver | 当前接收器名称 |
.Status | 整组告警的状态:firing 或 resolved |
.Alerts | 本组包含的告警列表(模板里通常遍历它) |
.GroupLabels | 按哪些标签分组的(路由 group_by 的结果) |
.CommonLabels | 本组所有告警共有的标签 |
.CommonAnnotations | 本组所有告警共有的注解 |
.ExternalURL | Alertmanager 的外网地址(拼链接用) |
遍历 .Alerts 后,单条告警对象上的常用字段:
| 字段 | 含义 |
|---|---|
.Status | 该条告警状态 |
.Labels | 标签集合,如 alertname、severity、instance |
.Annotations | 注解集合,summary、description 都在这里 |
.StartsAt | 告警开始时间 |
.EndsAt | 结束时间(未恢复时是个无意义的零值时间) |
.GeneratorURL | 生成告警的规则链接,点回 Prometheus 看表达式 |
.Fingerprint | 告警指纹,唯一标识 |
模板渲染发生在分组之后:一条通知对应一组告警,所以正文里通常要遍历 .Alerts 把每条都列出来。分组的逻辑回顾路由与接收器一篇。
常用模板片段
遍历所有告警,是最常见的骨架:
{{ range .Alerts }}
- [{{ .Status }}] {{ .Annotations.summary }}
实例: {{ .Labels.instance }}
{{ end }}
取某个标签或注解的值,用 index 函数,比点号访问更通用(标签名里有特殊字符时只能靠它):
级别: {{ index .CommonLabels "severity" }}
团队: {{ index .CommonLabels "team" }}
按状态给通知加不同图标(firing 红色、resolved 绿色):
{{ if eq .Status "firing" }}🔥 告警触发{{ else }}✅ 告警恢复{{ end }}
判断标签是否存在,不存在时给默认值,避免渲染出难看的空串:
负责人: {{ if .Labels.owner }}{{ .Labels.owner }}{{ else }}待认领{{ end }}
时间格式化(Go 的时间格式参考点固定是 2006-01-02 15:04:05,别改成别的):
开始时间: {{ .StartsAt.Format "2006-01-02 15:04:05" }}
模板可以抽成独立文件,在 alertmanager.yml 顶层用 templates 引用:
templates:
- /etc/alertmanager/templates/*.tmpl
实战:自定义钉钉 markdown 通知
Alertmanager 内置的接收器类型里没有钉钉/企微,对接姿势是:Alertmanager 的 webhook 接收器 → 一个转换层 → 钉钉机器人。转换层负责把 Alertmanager 的 webhook 报文按模板渲染成钉钉 markdown 格式再推给机器人,开源实现用 timonwong/prometheus-webhook-dingtalk 即可。
Alertmanager 侧配置:
receivers:
- name: dingtalk-ops
webhook_configs:
- url: http://dingtalk-adapter:8060/dingtalk/ops/send
send_resolved: true
转换层(prometheus-webhook-dingtalk)的配置,指向一个模板文件:
targets:
ops:
url: https://oapi.dingtalk.com/robot/send?access_token=xxxx
message:
msgtype: markdown
markdown:
title: '{{ template "ding.title" . }}'
text: '{{ template "ding.text" . }}'
模板文件 ding.tmpl,标题带级别 emoji,正文列实例、摘要、开始时间:
{{ define "ding.title" }}
{{ if eq .Status "firing" }}🔥【{{ index .CommonLabels "severity" }}】{{ else }}✅【恢复】{{ end }}{{ index .CommonLabels "alertname" }}
{{ end }}
{{ define "ding.text" }}
{{ range .Alerts }}
**告警**: {{ .Annotations.summary }}
**实例**: {{ .Labels.instance }}
**级别**: {{ .Labels.severity }}
**开始时间**: {{ .StartsAt.Format "2006-01-02 15:04:05" }}
**详情**: {{ .Annotations.description }}
---
{{ end }}
[查看 Alertmanager]({{ .ExternalURL }})
{{ end }}
最终群里收到的效果:一条 markdown 消息,标题是「🔥【critical】NodeDown」,正文分条列出每条告警的实例、摘要、开始时间,结尾带跳回 Alertmanager 的链接。
企业微信群机器人同样支持 markdown,走同一个转换层(它也内置了企微的消息类型),把 msgtype 和目标 URL 换成企微机器人的即可,模板内容照搬。
改完模板不用等真实告警:用 amtool 或直接 curl 往 Alertmanager 的 /api/v2/alerts 推一条测试告警,立刻能看到渲染效果。模板语法错误会在 Alertmanager 日志里报出来,发送失败数会在 alertmanager_notifications_failed_total 上体现。
resolved 通知
每个接收器配置里都有个 send_resolved 开关:开了之后,告警恢复时会再发一条「已恢复」通知。
receivers:
- name: dingtalk-ops
webhook_configs:
- url: http://dingtalk-adapter:8060/dingtalk/ops/send
send_resolved: true
为什么恢复通知很重要:它决定值班的人能不能安心睡觉。没有 resolved 通知,值班看到 firing 告警后只能不停刷仪表盘确认「好了没有」;有了它,收到 ✅ 就可以放下手机。对夜间告警尤其关键——firing 把人叫醒,resolved 让人重新睡着。
哪些渠道建议开:
- IM 类(钉钉、企微、Slack):开,恢复消息在群里刷一条几乎无成本。
- 电话/短信类(PagerDuty、Opsgenie 的电话策略):谨慎开。恢复通知也打电话会很烦,这类渠道通常只让 firing 触发电话,resolved 走 IM。
- email:看团队习惯,一般开,反正邮件不打扰。
send_resolved 的默认值因集成类型而异(比如 email、webhook 默认 false,PagerDuty 默认 true)。不要凭印象,配置时显式写出来,代码评审时一眼能看清。
告警内容规范
模板再漂亮,源头内容不行也白搭。规范要落在告警规则的 annotations 和 labels 上——模板只是搬运工。
summary 一句话说清「什么坏了」
summary 是通知的标题,值班在锁屏上瞟一眼就要知道发生了什么。好的 summary:api-gateway 的 5xx 比例超过 5%、10.0.0.21 磁盘剩余不足 10%。坏的 summary:Check failed、Alert triggered——等于没说。
带标签值让 summary 自解释(这段写在告警规则里):
annotations:
summary: "{{ $labels.instance }} CPU 使用率超过 90%"
description 给「影响和排查入口」
description 是通知正文,回答两个问题:影响是什么(哪个业务、多少用户)和第一步去哪看(仪表盘链接、日志查询入口、runbook 地址):
annotations:
description: "订单接口 P99 延迟超过 2s,影响下单链路。排查入口:https://grafana.example.com/d/order-api"
通知里带 runbook 链接是拉开告警质量差距的关键一招——半夜三点没人想现场翻 wiki。
标签命名约定:三件套
所有告警规则统一带三个标签,路由和统计都靠它们:
| 标签 | 取值约定 | 用途 |
|---|---|---|
severity | critical / warning / info | 路由分级:critical 打电话,warning 发群,info 进周报 |
team | 团队名,如 infra / order | 路由到对应团队的接收器 |
service | 服务名,如 api-gateway | 统计和复盘时按服务维度聚合 |
取值集合要收口(比如 severity 只允许三个值),写成团队约定并放在规则评审 checklist 里。规则的完整写法见告警规则与 Alertmanager一篇。
最佳实践清单
内容侧
- 每条告警必须 actionable:收到通知的人有明确动作可做。看了只能说「哦,知道了」的告警,要么降级成报表,要么删掉。
- severity 分级和通知渠道绑定:critical 打电话/值班系统,warning 发群消息,info 只进仪表盘或周报。别所有级别都往一个群里灌。
- summary 一句话、description 带排查入口、标签三件套齐全——写进规则评审 checklist。
链路侧
- resolved 通知默认开启(电话渠道除外)。
- 告警分组、去重、静默的配置回顾本系列抑制、静默与免打扰一篇,内容规范要建立在链路不轰炸的前提下。
治理侧
- 定期清理没人看的告警:一个季度 review 一次,统计每条告警的触发次数和处置记录,零处置的要么改进要么删。告警只有被认真对待才有价值,狼来了的故事在告警群天天上演。
- 重大故障后复盘告警表现:该响的响了吗?有没有告警风暴?通知内容够不够定位?复盘结论落回规则和模板,形成闭环。
常见坑
模板字段渲染成空
最常见的模板事故:通知发出来了,但「实例: 」后面是空的。原因是该条告警没有 instance 这个标签——模板里访问不存在的标签不会报错,只会渲染成空串。应对:
- 关键字段在告警规则里就保证有值(标签三件套强制)。
- 模板里对可能缺失的字段用 if 判空给默认值(写法见前面「常用模板片段」一节)。
- 新模板上线前用测试告警把 firing 和 resolved 两种状态都渲染一遍。
repeat_interval 轰炸
通知隔三差五重复发同一个告警,值班群被刷屏。这不是模板问题,是路由里 repeat_interval 设得太短(或根本没设,用了默认的 4h,而有些团队连 4h 都嫌密)。重发间隔的语义和调优见路由与接收器一篇。原则是:critical 可以勤提醒(30m-1h),warning 半天到一天一次足矣,配合静默机制处理「已知问题别再喊」。
resolved 不发
「告警明明恢复了,群里没收到恢复通知」——九成是 send_resolved 没开。它在每个集成配置里单独设置,默认值因类型而异。检查顺序:先看配置里写没写,再看 Alertmanager 日志里该条通知的渲染发送记录。
通知里的链接点不开
模板里用 .ExternalURL 拼的链接(比如跳回 Alertmanager 的链接)是内网地址或者 localhost,手机上点开白屏。这是启动参数 --web.external-url 没配或配成了内网地址。把它配成 Alertmanager 对外的可访问地址(前面 HA 一篇的启动命令里已带),通知里的链接才站得住。涉及反向代理和路径前缀时还要配合 --web.route-prefix。
小结
本篇是系列的收尾,把「告警发什么」讲完了:通知内容由 Go template 渲染,核心数据是 .Alerts 列表和单条告警的 .Labels、.Annotations、.StartsAt;钉钉/企微走 webhook + 转换层,模板标题带状态图标、正文列实例摘要时间;send_resolved 让值班能安心;内容规范上,summary 说清什么坏了、description 给排查入口、severity/team/service 三件套收口;治理上定期清理无效告警、故障后复盘告警表现。
回顾全系列六篇:核心概念与架构建立心智模型——分组、去重、通知流水线;部署篇把服务跑起来;路由与接收器决定告警发给谁;抑制、静默与免打扰把噪音压下去;高可用与运维监控篇让 Alertmanager 自己不再是单点;本篇则把通知内容打磨到「半夜看一眼就知道该干什么」。六篇走完,从概念到生产的路已经铺完,剩下的就是把这套东西在自己团队里真正立起来——配速查和参考资料在附录,用的时候翻。