ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

PrometheusAlert告警转发:Alertmanager多渠道通知与模板实践

2026/10/1 9:23:16 拓冰建站 浏览量
PrometheusAlert告警转发:Alertmanager多渠道通知与模板实践 告警渠道从 1 个变成 4 个的时候alertmanager.yml就开始失控了。上个月帮一个团队收拾他们的监控链路Alertmanager 里堆了七个 receiver、五套 template每加一个机器人就要复制一大段 YAML改一个文案要动五处最后没人敢碰那个文件。这种局面下PrometheusAlert 这类告警转发中间件的价值就出来了——它把告警从哪来和告警往哪去彻底解耦Alertmanager 只保留一个 webhook 出口钉钉、企业微信、飞书、短信、邮件这些落地渠道全部交给它管。这篇东西不是官方 README 的翻译而是我自己在两三套环境里装过、配过、也踩过坑之后的完整记录。安装部分会讲二进制和容器两种方式的取舍配置部分只挑真正要改的参数说重点放在对接 Alertmanager 的细节和模板编写上——因为新手卡住的地方基本都在这里。如果你现在只是把 PrometheusAlert 跑起来了但告警发不出去、或者发出去是一坨乱码那中间几节能直接对上你的症状。需要说明的是本文提到的默认路径、默认账号、参数名以常见版本为准PrometheusAlert 迭代比较快你手上的包如果对不上以解压目录里那份示例配置和自带模板为准思路是一样的。1. 先搞清楚 PrometheusAlert 在告警链路里扮演什么角色很多人装它的时候是懵的我明明已经有 Alertmanager 了为什么还要再加一个组件这个疑问不解开后面配置全靠猜。1.1 一个真实的触发点receiver 把配置文件撑爆了Alertmanager 原生支持的接收端包括 webhook、邮件以及部分厂商的专用集成。问题是它的设计假设是一条路由对应一个接收端组合当你需要同一条告警同时进钉钉群、企业微信群、飞书群还要给值班手机发短信时你要么写三个 receiver 然后在 route 里做continue: true串联要么接受只发一个渠道。更难受的是模板Alertmanager 的模板是全局的 Go template一个告警要发三个渠道就只能共用一套文案而钉钉和企业微信对 Markdown 的支持程度完全不一样硬套的结果就是某个渠道里显示出一堆星号和竖线。PrometheusAlert 出现的位置很巧它在 Alertmanager 的下游、在渠道机器人的上游。Alertmanager 只需要把告警 POST 给它一个地址剩下发给谁、长什么样全部由它处理。1.2 它内部到底做了什么接收、识别、套模板、转发一次告警穿过 PrometheusAlert 的过程大概是这样监听 HTTP 端口默认 8080收到一个 POST 请求根据请求路径和 payload 结构判断来源——Alertmanager 的 JSON、Grafana 的 webhook、Zabbix 的媒介脚本、或者任意自定义脚本取出告警内容和指定模板里的变量做绑定用 Go template 渲染出最终文本根据 URL 参数或后台配置决定走哪个渠道按对应机器人接口的协议把内容 POST 出去把这次转发记到本地库一般是 db 目录下的 SQLite 文件里供后台页面查询和重发。这里面有一个很关键的认知PrometheusAlert 不做告警抑制、不做去重、不做分组。这些活儿还是 Alertmanager 干的它就是个格式化 分发的中间层。想明白这一点你就知道为什么告警风暴不能指望它来解决也理解为什么重启它不会导致告警状态错乱——它本身没有状态机只有一个转发日志。提示正因为不做抑制把repeat_interval配得太短会直接反映成钉钉群里被同一个告警刷屏而这个锅 Alertmanager 背不要去找 PrometheusAlert。1.3 什么场景值得上它什么场景纯属加负担判断标准其实很朴素渠道数量 ≥ 2或者需要按告警级别走不同文案或者需要留一份可查询的告警历史那它就值得装。反过来如果你只有一个钉钉群模板也只要一行字那 Alertmanager 自带的 webhook 接收端已经够用多一个组件多一份运维成本。对比项Alertmanager 直接对接渠道经 PrometheusAlert 转发配置复杂度每加一个渠道改一次 YAML只维护一套配置文件模板灵活度全局模板难按渠道区分一个渠道一套模板告警历史无只能翻日志本地库可查询、可重发多渠道同发需continue串联易漏路由配置解决额外运维成本无多一个常驻进程故障影响面单渠道失败不影响其他中间件挂了全部渠道哑火最后一行是重点PrometheusAlert 变成了告警链路上的单点。所以它必须做进程守护和存活监控这一点在后面安装那一节会专门讲。2. 安装方式的取舍容器省事二进制更可控PrometheusAlert 的交付形式很轻一个二进制文件加几个目录就能跑所以不存在编译半小时那种事。真正要选的是用容器还是直接跑二进制。2.1 Docker 部署三个目录必须挂出来镜像很常见拉下来直接跑也行但我强烈建议把conf、db、logs三个目录挂到宿主机上否则删容器等于删配置和告警历史。docker run -d \ --name prometheusalert \ --restartalways \ -p 8080:8080 \ -v /data/prometheusalert/conf:/app/conf \ -v /data/prometheusalert/db:/app/db \ -v /data/prometheusalert/logs:/app/logs \ feiyu563/prometheus-alert:latest用 docker-compose 写会更清爽version: 3 services: prometheusalert: image: feiyu563/prometheus-alert:latest container_name: prometheusalert restart: always ports: - 8080:8080 volumes: - ./conf:/app/conf - ./db:/app/db - ./logs:/app/logs这里有两个新手常踩的点。第一挂载目录如果宿主机上不存在Docker 会自动创建但创建出来的是root:root权限的目录容器里以非 root 用户运行时就写不进去表现是配置改不动、告警记录不落库。做法是先mkdir -p再chown到容器运行用户或者先让容器自己跑一遍生成初始文件、停掉、再把目录权限调整好。第二镜像里的默认配置是给你做模板用的第一次启动后一定要进 conf 目录看一眼实际生成的文件名别照着网上抄的路径改路径错了进程不会报错只是配置不生效。注意容器里的本地时间默认是 UTC告警记录页面上的时间会比你晚 8 小时。挂载/etc/localtime:/etc/localtime:ro或者设置TZAsia/Shanghai环境变量都能解决这个坑不踩一次很难想到。2.2 二进制加 systemd我更推荐的生产做法直接跑二进制的好处是调试直观——日志就在眼前配置文件路径一目了然不用层层进容器。步骤是下载对应平台的压缩包、解压、把目录放到/opt或/usr/local下然后写一个 systemd 单元文件。mkdir -p /opt/prometheusalert cd /opt/prometheusalert # 把解压出来的二进制、conf、db、logs、views 等目录放到这里 chmod x PrometheusAlertsystemd 单元文件长这样[Unit] DescriptionPrometheusAlert Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple WorkingDirectory/opt/prometheusalert ExecStart/opt/prometheusalert/PrometheusAlert Restartalways RestartSec5 LimitNOFILE65536 [Install] WantedBymulti-user.targetWorkingDirectory这行不能省。PrometheusAlert 默认按相对路径找conf、db、views这些目录如果不设工作目录systemd 会以/作为起点去找结果就是启动成功但读不到配置、页面 404。这是我自己踩过的最典型的一个坑systemctl status显示 active (running)但访问 8080 是白页。Restartalways也建议加上。前面说过它是告警链路的单点进程意外退出而没人发现等于所有告警静默这比任何配置错误都危险。有条件的话再给它的/或健康检查路径挂一个探测进程假死也能被发现。2.3 首次登录与端口收口起来之后浏览器打开http://内网IP:8080默认账号一般是admin默认密码与程序名相关常见版本里是prometheusalert具体看你那份文档。第一次登录立刻改密码这个东西默认是明文比对密码弱就等于把告警内容和机器人地址全暴露了。端口策略上8080 不要往公网放。Alertmanager 通常在同一个内网里让它直接访问内网 IP 就行。确实需要跨网络访问的用反向代理挡在前面并注意两件事一是反代时不要把路径前缀改得面目全非否则你写在 Alertmanager 里的 webhook 地址和实际路径对不上二是把proxy_read_timeout、client_max_body_size调大一点告警 payload 在某些场景下会比较大默认值偏小容易 413。3. conf/app.conf 里真正要改的参数就那么几个打开配置文件你可能会看到几十行但绝大多数保持默认就行。全改一遍的结果通常是引入一堆自己都说不清的变量。3.1 必改参数清单下面这几个是我每套环境都会动的地方字段名不同版本可能有差异但含义是通用的参数作用建议值服务监听端口决定访问地址内网端口避免与常用服务冲突运行模式影响日志详细程度调试期用开发模式上线切生产钉钉机器人地址默认钉钉出口填 access_token 形式的完整地址企业微信机器人地址默认企微出口填 webhook key 形式的完整地址飞书机器人地址默认飞书出口填 hook 形式地址是否默认 所有人决定是否打扰全员默认关闭按级别在模板里控制告警记录开关是否落库打开历史告警的价值很高短信/电话/邮件地址其他渠道出口按需填不用的留空写配置文件的时候注意它通常是 ini 风格#开头是注释等号两边不要留全角空格值里带、?这种 URL 参数时直接写不要加引号加了引号会被当值的一部分传出去机器人就会返回参数错误。改完必须重启进程PrometheusAlert 不会热加载配置。3.2 各渠道机器人地址怎么拿钉钉群机器人加了之后会给一个带access_token的地址另外还要选一种安全设置自定义关键词、加签、或者 IP 白名单。这里有个高频翻车点——如果机器人开了加签而你的版本对加签支持不完整消息会一直被拒。最省事的组合是自定义关键词关键词选一个一定会出现在你模板里的词比如告警系统名或者告警两个字然后确认模板里确实包含它。企业微信和飞书的机器人是直接给一条完整 webhook 地址把它们原样粘到配置里即可注意不要漏掉末尾的字符。邮件渠道要单独配 SMTP 服务器、端口、发件账号和授权码并且确认你的发件服务器允许该来源 IP 发信。提示机器人地址属于敏感信息它等于一把可以直接往群里发消息的钥匙。配置文件不要提交到公开代码库多人协作时用环境变量或者配置管理工具注入。3.3 用 URL 参数覆盖渠道地址一个实例服务多个团队这是我用得最多的一个技巧。PrometheusAlert 支持在 webhook 地址的查询参数里临时指定渠道地址和模板名也就是说配置文件里可以什么默认地址都不写全靠调用方传。curl -X POST \ http://192.168.1.10:8080/prometheusalert?typeddtplops-alertat0ddurlhttps://oapi.dingtalk.com/robot/send?access_tokenxxxxxx \ -H Content-Type: application/json \ -d { alerts: [{ status: firing, labels: {alertname: InstanceDown, severity: critical, instance: 10.0.0.21:9100}, annotations: {description: 节点探活失败超过 3 分钟} }] }这样做的收益很直接A 团队和 B 团队各自有自己的群机器人但共用一套 PrometheusAlert 实例只要在 Alertmanager 那边的 receiver 里写不同的完整地址就行。省掉了为每个团队改配置文件、重启进程的麻烦。at1是 所有人tpl是模板名写你后台里存的那个名字。用这种方式的时候配置文件里的默认值可以留空减少误发。4. 打通 Alertmanager地址写对了链路就通了一半这一步是整个部署里最容易出错的地方因为它横跨两个组件、两套网络。4.1 Alertmanager 端只改两处在alertmanager.yml里加一个 receiver然后在 route 里指向它route: receiver: prometheusalert group_by: [alertname, instance] group_wait: 30s group_interval: 5m repeat_interval: 4h receivers: - name: prometheusalert webhook_configs: - url: http://192.168.1.10:8080/prometheus/alert send_resolved: true http_config: follow_redirects: true这里四个时间参数值得花两分钟理解因为它们直接决定你被消息打扰的频率group_wait同组告警等多久一起打包发出设太小会一条一条来group_interval同一组告警有新增时多久后再发一次repeat_interval告警一直不恢复多久重复提醒一次send_resolved告警恢复时是否也推一条。PrometheusAlert 不做抑制所以这四个参数就是你的降噪总闸。生产上repeat_interval设 4 小时比较舒服核心告警可以缩短到 1 小时。4.2 webhook 地址怎么写容器网络下的经典错误三种部署形态写法完全不同PrometheusAlert 和 Alertmanager 都在宿主机上跑http://127.0.0.1:8080/prometheus/alert可以但更推荐写内网 IPAlertmanager 在宿主机、PrometheusAlert 在容器里并且做了端口映射写宿主机内网 IP 映射端口不要写localhost两个都在容器里、同一个自定义网络写容器服务名 容器内端口比如http://prometheusalert:8080/prometheus/alert。最常见的错误是 Alertmanager 跑在容器里webhook 写成http://127.0.0.1:8080/...——在容器内部127.0.0.1 指向的是它自己当然连不上。判断方法很简单进 Alertmanager 容器curl一下这个地址不通就说明地址错了跟 PrometheusAlert 本身没关系。4.3 恢复通知必须区分状态否则群里的消息很吵开了send_resolved之后恢复告警也会走同一条链路进来。如果你的模板不区分状态恢复消息会带着告警名称、严重级别原样出现值班的人看到会误以为又炸了。处理方式是在模板里判断状态字段。Alertmanager 的 payload 顶层有一个状态字段值是firing或resolved模板里可以直接引用它做分支{{ if eq .Status resolved }} 【已恢复】{{ .CommonLabels.alertname }} {{ else }} 【告警】{{ .CommonLabels.alertname }} {{ end }}这样一条模板同时覆盖两种状态群里一眼就能分辨。另外恢复通知建议只在关键级别开启低级别告警的恢复消息完全是噪音。5. 模板编写把原始 JSON 变成值班的人看得懂的东西模板是这个工具真正的价值所在。默认模板能跑但通常不适合你的业务改的时候关键是搞清楚变量从哪来。5.1 先看一遍真实 payload再动手写模板Alertmanager 发过来的结构大致是顶层有一组公共标签下面挂一个告警数组。也就是说同一个分组里的多条告警公共部分在顶层每条自己的细节在数组里。常见的引用方式表达式含义.CommonLabels.alertname告警规则名.CommonLabels.severity告警级别.CommonAnnotations.description描述信息.CommonAnnotations.summary摘要.Status整体状态触发或恢复.Alerts告警明细数组.ExternalURL回跳 Alertmanager 的地址要拿到数组里某一条的具体值需要遍历比如按行列出每个实例。第一次写模板最有效的办法是先弄一份真实的告警 JSON把它存成文件然后照着字段名写而不是凭记忆猜字段。猜错的后果不是报错而是渲染出一串空值或者no value界面上看起来发成功了实际上什么信息都没有。5.2 一份可以直接改的钉钉模板下面这份我一直在用结构清晰、信息密度合适改成别的渠道也只需要调整标题行的写法# {{ if eq .Status resolved }}【已恢复】{{ else }}【告警】{{ end }}{{ .CommonLabels.alertname }} **级别**{{ .CommonLabels.severity }} **集群**{{ .CommonLabels.cluster }} **实例**{{ .CommonLabels.instance }} **描述**{{ .CommonAnnotations.description }} **开始时间**{{ .CommonAnnotations.startsAt }} {{ if eq .Status firing }} 请相关同事关注处理完请回复群内消息 {{ end }} [查看告警详情]({{ .ExternalURL }})几个细节值得说一是标题行用一级标题钉钉渲染出来是加粗大字比正文里的星号更醒目二是把实例和描述放在最前面值班的人扫一眼就知道哪台机器出了什么事三是最后附上回跳链接需要看原始告警详情时不用再翻工具。5.3 按级别分支和不同渠道的渲染差异真实环境里不会所有告警都用一套文案。我的做法是按级别拆成三套模板严重、警告、提示。严重的加 所有人 并附处理入口警告只发群不打扰提示降频或者只记录不发。然后在 Alertmanager 那边按标签分流或者在调用时用tpl参数指定不同模板名。另一个必须注意的是各渠道对 Markdown 的支持差异。钉钉支持标题、加粗、链接但对表格支持很差写了表格会变成一行乱码企业微信的 Markdown 支持更窄很多语法直接当纯文本飞书相对宽松卡片格式也更漂亮。所以别指望一套模板打天下至少要针对常用的一两个渠道各写一份改动的内容其实只是标题行和列表写法。注意如果机器人的安全设置用了自定义关键词务必将关键词放在每套模板里都一定会出现的固定位置比如标题行前缀。模板一改关键词没了消息立刻被拒而且错误信息只会体现在接口返回里群聊里看不到任何提示。6. 排错与运维从 webhook 到群消息的六步验证告警发不出去的时候最忌讳的就是到处乱改。这条链路上有五个环节按顺序一个个排除比反复重启进程高效得多。6.1 六步排查法一步都不要跳确认 Alertmanager 真的发出去了。看它的日志有没有 webhook 相关记录或者直接用curl手动往 PrometheusAlert 打一条测试告警。手动打能通、Alertmanager 打不通问题就在网络或地址上。确认 PrometheusAlert 收到了。看它自己的日志有没有打印请求记录更直观的是打开后台的告警记录页面看有没有新记录。记录都没有说明请求根本没到。确认模板渲染成功。在告警记录里看渲染后的内容。如果是空的、或者一堆no value那是模板变量写错了属于第 5 节的问题。确认渠道地址可达。在 PrometheusAlert 所在的机器或容器里用curl直接 POST 一次机器人地址可以先发个最简单的{msgtype:text,text:{content:test}}排除出网、DNS、防火墙问题。确认机器人没有拦截。关键词对不对、加签有没有配、IP 白名单有没有漏掉 PrometheusAlert 的出口 IP。确认接口返回是成功。这一步很多人漏掉机器人接口返回 HTTP 200 不代表发送成功body 里还有一个业务错误码。PrometheusAlert 的日志里一般会带上这个返回内容看到非成功码就是被渠道拒了。6.2 常见现象对照表现象大概率原因处理方式告警记录页面没有新记录地址错误或网络不通从 Alertmanager 侧 curl 验证地址有记录但内容为空模板变量写错对照真实 payload 改字段名渠道返回参数错误配置值带了多余引号或全角字符去掉引号检查编码钉钉提示关键词不匹配模板里没有关键词在标题行加固定关键词部分渠道收到、部分没收到多渠道地址配置遗漏逐个渠道单独测试恢复通知也 所有人模板没区分状态用状态字段做分支判断时间显示晚 8 小时容器时区是 UTC挂载时区文件或设置时区变量服务正常但页面白屏工作目录不对静态资源找不到systemd 里补上工作目录排查时有个习惯特别有用先手动构造一条测试请求把链路走通再接真实告警。因为真实告警里字段多、嵌套深出问题的地方多手动请求只有几个字段一旦跑通就证明地址、认证、模板、渠道这条线是好的剩下的问题一定在数据格式上。这个顺序反过来做调试时间会翻好几倍。6.3 备份、升级与日常巡检这套东西的资产其实只有两块conf目录配置和模板和db目录告警记录。二进制随时可以重新下载。所以备份策略很简单定期打包这两个目录。SQLite 的备份有个坑——如果直接cp一个正在被写入的库文件可能拿到损坏的副本。稳妥的办法是用sqlite3的.backup命令或者备份前先停服务systemctl stop prometheusalert tar czf /backup/prometheusalert-$(date %F).tar.gz /opt/prometheusalert/conf /opt/prometheusalert/db systemctl start prometheusalert升级流程也要注意顺序先备份再替换二进制然后启动观察日志最后进后台点一遍各个模板确认还在。模板在有些版本里既存在文件里也存在库里升级后如果发现模板变回默认了就用备份里的文件覆盖回去。日常巡检我一般只盯三件事进程是否活着挂了等于全部渠道静默、磁盘是否够告警记录会持续增长低配机器上一年能写出几百兆、以及每周翻一次告警记录里的转发失败条目。第三件事最有价值很多渠道问题不是完全发不出去而是某类告警长期静默失败没人看记录就一直发现不了。我自己最深刻的体会是装了 PrometheusAlert 之后真正的关键已经转移到模板上了。刚上线那两周几乎所有问题都出在模板变量和渠道拦截上跟安装本身毫无关系。所以我现在的习惯是每接一个新渠道先用手动构造的最小 payload 跑通全链路再写正式模板在模板里只保留值班的人真正需要的那几行信息——告警名、级别、实例、一句人话的描述、一个回跳链接。剩下的细节都留在 Alertmanager 里需要的时候点链接去看。群里消息越短被认真读完的概率越高。