Alertmanager 对接钉钉-企微 webhook 告警指南

Alertmanager 对接钉钉/企微 webhook 实测:webhook receiver 配置、v4 协议 payload 完整结构、直接推送官方端点失败回显、自建转换层(v4→钉钉/企微 markdown)全链路转发复现与字段映射

钉钉/企微告警webhook钉钉/企微webhook告警2026/10/09

Alertmanager 对接钉钉-企微 webhook 告警指南

目标:实测 Alertmanager(0.34.1)通过 webhook_configs 对接钉钉/企微机器人的完整链路——webhook receiver 配置、v4 协议 payload 完整结构、直接推送官方端点的失败回显(300005/93000)、自建转换层把 v4 payload 转为钉钉/企微 markdown 消息的全链路复现与字段映射。机器人创建为 ⚠️ 前置(详见《钉钉机器人 webhook 告警接入指南》《企业微信机器人 webhook 告警接入指南》),本环境的转发/转换逻辑在本地完整实测。

一、环境信息

信息项值
Alertmanager 版本0.34.1(Ubuntu 22.04 单实例,systemd 托管)
官方端点钉钉 https://oapi.dingtalk.com/robot/send?access_token=<TOKEN>;企微 https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=<KEY>
直接对接实测假 token/key → 300005 / 93000(见 §四)
转换层自建 Python 服务(监听 19091),接收 v4 → 转 markdown → 转发模拟端点
本地回显端点http://127.0.0.1:19090/dingtalk(复现官方端点收到钉钉格式消息)

二、接收端准备(本地回显端点,模拟官方机器人端点)

本环境无真实钉钉/企微群,用 Python HTTP 服务接收并原样打印请求,用于验证"发出的消息长什么样":

python3 /tmp/webhook_echo.py 19090

💡 说明:该脚本监听 19090 端口,收到 POST 后打印 METHOD/PATH/HEADERS/BODY 到控制台。真实环境用官方端点替换即可。

三、Alertmanager webhook receiver 配置

在 alertmanager.yml 中定义 receiver:

route:
  group_by: ["alertname"]
  group_wait: 1s
  group_interval: 5s
  repeat_interval: 1m
  receiver: "dingtalk"

receivers:
  - name: "dingtalk"
    webhook_configs:
      - url: "http://127.0.0.1:19091/convert"
        send_resolved: true
        max_alerts: 64
        http_config:
          timeout: 10s
参数默认说明
url-接收端地址(官方端点或转换层)
send_resolvedfalse是否发送 resolved 恢复通知
max_alerts0(不截断)单次请求最多携带告警数,超过被截断(truncatedAlerts 标记)
http_config.timeout10s请求超时,超时触发 Alertmanager 端重试
http_config.bearer_token / authorization-需要鉴权的接收端(如企业内部门户)

配置后校验并热加载:

amtool check-config /etc/alertmanager/alertmanager.yml
curl -X POST http://127.0.0.1:9093/-/reload

实测回显(check-config):

Checking '/etc/alertmanager/alertmanager.yml'  SUCCESS

四、实测 1:v4 payload 直接推送官方端点(失败回显)

把 Alertmanager 的 webhook url 直接指向官方端点时,Alertmanager 发送的是自身 v4 协议 JSON,与钉钉/企微要求的 msgtype 消息结构不兼容。用假凭据实测端点契约(真实凭据同样不识别 v4 结构,需转换层,见 §五/§六):

# 钉钉:v4 payload 直推
curl -s -X POST "https://oapi.dingtalk.com/robot/send?access_token=INVALID_TOKEN_123" \
  -H "Content-Type: application/json" \
  -d '{"receiver":"echo","status":"firing","alerts":[{"status":"firing","labels":{"alertname":"DiskFull","instance":"db-01","severity":"critical"},"annotations":{},"startsAt":"2026-10-08T21:30:01Z","endsAt":"0001-01-01T00:00:00Z"}],"groupLabels":{"alertname":"DiskFull"},"commonLabels":{"alertname":"DiskFull"},"externalURL":"http://am:9093","version":"4","groupKey":"{}:{alertname=\"DiskFull\"}"}'

实测回显:

{"errcode":300005,"errmsg":"token is not exist"}
# 企微:v4 payload 直推
curl -s -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=FAKE-KEY-1234567890" \
  -H "Content-Type: application/json" \
  -d '<同上 v4 JSON>'

实测回显:

{"errcode":93000,"errmsg":"invalid webhook url, hint: [1791495031356212450879172], from ip: 42.63.89.89, more info at https://open.work.weixin.qq.com/devtool/query?e=93000"}

⚠️ 结论:即便换成有效凭据,v4 payload 也缺少 msgtype 字段、结构与钉钉/企微协议不符,官方端点不会按告警卡片展示。生产对接必须经转换层(§五)把 v4 转成各自消息格式,或使用官方/社区现成组件(如 prometheus-webhook-dingtalk)。

五、实测 2:转换层全链路转发复现

转换层职责:接收 Alertmanager v4 payload → 提取关键字段 → 组装钉钉/企微 markdown 消息 → 转发官方端点。本环境转发到本地回显端点验证。

5.1 转换层服务(本环境实现,Python)

# /tmp/am_dingtalk_proxy.py(节选核心:v4 -> 钉钉 markdown)
def convert_v4(payload):
    first = payload["alerts"][0]
    labels, annos = first["labels"], first["annotations"]
    msg = (
        "### %s\n> %s\n\n"
        "- 级别: **%s**\n- 实例: %s\n- 状态: %s\n"
        "- 开始时间: %s\n- 分组: %s"
    ) % (
        "【%s】%s" % ("恢复" if payload["status"] == "resolved" else "告警", labels["alertname"]),
        annos.get("summary", "无摘要"),
        labels.get("severity", "unknown"), labels.get("instance", "-"),
        payload["status"], first["startsAt"][:19].replace("T", " "),
        payload.get("groupKey", ""),
    )
    return {"msgtype": "markdown", "markdown": {"title": "【告警】" + labels["alertname"], "text": msg}}

启动转换层(监听 19091):

nohup python3 /tmp/am_dingtalk_proxy.py > /tmp/am_dingtalk_proxy.log 2>&1 &

5.2 触发告警验证链路

推送一条带注解的告警到 Alertmanager:

curl -s -X POST http://127.0.0.1:9093/api/v2/alerts \
  -H "Content-Type: application/json" \
  -d '[{"labels":{"alertname":"HighCPU","instance":"app-02","severity":"critical"},"annotations":{"summary":"CPU 使用率持续 95% 超过 10 分钟"},"startsAt":"2026-10-08T21:35:00Z"}]'

Alertmanager 路由后经 webhook 发出的 v4 payload 完整结构(转换层/回显端点实际收到):

{
  "receiver": "echo",
  "status": "firing",
  "alerts": [
    {
      "status": "firing",
      "labels": {
        "alertname": "HighCPU",
        "instance": "app-02",
        "severity": "critical"
      },
      "annotations": {
        "summary": "CPU 使用率持续 95% 超过 10 分钟"
      },
      "startsAt": "2026-10-08T21:35:00Z",
      "endsAt": "0001-01-01T00:00:00Z",
      "generatorURL": "",
      "fingerprint": "f091dfb75ab9ad11"
    }
  ],
  "notification_reason": "first notification",
  "groupLabels": {
    "alertname": "HighCPU"
  },
  "commonLabels": {
    "alertname": "HighCPU",
    "instance": "app-02",
    "severity": "critical"
  },
  "commonAnnotations": {
    "summary": "CPU 使用率持续 95% 超过 10 分钟"
  },
  "routeLabels": {},
  "externalURL": "http://947a50672d4a:9093",
  "version": "4",
  "groupKey": "{}:{alertname=\"HighCPU\"}",
  "truncatedAlerts": 0
}

转换层日志(已转为钉钉 markdown 消息):

=== converted to DingTalk markdown ===
{
  "msgtype": "markdown",
  "markdown": {
    "title": "【告警】HighCPU",
    "text": "### 【告警】HighCPU\n> CPU 使用率持续 95% 超过 10 分钟\n\n- 级别: **critical**\n- 实例: app-02\n- 状态: firing\n- 开始时间: 2026-10-08 21:35:00\n- 分组: {}:{alertname=\"HighCPU\"}"
  }
}
=== echo response ===
{"status":"ok"}

回显端点(模拟钉钉端点)实际收到的消息体:

METHOD: POST
PATH: /dingtalk
HEADERS: {'Content-Type': 'application/json', 'Host': '127.0.0.1:19090', 'User-Agent': 'Python-urllib/3.10', 'Content-Length': '372'}
BODY: {
  "msgtype": "markdown",
  "markdown": {
    "title": "【告警】HighCPU",
    "text": "### 【告警】HighCPU\n> CPU 使用率持续 95% 超过 10 分钟\n\n- 级别: **critical**\n- 实例: app-02\n- 状态: firing\n- 开始时间: 2026-10-08 21:35:00\n- 分组: {}:{alertname=\"HighCPU\"}"
  }
}

💡 链路结论:告警来源 → Alertmanager(路由/分组/抑制/静默)→ webhook v4 → 转换层(v4→markdown)→ 钉钉/企微端点。Alertmanager 侧只需把 receiver 的 url 指到转换层即可,告警聚合去重等逻辑全部沿用 Alertmanager。

六、v4 → 钉钉/企微 字段映射与差异

v4 payload 字段映射到钉钉 markdown映射到企微 markdown
alerts[].labels.alertnamemarkdown.title + 正文标题正文标题(企微无 title 字段)
alerts[].labels.instance/severity正文列表项正文列表项
alerts[].annotations.summary/description正文引用行正文引用行
status(firing/resolved)标题前缀【告警】/【恢复】同左
groupKey / commonLabels分组信息行同左
externalURL跳转链接(可选)同左

差异注意:

  1. 企微 markdown 无独立 title:会话列表直接显示正文首行,标题样式需写进 content(如 ### 【告警】HighCPU)。
  2. 钉钉 markdown 有 title(会话列表纯文本标题)与 text(正文)。
  3. 转换层必须处理 status=resolved(send_resolved: true 时 Alertmanager 会发恢复通知),否则恢复事件被丢弃、群里只有告警没有恢复。
  4. 大告警批次会被 max_alerts 截断,truncatedAlerts > 0 时转换层可追加"另有 N 条被截断"提示。

七、常见问题

1. webhook 发送失败会怎样?

Alertmanager 对非 2xx 响应按指数退避重试(约 5s/10s/20s…),长时间失败后丢弃并记录 level=error;转换层需保证幂等(重复投递是正常的)。

2. 一条告警被分组后发几次?

按路由 group_by 分组聚合,repeat_interval 内同组只发一次;firing 与 resolved 各发一次(send_resolved 开启时)。

3. 必须自建转换层吗?

不是。可选:①社区现成组件(prometheus-webhook-dingtalk 等);②钉钉/企微官方"自定义机器人"配合云函数/Serverless 中转;③本文 §五 的自建方案(约 40 行,可控无外部依赖)。

4. Alertmanager 直接发官方端点收到 300005/93000?

凭据无效时必现;凭据有效时因 v4 结构不兼容同样无法按告警卡片展示。两步都确认后仍异常,按《钉钉机器人 webhook 告警接入指南》§六 /《企业微信机器人 webhook 告警接入指南》§五 排查。

八、参考链接

Alertmanager 对接钉钉-企微 webhook 告警指南 - Quzuo