首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >5 分钟接入钉钉告警推送,我们是怎么把 P0 漏推事故变成 3 道防线的

5 分钟接入钉钉告警推送,我们是怎么把 P0 漏推事故变成 3 道防线的

作者头像
行者全栈架构师
发布2026-07-14 13:39:34
发布2026-07-14 13:39:34
1110
举报

凌晨 3 点,MySQL 主库宕机,告警触发——但 40 分钟后运维才收到通知。不是因为告警没发出来,而是推送通道自己先挂了。

01 一个 P0 告警漏推 40 分钟的真实事故

2026 年 6 月的一个周三凌晨,MySQL 主库宕机。

Alertmanager 在 03:15:00 精准触发了 P0 告警,通过 Webhook 推送到 Hermes Agent。然而运维团队等了整整 40 分钟都没有收到钉钉通知,直到用户投诉才发现服务已经挂了。

指标

预期

实际

告警触发时间

03:15:00

✅ 正常触发

Webhook 推送时间

03:15:02

❌ 从未到达 Hermes

钉群收到通知

03:15:05

🚨 晚了 40 分钟

故障持续

40 分钟(无人响应)

排查过程

Step 1:检查 Alertmanager 日志

代码语言:javascript
复制
journalctl -u alertmanager --since "03:10" --until "03:20" | grep -i "webhook\|error"

输出:

代码语言:javascript
复制
Mar 15 03:15:01 alertmanager[12345]: level=error msg="Notify for webhook failed"
  receiver=hermes-agent err="Post \"http://hermes-agent:8080/api/v1/alerts\":
  dial tcp 10.0.1.50:8080: connect: connection refused"

Step 2:检查 Hermes Agent

代码语言:javascript
复制
systemctl status hermes-agent
代码语言:javascript
复制
● hermes-agent.service - Hermes AI Agent
   Loaded: loaded (/etc/systemd/system/hermes-agent.service; enabled)
   Active: inactive (dead) since Tue 2026-03-14 22:30:00 CST
# ⚠️ 上周扩容操作后忘记重启了!

Step 3:有监控告警通道的健康检查吗?

代码语言:javascript
复制
grep -r "webhook\|health.*check" /etc/prometheus/alerts/
# 输出为空 ❌

根因三层

根因 1:Hermes Agent 进程被意外停止。上周 K8s 节点扩容,运维同事停掉了非 K8s 管理的服务,忘了重启 Hermes。

根因 2:没有告警通道健康检查。Alertmanager → Hermes Agent → 钉钉这条链路是否正常,没有任何监控。

根因 3:没有配置降级通知。钉钉不通时,没有自动切到邮件或短信兜底。

三个理由加在一起,就是"必出事"。

修复方案

代码语言:javascript
复制
# Prometheus 监控告警通道健康
groups:
  - name: alert_channel_health
    rules:
      - alert: HermesAgentDown
        expr: up{job="hermes-agent"} == 0
        for: 1m
        labels: { severity: P1 }
        annotations:
          summary: "Hermes Agent 不可达"
          description: "告警推送通道中断,钉钉将收不到通知"

      - alert: WebhookDeliveryFailed
        expr: rate(alertmanager_notification_failed_total{integration="webhook"}[5m]) > 0
        for: 2m
        labels: { severity: P0 }
        annotations:
          summary: "Webhook 推送失败"
代码语言:javascript
复制
# 配置降级:钉钉不通时走邮件
receivers:
  - name: hermes-agent
    webhook_configs:
      - url: "http://hermes-agent:8080/api/v1/alerts"
        send_resolved: true
    email_configs:
      - to: "ops@example.com"

修复后,4 个月内零漏推。

教训:告警通道本身也要被监控。监控系统的"最后一公里"往往最脆弱。

02 钉钉告警到底比邮件快多少?

回到根本问题——为什么非要搞钉钉推送?

我之前一直用 Alertmanager 发邮件告警,痛点一堆:

痛点

具体表现

邮件延迟

平均 3-5 分钟才到手机

信息过载

200+ 封告警邮件,P0 夹在营销邮件中间

无交互

告警邮件只能看,不能认领或静默

移动不友好

手机上看监控图和日志体验极差

有一次凌晨 P0 故障,告警邮件被手机自动归到"推广"文件夹,我早上 8 点才看到。

邮件 vs 钉钉对比

维度

邮件告警

钉钉告警

推送延迟

3-5 分钟

< 3 秒

移动体验

差(排版不适配)

好(卡片原生适配)

可交互

是(认领/静默/跳转)

群协作

转发麻烦

群内 @相关人

配置复杂度

低(SMTP)

低(Webhook)

结论:不是邮件不能用,而是钉钉把"发出去通知"升级成了"让团队能立即响应"。

03 5 分钟从零配置钉钉告警推送

Step 1:创建钉钉自定义机器人

打开钉钉群 → 群设置 → 智能群助手 → 添加机器人 → 自定义

  • 机器人名称:Hermes 告警机器人
  • 安全设置:选择 加签(比 IP 白名单更灵活)
  • 复制 Webhook 地址和加签密钥

为什么选加签?运维机器 IP 可能变化(如 K8s Pod 重建),加签用密钥验证更稳定。

获取的信息:

代码语言:javascript
复制
Webhook: https://oapi.dingtalk.com/robot/send?access_token=abc123def456
Secret:  SECxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Step 2:Hermes Agent 通知渠道配置

代码语言:javascript
复制
# hermes-agent.yml - 钉钉通知渠道配置
notifiers:
  - name: dingtalk-ops
    type: dingtalk
    enabled: true
    config:
      webhook: "https://oapi.dingtalk.com/robot/send?access_token=abc123def456"
      secret: "SECxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      msg_type: "actionCard"
    # 消息模板
    templates:
      critical: |
        ## 🔴 P0 告警 - {{ .Labels.alertname }}
        **服务**: {{ .Labels.job }}
        **环境**: {{ .Labels.env }}
        **实例**: {{ .Labels.instance }}
        **详情**: {{ .Annotations.description }}
        **触发时间**: {{ .StartsAt.Format "2006-01-02 15:04:05" }}

        👉 请立即响应!
      warning: |
        ## 🟡 P2 告警 - {{ .Labels.alertname }}
        **服务**: {{ .Labels.job }}
        **详情**: {{ .Annotations.description }}
        **触发时间**: {{ .StartsAt.Format "2006-01-02 15:04:05" }}

Step 3:告警路由绑定

代码语言:javascript
复制
# hermes-agent.yml - 告警路由配置
routes:
  - name: ops-critical
    match:
      severity: critical
    notifier: dingtalk-ops
    template: critical
    # 互动按钮
    actions:
      - title: "✅ 认领告警"
        action: "acknowledge"
      - title: "🔇 静默 1 小时"
        action: "silence"
        params:
          duration: "1h"
      - title: "📊 查看 Dashboard"
        action: "link"
        url: "https://grafana.internal/d/{{ .Labels.job }}"

  - name: ops-warning
    match:
      severity: warning
    notifier: dingtalk-ops
    template: warning
    actions:
      - title: "✅ 认领告警"
        action: "acknowledge"
      - title: "📊 查看 Dashboard"
        action: "link"
        url: "https://grafana.internal/d/{{ .Labels.job }}"

5 分钟,3 步,钉钉告警推送跑通。

04 告警卡片长这样

用 actionCard 而不是普通 markdown——因为支持按钮交互,运维可以在钉钉里直接认领告警或静默。

代码语言:javascript
复制
import hmac
import hashlib
import base64
import time
import requests
import urllib.parse

def sign_request(secret: str) -> tuple[str, str]:
    """计算钉钉加签"""
    timestamp = str(round(time.time() * 1000))
    string_to_sign = f"{timestamp}\n{secret}"
    hmac_code = hmac.new(
        secret.encode("utf-8"),
        string_to_sign.encode("utf-8"),
        digestmod=hashlib.sha256
    ).digest()
    sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
    return timestamp, sign

def send_action_card(
    webhook: str, secret: str, title: str,
    text: str, buttons: list[dict]
) -> dict:
    timestamp, sign = sign_request(secret)
    url = f"{webhook}&timestamp={timestamp}&sign={sign}"
    payload = {
        "msgtype": "actionCard",
        "actionCard": {
            "title": title,
            "text": text,
            "btnOrientation": "1",
            "btns": buttons,
        }
    }
    resp = requests.post(
        url, json=payload,
        headers={"Content-Type": "application/json"}
    )
    return resp.json()

# 发送 P0 告警
result = send_action_card(
    webhook="https://oapi.dingtalk.com/robot/send?access_token=abc123def456",
    secret="SECxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    title="🔴 P0 - MySQLDown",
    text="""## 🔴 P0 告警 - MySQLDown

**服务**: order-db
**环境**: production
**实例**: 10.0.1.5:3306
**详情**: MySQL 主库连接失败
**触发时间**: 2026-07-01 03:25:01

> 请立即响应!""",
    buttons=[
        {"title": "✅ 认领告警", "actionURL": "https://hermes.internal/api/ack/mysql-down-abc123"},
        {"title": "🔇 静默 1h", "actionURL": "https://hermes.internal/api/silence?alert=mysql-down&duration=1h"},
        {"title": "📊 Dashboard", "actionURL": "https://grafana.internal/d/order-db"},
    ]
)

在钉钉群里看到的效果:

代码语言:javascript
复制
┌──────────────────────────────────┐
│ 🔴 P0 告警 - MySQLDown           │
│                                  │
│ 服务: order-db                   │
│ 环境: production                 │
│ 实例: 10.0.1.5:3306              │
│ 详情: MySQL 主库连接失败          │
│ 时间: 2026-07-01 03:25:01        │
│                                  │
│ ┌───── ✅ 认领告警 ──────────┐   │
│ ├───── 🔇 静默 1h ───────────┤   │
│ └───── 📊 Dashboard ──────────┘   │
└──────────────────────────────────┘

05 Alertmanager 对接 + 通道监控

Alertmanager 配置 Webhook

代码语言:javascript
复制
# alertmanager.yml
route:
  receiver: hermes-agent
  group_wait: 15s
  group_interval: 3m
  repeat_interval: 10m
  group_by: [ 'alertname', 'job', 'severity' ]

receivers:
  - name: hermes-agent
    webhook_configs:
      - url: "http://hermes-agent:8080/api/v1/alerts"
        send_resolved: true
        http_config:
          headers:
            Authorization: "Bearer hermes-token-xxx"

告警推送通道监控项

监控项

采集方式

告警阈值

级别

Hermes Agent 可用性

Prometheus up

== 0 持续 30s

P1

Webhook 推送成功率

Alertmanager 指标

< 99%

P0

钉钉 API 响应时间

Hermes 内部指标

P99 > 2s

P2

告警端到端延迟

端到端拨测

> 10s

P1

钉钉 Token 有效性

定时拨测

401 响应

P0

降级通道可用性

定时拨测邮件/短信

不可用

P1

健康检查脚本

代码语言:javascript
复制
#!/bin/bash
# alert_channel_health.sh - 告警推送通道健康检查

# 1. 检查 Hermes Agent 进程
if ! systemctl is-active --quiet hermes-agent; then
    echo "[ALERT] Hermes Agent 进程停止,尝试重启..."
    systemctl restart hermes-agent
    sleep 5
    if ! systemctl is-active --quiet hermes-agent; then
        curl -X POST "https://oapi.dingtalk.com/robot/send?access_token=TOKEN" \
            -H "Content-Type: application/json" \
            -d '{"msgtype":"text","text":{"content":"🚨 Hermes Agent 重启失败!"}}'
    fi
fi

# 2. 拨测钉钉 Webhook
python3 /opt/scripts/dingtalk_probe.py \
    --webhook "https://oapi.dingtalk.com/robot/send?access_token=TOKEN" \
    --secret "SECRET" --timeout 5 \
    || echo "[ALERT] 钉钉推送链路故障"

部署后,告警通道再出问题,第一时间告警你——而不是等你发现了再追责。

手动脚本 vs Hermes Agent 配置

维度

手动 Python 脚本

Hermes Agent 配置

开发量

100+ 行代码

50 行 YAML

加签逻辑

自己实现

内置

模板渲染

手拼字符串

Go Template

告警恢复通知

需额外处理

send_resolved: true

按钮交互

需自建 HTTP 服务

内置

维护成本

低(只改 YAML)

06 三个必须避开的坑

坑 1:加签报 "sign not match"

现象:发送消息后返回 {"errcode":310000,"errmsg":"sign not match"}

原因:加签计算时 timestamp 和 secret 的拼接格式有误,或 timestamp 与服务器时间差超过 1 小时。

解决

代码语言:javascript
复制
# 检查服务器时间是否准确
ntpdate -q ntp.aliyun.com

# 如果偏差大,同步时间
sudo ntpdate ntp.aliyun.com

加签的 timestamp 必须是当前毫秒时间戳,不能用缓存值。

坑 2:告警恢复消息还是红色 P0 格式

现象:服务恢复了,钉钉收到的消息还是红色 P0 告警格式,差点把值班同事吓一跳。

原因:模板没有区分 firingresolved

解决

代码语言:javascript
复制
templates:
  critical: |
    {{- if eq .Status "firing" -}}
    ## 🔴 P0 告警 - {{ .Labels.alertname }}
    **详情**: {{ .Annotations.description }}
    **触发时间**: {{ .StartsAt.Format "2006-01-02 15:04:05" }}
    {{- else -}}
    ## 🟢 已恢复 - {{ .Labels.alertname }}
    **持续时间**: {{ .EndsAt.Sub .StartsAt }}
    **恢复时间**: {{ .EndsAt.Format "2006-01-02 15:04:05" }}
    {{- end -}}

告警恢复通知比触发通知更重要——它告诉值班人员"可以安心了"。

坑 3:Webhook 用了 localhost,K8s 访问不通

现象:Alertmanager 配了 http://localhost:8080,但 Hermes Agent 在另一个 Pod 里,推送失败。

原因:K8s 中每个 Pod 独立网络命名空间,localhost 指的是自己 Pod。

解决

代码语言:javascript
复制
# 用 K8s Service DNS,不用 localhost
receivers:
  - name: hermes-agent
    webhook_configs:
      - url: "http://hermes-agent.ops-monitoring.svc.cluster.local:8080/api/v1/alerts"
        send_resolved: true

K8s 环境中服务间通信一律用 Service DNS。

07 总结:从漏推 40 分钟到零漏推

Hermes Agent + 钉钉告警推送的核心价值:

  • 推送延迟:从 3-5 分钟 → < 3 秒(⬇️ 99%)
  • 交互能力:从"只能看"到"认领/静默/跳转"三按钮
  • 配置成本:50 行 YAML 替代 100+ 行 Python 脚本
  • 可靠性:告警通道健康监控 + 降级方案 = 4 个月零漏推

适合谁用:团队用钉钉办公、需要移动端快速响应告警、希望告警可交互(认领/静默)

不适合谁用:不用钉钉(飞书/企微有替代方案)、告警量 < 5 条/天(邮件足够)

💬 你们团队用什么渠道接收告警?钉钉、飞书、企微、还是邮件?评论区聊聊你的选择~选钉钉的人最多我猜,但我可能猜错了 😄

⭐️ 觉得有用?点个「在看」和「转发」,让更多运维兄弟 5 分钟搞定告警推送~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-07-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 行者架构谈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 01 一个 P0 告警漏推 40 分钟的真实事故
    • 排查过程
    • 根因三层
    • 修复方案
  • 02 钉钉告警到底比邮件快多少?
    • 邮件 vs 钉钉对比
  • 03 5 分钟从零配置钉钉告警推送
    • Step 1:创建钉钉自定义机器人
    • Step 2:Hermes Agent 通知渠道配置
    • Step 3:告警路由绑定
  • 04 告警卡片长这样
  • 05 Alertmanager 对接 + 通道监控
    • Alertmanager 配置 Webhook
    • 告警推送通道监控项
    • 健康检查脚本
    • 手动脚本 vs Hermes Agent 配置
  • 06 三个必须避开的坑
    • 坑 1:加签报 "sign not match"
    • 坑 2:告警恢复消息还是红色 P0 格式
    • 坑 3:Webhook 用了 localhost,K8s 访问不通
  • 07 总结:从漏推 40 分钟到零漏推
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档