告警发不出去这件事,最让人难受的不是技术难,而是链路太长、每一段都长得像没问题。Prometheus 采集正常,Alertmanager 日志里 receiver 也执行了,钉钉群里就是没动静。我从第一次把 Alertmanager 的 webhook 直接怼到钉钉机器人上,到后来把 PrometheusAlert 接进链路中间,前后踩过的坑基本覆盖了“配置写错”“模板渲染炸了”“消息太长被截断”“机器人被限流”“时间显示成 UTC”这五类。这篇就把 PrometheusAlert 的安装、配置、模板、路由、对接和排错一次性讲透,适合已经跑着 Prometheus + Alertmanager 但告警体验还很粗糙的运维同学,也适合刚接手告警平台、需要快速搭一套能用的通知中心的人。看完你应该能做到:半小时内把服务跑起来,把钉钉、企业微信、飞书里至少一个渠道打通,并且知道出问题该从哪一段开始掐。
1. 告警链路上为什么值得插一层 PrometheusAlert
1.1 Alertmanager 直连机器人的那几个固有短板
Alertmanager 自带的 webhook 接收端其实非常“素”,它只负责把一组告警按group_by聚合好,然后 POST 到你在webhook_configs里写的地址。剩下的活——拼消息、选样式、@ 指定的人、按业务分群、留下告警记录——它一概不管。于是很多团队的第一版方案就是把钉钉机器人的 webhook 地址直接填进 Alertmanager,靠钉钉那一侧做一点点内容格式适配。
这个方案在只监控三五个服务的时候勉强能用,一旦服务数量上到几十个,问题立刻暴露。第一是模板能力太弱,Alertmanager 的templates虽然支持 Go template,但想按“告警级别”输出不同颜色、想在企业微信里 @ 值班人、想在消息末尾附上跳转链接,配置起来非常别扭。第二是渠道切换成本高,今天用钉钉、明天想加飞书、后天要给老板发短信,每一次都要动 Alertmanager 的配置文件并 reload。第三是没有记录,三天前那条数据库连接数告警到底发没发出去、发给了谁,翻聊天记录只能靠人肉搜。第四是路由能力缺失,生产库的告警想发给 DBA 群、测试环境的告警想直接丢进一个“噪音群”,在 Alertmanager 里靠route树硬写也能做,但每加一个业务就要重画一遍匹配规则。
PrometheusAlert 正是冲着这四个点来的。它本质是一个告警转发与通知中心:上游接 Alertmanager、Grafana、Zabbix,甚至你自己写的脚本;中间做模板渲染、路由匹配、记录落库;下游把渲染好的消息投递到钉钉、企业微信、飞书、邮件、短信、语音、Bark、自定义 Webhook 等一堆通道。链路变成“Alertmanager → PrometheusAlert → 各种机器人”,Alertmanager 只管聚合,PrometheusAlert 管表达和分发,职责一下就清楚了。
1.2 它在链路里的准确位置和能力边界
我先把这个组件的能力说清楚,免得你抱着错误的预期去装。它是一次“转发 + 渲染 + 记录”,不是“聚合器”,也不是“告警规则引擎”。聚合并发这件事仍然由 Alertmanager 的group_by、group_wait、group_interval负责;PrometheusAlert 拿到的已经是聚合过后的一批告警了。它不做告警抑制,也不做静默的判定,那些还是 Alertmanager 的活。它做的,是把一批告警渲染成一条(或几条)人类一眼能读懂的消息,然后按你定义的规则送去正确的群。
它提供的能力大致有这几块:一是多通道通知,主流的几个 IM 机器人都覆盖;二是模板自定义,可以给钉钉一套模板、给飞书另一套模板,同一批告警渲染出不同风格;三是路由,按告警里的某个标签(常见是app或者job)把消息分派到不同的机器人地址;四是告警记录,默认落在 SQLite 或者 MySQL 里,Web 界面能翻历史;五是对外提供 HTTP 接口,也就是/prometheusalert这类路径,你可以用 curl 直接怼一条消息进去,这让它天生适合被自研的发布系统、巡检脚本、批处理任务调用。
注意:PrometheusAlert 的接收接口默认不做身份校验。你可以把它理解成一个“谁拿到地址谁就能往里发消息”的服务。所以它一定不要直接暴露在公网,放在内网、加一层反向代理做 IP 白名单,是最省事的做法。
1.3 什么规模的团队适合上,什么情况可以先不上
监控对象少于十个、告警每天不到二十条、只用一个 IM 渠道,说实话没必要引入额外组件,Alertmanager 直连机器人加一个稍微讲究点的模板就能过日子。多一个组件就多一个故障点,这是实话。
但只要出现下面任意一种情况,我就建议上:需要同时投递两个以上渠道;不同业务线的告警必须进不同的群;需要在消息里 @ 到具体的人;需要留一份可检索的告警历史用于事后复盘或者算 SLA;自研系统需要程序化地推消息。这几条满足一条,引入 PrometheusAlert 带来的收益就明显大于它带来的维护成本了。
还有一个容易被忽略的点:它能顺手把“人写的脚本要发通知”这件事统一掉。以前每个脚本各自维护一份钉钉 token、各自拼 JSON,token 一换要满地改。接到 PrometheusAlert 之后,脚本只需要 POST 一个 JSON 到固定地址,渠道怎么变都不用动脚本。
2. 部署前的选型:装在哪、用什么存、怎么保证安全
2.1 三种部署方式的实际取舍
PrometheusAlert 是 Go 写的,编译成了单个静态二进制,部署方式上有三条路:裸机跑二进制、容器跑 Docker、编排跑 Kubernetes。我三种都用过,体感差别很明显。
二进制方式的好处是排障最直接。日志文件在哪、配置改了有没有生效、端口有没有起来,一条ss -lntp就看清了。适合物理机或者长期不变的虚拟机环境,也适合你第一次摸这个组件、想先搞清楚它到底怎么跑的阶段。缺点是升级要手动停服、换文件、重启,系统级的环境变量还得自己管。
Docker 是我目前最推荐的默认选项。镜像里依赖都打包好了,配置文件通过挂载进来,环境变量通过-e传,升级就是改一下 tag 再docker compose up -d。唯一要小心的是数据持久化,SQLite 文件和日志目录都要挂到宿主机上,否则容器一重建历史记录就没了。
Kubernetes 适合本来就跑在集群里的团队,好处是配置走 ConfigMap、密码走 Secret、存储走 PVC,和其他服务的治理方式一致。但要注意 PrometheusAlert 本身不太适合盲目扩多副本,下面讲存储的时候会详细说为什么。
| 部署方式 | 上手难度 | 升级成本 | 适合场景 | 主要坑点 |
|---|---|---|---|---|
| 二进制 | 低 | 中 | 物理机、虚拟机、初次验证 | 环境变量与 systemd 管理要自己写 |
| Docker | 低 | 低 | 绝大多数中小规模场景 | 数据目录必须挂载出来 |
| Kubernetes | 中 | 低 | 已有集群、追求统一治理 | 多副本与 SQLite 冲突 |
2.2 数据落盘的三条路线:内存、SQLite、MySQL
PrometheusAlert 的记录存储有三条路线,选错了后面会很难受。
不落盘、纯内存,重启就清空。适合“我只是想转发消息,不需要历史”的场景,性能最好,也最省心。
SQLite 是默认选项,数据落在本地一个 db 文件里,零依赖。单机部署用它完全够用,我自己的测试环境跑了几个月,几十万条记录也没什么问题。但要注意两点:一是文件必须持久化,容器里挂载出来;二是 SQLite 不支持多进程同时写,所以你不能用它来支撑多个 PrometheusAlert 副本。
MySQL 适合数据量大、需要长期保留、或者要多副本部署的场景。配置上把驱动换成 mysql,填好地址、库名、账号密码即可,它会自动建表。多副本的前提就是所有副本指向同一个 MySQL。
我的经验判断标准很简单:单实例 + 记录保留一个月以内,用 SQLite;需要多副本,或者记录要跨年保留、要接数据分析,上 MySQL。中间那种“记录要留很久但只有单实例”的情况,SQLite 也扛得住,只是备份要做成定时拷贝文件。
2.3 端口、网络与暴露面的规划
默认 Web 端口是 8080。这个端口在很多环境里已经被占用了(比如某些管理后台),部署前先ss -lntp | grep 8080看一眼,撞了就换。换端口的方式有两种,配置文件的httpport或者启动参数,具体名字各版本略有差异,装完后用首页能不能打开来验证,比背参数名靠谱。
网络方向要想清楚两件事。第一,PrometheusAlert 需要能访问外网的机器人地址吗?如果是钉钉、企业微信、飞书这类 SaaS 机器人,答案是必须能出去。如果你们的出口是白名单制,要提前把这些域名放行。第二,谁能访问它的 8080?上游 Alertmanager、Grafana 所在的机器必须能通,其他机器尽量关掉。PrometheusAlert 默认有一个 Web 管理界面,登录账号默认是admin,密码默认是prometheusalert,这个默认密码第一次登录就必须改,改的方式是设置登录账号密码相关的环境变量或者配置项。
还有一个小细节:如果你前面挂了 Nginx 做域名,注意把请求体的体积放开一点。告警批次大的时候,Alertmanager POST 过来的 JSON 可能有好几 MB,Nginx 默认的client_max_body_size 1m会直接给你 413,然后你会在 Alertmanager 日志里看到发送失败,但完全不知道是 Nginx 拦的。
3. 从零把 PrometheusAlert 跑起来
3.1 二进制方式:最省事也最好排障
二进制方式我一般这么走。先拿到对应平台的压缩包,解压之后目录里通常有一个可执行文件、一个conf目录和一份示例配置。把可执行文件丢到/opt/prometheusalert/,配置放在/opt/prometheusalert/conf/,日志目录建好,然后直接前台跑一次,看它输出的端口和配置加载信息。
mkdir -p /opt/prometheusalert/{conf,logs,db} cd /opt/prometheusalert # 解压后的可执行文件建议重命名成一个固定的名字,方便写 systemd mv prometheusalert_linux_amd64 prometheusalert chmod +x prometheusalert # 前台先跑一次,确认端口和配置没问题 ./prometheusalert前台跑起来之后,浏览器打开http://机器IP:8080,能看到登录页就说明主进程没问题。这时候用默认账号登进去,第一件事改密码,第二件事看首页有没有报数据库相关的错误。确认没问题了,Ctrl+C 停掉,写 systemd 托管。
# /etc/systemd/system/prometheusalert.service [Unit] Description=PrometheusAlert After=network.target [Service] Type=simple WorkingDirectory=/opt/prometheusalert ExecStart=/opt/prometheusalert/prometheusalert Restart=always RestartSec=5 Environment=TZ=Asia/Shanghai [Install] WantedBy=multi-user.target这里Environment=TZ=Asia/Shanghai这行别省。我最早一次部署就是漏了它,结果告警记录里的时间全是 UTC,和本地时间差八小时,事后复盘时把两条不相干的告警看成同一时刻发生的,白白绕了半小时。容器方式同理,-e TZ=Asia/Shanghai加上,PrometheusAlert 自己也有时区相关的配置项,两边保持一致最稳。
3.2 Docker 与 Compose:参数怎么传才不乱
Docker 方式的核心是把“配置”和“数据”两件事处理干净。配置我倾向于用环境变量传关键项(登录账号密码、各渠道 token、数据库连接),用配置文件管那些不常变的项(端口、日志级别)。数据就是 db 文件和 logs 目录,必须挂出来。
docker run -d \ --name prometheusalert \ --restart=always \ -p 8080:8080 \ -e TZ=Asia/Shanghai \ -e PA_LOGIN_USER=admin \ -e PA_LOGIN_PASSWORD='换成你自己的强密码' \ -v /data/prometheusalert/conf:/app/conf \ -v /data/prometheusalert/db:/app/db \ -v /data/prometheusalert/logs:/app/logs \ feiyu563/prometheusalert:latest用 Compose 会更顺手,尤其是升级的时候,改个 tag 再up -d就行。写法上我把环境变量集中放在environment段,把易变的密码放到同目录的.env文件里,Compose 会自动读取,这样配置文件可以放心提交到代码仓库,密码不会跟着进去。
services: prometheusalert: image: feiyu563/prometheusalert:latest container_name: prometheusalert restart: always ports: - "8080:8080" environment: TZ: Asia/Shanghai PA_LOGIN_USER: admin PA_LOGIN_PASSWORD: ${PA_PASSWORD} PA_TIMEZONE: Asia/Shanghai volumes: - ./conf:/app/conf - ./db:/app/db - ./logs:/app/logs关于环境变量的命名,这里必须提醒一句:不同版本的前缀和拼写有过调整,我建议你不要完全照抄网上任何一篇教程(包括这篇),而是以你拉下来那个版本自带的示例配置为准。做法很简单,容器起来之后docker exec -it prometheusalert sh进去,把conf目录里的示例配置读一遍,再对照官方仓库的说明文档核一遍。这一步花五分钟,能省掉后面半小时“为什么我传了 token 还是发不出去”的困惑。
3.3 Kubernetes 部署:配置、存储与探针
K8s 部署我建议拆成四块:ConfigMap 放配置文件,Secret 放机器人的 token 和数据库密码,PVC 放数据和日志,Deployment + Service + Ingress 把它们串起来。
探针这块有个实际建议:优先用tcpSocket探 8080,或者 HTTP 探首页。PrometheusAlert 的健康检查路径在不同版本里不完全一致,用 TCP 探针最不容易踩坑,因为它只要求端口在监听,而这个条件在任何版本里都成立。
containers: - name: prometheusalert image: feiyu563/prometheusalert:latest ports: - containerPort: 8080 env: - name: TZ value: "Asia/Shanghai" - name: PA_LOGIN_PASSWORD valueFrom: secretKeyRef: name: prometheusalert-secret key: login-password volumeMounts: - name: conf mountPath: /app/conf - name: data mountPath: /app/db readinessProbe: tcpSocket: port: 8080 initialDelaySeconds: 10 periodSeconds: 10 livenessProbe: tcpSocket: port: 8080 initialDelaySeconds: 30 periodSeconds: 20副本数这里重点说一下。用 SQLite 的话,副本数必须是 1,而且 Deployment 的更新策略最好设置成Recreate,否则滚动更新时新旧两个 Pod 同时挂在同一个 PVC 上写 SQLite,轻则报锁错误,重则该写的记录丢了。想扩多副本,就先把存储换成 MySQL。
4. 核心配置拆解:渠道、模板、路由
4.1 通知渠道配置与几个容易填错的字段
渠道配置基本都在 Web 界面的“配置”菜单里,或者写在配置文件的对应段落。钉钉需要两个东西:机器人的 webhook 地址,以及安全设置里如果你选了“加签”,还需要那个以SEC开头的密钥。这两个字段分开填,只填 webhook 不填密钥,钉钉会返回签名校验失败。企业微信只需要一个 key,就是从 webhook 地址里key=后面那串,配置项叫不叫 token 各版本不一样,看到的字段含义对了就行。飞书自定义机器人的地址比较长,形如https://open.feishu.cn/open-apis/bot/v2/hook/xxxx,整条填进去。
| 渠道 | 必填项 | 常见错误 | 验证方式 |
|---|---|---|---|
| 钉钉 | webhook 地址 + 加签密钥 | 只填地址漏密钥、IP 段未加白 | 后台“测试”按钮发一条 |
| 企业微信 | webhook 中的 key | 把整条 URL 填进了 key 字段 | 群里看到测试消息 |
| 飞书 | 完整 webhook 地址 | 地址被截断、签名校验未开 | 群里看到测试消息 |
| 邮件 | SMTP 地址、端口、账号、授权码 | 用了登录密码而不是授权码 | 收件箱收到测试信 |
| 自定义 Webhook | 目标 URL、请求头 | 对方要求特定 Content-Type | 对方服务日志里看到请求 |
邮件这一项特别容易卡住。现在主流邮箱服务基本都要求用“授权码”而不是账号登录密码,端口和加密方式也有讲究:465 一般配 SSL,587 一般配 STARTTLS。如果你填完一直报认证失败,先别怀疑代码,去邮箱后台把授权码重新生成一个再试。
填完之后不要急着去改 Alertmanager,先在 PrometheusAlert 的配置页点“测试”,或者在首页找测试入口发一条。渠道本身通了,再去接上游,这样后面出问题就只剩一层链路要查。
4.2 模板机制与常用变量速查
模板是 PrometheusAlert 最值钱的部分,也是最容易出问题的地方。它的模板语法是 Go template,Alertmanager 传过来的那批告警在模板里就是标准的结构化数据,你可以遍历.Alerts,可以取.Status、.CommonLabels、.CommonAnnotations、.ExternalURL、.GroupLabels,每条告警里还有.Labels、.Annotations、.StartsAt、.EndsAt。
一个我常用的、兼顾信息量和可读性的钉钉 markdown 模板大致长这样:
## {{ .CommonLabels.alertname }} ({{ .Status }}) **告警数量**:{{ .Alerts | len }} **集群**:{{ .CommonLabels.cluster }} **时间**:{{ .CommonLabels.starts_at }} {{ range .Alerts }} --- **实例**:{{ .Labels.instance }} **级别**:{{ .Labels.severity }} **摘要**:{{ .Annotations.summary }} **详情**:{{ .Annotations.description }} {{ end }} [查看告警面板]({{ .ExternalURL }})这里面有几个关键取舍值得说。第一,为什么不直接在模板里格式化时间?因为不同渠道对时间字符串的容忍度不一样,最稳的做法是在告警规则那边就把时间做成一个 label 带过来,或者干脆不放时间,让 Alertmanager 的StartsAt原样透传,阅读时按需换算。第二,为什么用CommonLabels而不是遍历每一条的 labels?因为聚合后的告警往往共享同一组公共标签,用公共标签做标题最简洁,逐条信息放在循环里。第三,ExternalURL这个字段非常有用,它就是你 Alertmanager 的访问地址,拼出来的链接能一键跳回复盘页面,值班同学会感谢你这个细节。
模板里还有一类“全局可用变量”,比如告警条数这类统计值,具体名字各版本可能有差异,建议做法是:先用一个最简模板只输出{{ . }},把渲染结果看一眼,你就知道当前版本到底给了你哪些字段。这比翻文档快得多,也是我接手任何模板引擎时的第一招。
4.3 路由规则怎么用才不打架
路由解决的问题是:同一批告警,A 业务的进 A 群,B 业务的进 B 群。PrometheusAlert 的路由通常按告警里的某个标签做匹配,app是最常用的那一维。你在 Prometheus 的告警规则里给每条告警打上app: order-service,然后在路由配置里写“app 等于 order-service 的走订单群机器人”。
路由配置有三个坑。第一个坑是顺序和优先级,如果一个告警同时命中了两条规则会发生什么,取决于实现的匹配逻辑,是取第一条还是全部命中都发。我的做法是把路由设计成互斥的,宁可多写几条精确规则,也不要依赖“谁先谁后”这种隐含行为。第二个坑是缺省路由,一定要配一个兜底规则,否则某条新业务上线忘了配路由,告警就静默地消失了——这种“没报错但也没发出去”的问题最难查。第三个坑是标签没打上,路由匹配不到。上线新业务的时候,先在 Alertmanager 里确认告警带上了目标标签,再去配路由。
路由配好之后有一个验证动作必须做:手动构造一条带目标标签的告警,走一遍完整链路,确认它进了预期的群。我见过太多次“路由配置看着完全正确但就是不生效”,最后发现是标签名大小写不一致,或者标签根本就没在告警规则里定义。
4.4 告警记录与数据留存
告警记录的价值在事后。值班交接的时候,翻一遍昨晚发了哪些告警比口头描述靠谱得多;月底算告警数量、统计噪音比例,也需要有数据。启用记录的方式就是配置里把记录开关打开,再把存储指向你要用的后端。
数据量这件事要有预期。假设每天 200 条告警、每条记录按 2KB 算,一个月也就 12MB 左右,SQLite 完全无压力。但如果你的环境告警很吵,每天几千条,一年下来就是好几个 GB,这时候要么上 MySQL 并做定期归档,要么在告警源头把噪音先治理掉——后者才是根本解法。
提示:无论用哪种存储,都要把备份写成定时任务。SQLite 直接定期拷贝 db 文件即可(拷贝前最好先停写或者用 SQLite 的备份命令,避免拷到半截文件);MySQL 用常规的 mysqldump。备份这件事平时没感觉,等你要查三个月前那次故障的告警记录时,就知道它的价值了。
5. 对接上下游:Alertmanager、Grafana 与主动推送
5.1 Alertmanager webhook 对接的两种写法
对接 Alertmanager 有两条路,一条是让 Alertmanager 直接 POST 到 PrometheusAlert 的接收接口并在 URL 里带上渠道参数,另一条是把渠道信息交给 PrometheusAlert 的路由去决定,URL 里只带最少的参数。
第一种写法最直观,URL 里把类型、模板、机器人地址都写全:
receivers: - name: 'prometheusalert-dingtalk' webhook_configs: - url: 'http://10.0.0.10:8080/prometheusalert?type=dd&tpl=prometheus-dd&ddurl=https://oapi.dingtalk.com/robot/send?access_token=xxx' send_resolved: truetype指定渠道类型,tpl指定用哪个模板,send_resolved一定要设成true,否则恢复通知不会发出来——这是个极高频的遗漏项,很多人的“告警恢复了但群里没消息”就是因为它默认是false。
第二种写法更适合多渠道场景,URL 里不带机器人地址,由 PrometheusAlert 的路由根据标签决定投递目标。好处是机器人地址变了只改 PrometheusAlert 一处,Alertmanager 完全不用动,而且天然支持“同一批告警投多个群”。
receivers: - name: 'prometheusalert-router' webhook_configs: - url: 'http://10.0.0.10:8080/prometheusalert?type=dd&tpl=prometheus-dd' send_resolved: true改完 Alertmanager 配置记得 reload,然后去 Alertmanager 的界面看 Status 页,确认 receiver 配置已经被正确加载。这一步不做,你会对着旧配置查半天。
5.2 Grafana 与自研系统的接入
Grafana 的告警通知渠道里也能配 webhook,把它指向 PrometheusAlert 对应的接收路径即可。这里的关键是模板要换一套,因为 Grafana 推过来的 JSON 结构和 Alertmanager 不一样,字段名、嵌套层级都不同。我的建议是给 Grafana 单独建一个模板,名字上带grafana-前缀,别试图用一套模板同时吃两种数据源,那样只会让模板变得又长又脆。
自研系统接入的思路一样。发布系统上线完成后想发条通知、巡检脚本发现异常想推一条、批处理任务失败想告警,都只需要 POST 到 PrometheusAlert。这样做最直接的好处是把 token 集中到了一处:以后机器人换了、密钥轮转了,改 PrometheusAlert 的配置就行,散落在各处的脚本一个都不用动。
5.3 API 主动推送与自定义 Webhook
主动推送的写法就是一条 curl,把要渲染的字段当成 JSON 传进去,模板里用对应的变量取。比如你要发一条纯文本到飞书:
curl -X POST 'http://10.0.0.10:8080/prometheusalert?type=fs&tpl=自定义模板名' \ -H 'Content-Type: application/json' \ -d '{"msg":"订单服务发布完成,版本 v1.8.3","env":"prod"}'模板里就能用相应的变量把msg和env渲染出来。这套机制特别适合做发布通知和巡检报告:模板固定,字段由调用方填,展示风格统一。
自定义 Webhook 这个渠道也值得说一句,它的价值在于“把 PrometheusAlert 当成一个格式转换器”。有些内部系统只认特定格式的 JSON,你没有必要去改 PrometheusAlert 的代码,写一个自定义 Webhook,把目标地址、请求头、请求体模板配好,让它做一次格式转换再转发出去就行。我见过有人用它对接内部的工单系统,告警一来自动建单,效果出乎意料地好。
6. 实战避坑与排查手册
6.1 告警不来的逐段排查表
排查这类问题的核心方法只有一个:把链路切成段,从后往前推,每段单独验证。先确认渠道本身通不通,再确认 PrometheusAlert 收没收到,再确认 Alertmanager 发没发出去,最后看 Prometheus 有没有真的触发告警。
| 现象 | 优先排查位置 | 常见原因 | 处理方式 |
|---|---|---|---|
| 测试都发不出去 | PrometheusAlert 配置 | token 填错、加签密钥缺失 | 重新复制 webhook 与密钥 |
| 测试能发,真实告警不来 | Alertmanager | receiver 未 reload、URL 写错 | 看 Status 页确认配置已加载 |
| Alertmanager 报发送失败 | 网络与代理层 | 反向代理体积限制、防火墙 | 放开 body 大小与出站策略 |
| 消息发出但内容空白 | 模板 | 变量名写错、字段不存在 | 用输出整个上下文的最简模板定位 |
| 恢复了但没通知 | Alertmanager | send_resolved为 false | 改成 true 并 reload |
| 历史里没有记录 | 存储 | 记录开关未开、目录未挂载 | 检查配置与挂载路径、确认可写 |
从后往前推还有个小技巧:让 PrometheusAlert 的日志级别先调到 debug,观察它接收到请求时打印的原始内容。很多时候你在 Alertmanager 侧纠结半天,日志里一眼就能看出“根本没收到任何请求”,方向立刻就明确了。
6.2 模板渲染与消息截断的坑
模板相关的故障有两类,一类是渲染出来一片空白,一类是消息发出去被截断。空白的原因八成是变量名对不上——你写.Labels.instance但告警里根本没有instance这个标签,Go template 对不存在的字段不会报错,只会输出空。定位方法就是前面说的,先用最简模板把整个上下文吐出来看一遍。
截断的原因基本是渠道的长度限制。企业微信 markdown 消息的上限明显比钉钉紧,一批告警几十条的时候非常容易超。这个问题有三种解法,我按推荐程度排:第一,在 Alertmanager 那边把group_by调细,让每批告警的条数变少,从源头控制消息长度;第二,在模板里做截断,只展示前 N 条,剩下的用一句“另有 X 条告警请点击链接查看”带过;第三,把详情交给一个跳转页面,消息里只放摘要。
注意:消息里如果有特殊字符,比如 markdown 语法里的星号、下划线、方括号,可能导致渲染异常。项目名、实例名里带这些字符的场景不少,稳妥的做法是在模板里适当转义,或者干脆把这类字段放在代码块样式里输出。
6.3 告警风暴、限流与聚合的分工
每个 IM 机器人都有频率限制,量级大概在每分钟几十条这个范围,一旦超了就会返回限流错误,表现是“部分告警发出去了、部分没发”。这也是很多人第一次遇到“告警丢失”的原因。
治理这个问题的根子在源头。第一层是 Prometheus 告警规则的阈值要合理,别让一个磁盘使用率波动就来回触发;第二层是 Alertmanager 的聚合参数,group_wait太短会导致同一类告警重复发,repeat_interval太短会导致长时间未恢复的告警反复提醒;第三层才是 PrometheusAlert 的路由和模板,把同类告警合并成一条更长的消息而不是多条短消息。
我的经验是,一套健康的告警系统里,普罗米修斯的规则治理占七成功劳,Alertmanager 的参数调优占两成,剩下的才是通知工具的部分。指望靠通知工具去解决告警风暴,方向从一开始就错了。
6.4 升级、备份与多副本
升级前必须备份两样东西:配置文件和数据库。配置文件里躺着所有渠道的 token 和路由规则,数据库里躺着历史记录。升级的动作就是换镜像 tag 或者换二进制文件,重启,然后立刻做三件事:登录看首页有没有异常、发一条测试消息、翻一下告警记录能不能正常写入。这三步做完,升级才算真的完成。
多副本这件事再强调一次:SQLite 不支持,别硬上。要副本就先换 MySQL,换完之后所有副本指向同一个库,前面用负载均衡把上游请求分过来。但说句实话,PrometheusAlert 本身负载很轻,绝大多数场景单实例完全够用,把精力花在“数据库定时备份”和“配置版本化”上,收益比折腾多副本大得多。
配置版本化是个我强烈推荐的习惯:把配置文件和 Compose 文件一起放到代码仓库里,token 用环境变量注入,每次改路由、改模板都留一次提交记录。这样某天有人问“上周三那条告警为什么发到测试群了”,你能直接翻出当时的配置。我在吃过一次“谁把路由改了导致生产告警进了测试群”的亏之后,就再也没让配置脱离版本管理。
最后分享一个我一直在用的小习惯:给 PrometheusAlert 单独建一个“通知测试群”,所有新配的模板、新加的渠道、新写的路由,都先在这个群里跑一遍。看着一条渲染完整的消息真的出现了,再去动生产配置。这个习惯看着笨,但它把“改配置”这件事从一次需要屏住呼吸的操作,变成了一件随手就能做的小事。