技术博客

Alertmanager 实战:告警路由、分组、静默与 PagerDuty/钉钉集成

深入讲解 Prometheus Alertmanager 的核心能力:路由树(route tree)将不同告警分发到不同接收者、告警分组(grouping)合并同类告警减少噪音、静默(silence)和抑制(inhibition)规则、与钉钉/飞书/PagerDuty 集成,以及告警升级策略的完整生产配置。

AlertmanagerPrometheus告警监控钉钉运维

Prometheus 负责发现告警(PromQL 规则求值),Alertmanager 负责处理告警(路由、分组、去重、通知)。很多团队 Prometheus 配置正确但 Alertmanager 配置混乱,导致告警风暴(同一故障发出几十条通知)或漏报(重要告警被淹没)。本文讲解 Alertmanager 的核心机制和生产级配置。

Alertmanager 核心概念

Prometheus → (告警规则触发) → 告警 → Alertmanager → 通知接收者

Alertmanager 做什么:
  1. 接收 Prometheus 发来的告警
  2. 分组(Grouping):同类告警合并(一个服务挂了,10 个节点的告警 → 1 条通知)
  3. 抑制(Inhibition):高严重性告警触发时,屏蔽低严重性告警
  4. 静默(Silence):维护窗口期间临时屏蔽告警
  5. 路由(Routing):根据标签把告警发给正确的团队
  6. 通知(Notification):Email/Slack/钉钉/PagerDuty

告警状态:
  pending    → 告警触发但还没超过 for 时间(Prometheus 内部)
  firing     → 持续超过 for 时间,发给 Alertmanager
  resolved   → 告警恢复,Alertmanager 发恢复通知

一、Alertmanager 配置结构

# alertmanager.yml 基本结构
global:
  # 全局默认值
  resolve_timeout: 5m          # 告警恢复后等多久发恢复通知(默认 5m)
  smtp_smarthost: 'smtp.company.com:587'
  smtp_from: 'alertmanager@company.com'
  smtp_auth_username: 'alerts@company.com'
  smtp_auth_password: 'secret'

# 路由树(必须配置)
route:
  receiver: 'default'          # 默认接收者(未匹配任何子路由时使用)
  group_by: ['alertname', 'cluster', 'namespace']
  group_wait: 30s              # 收到第一条告警后等 30s 再发(等其他同组告警聚合)
  group_interval: 5m           # 同组有新告警时,5 分钟后发通知(不是每条都发)
  repeat_interval: 4h          # 告警持续时,4 小时重发一次(避免被遗忘)

  routes:
    # 子路由 1:关键业务告警 → PagerDuty(立即通知)
    - matchers:
        - severity="critical"
      receiver: pagerduty
      group_wait: 5s           # 关键告警快速通知
      repeat_interval: 1h

    # 子路由 2:Kubernetes 相关告警 → 基础设施团队
    - matchers:
        - team="infra"
      receiver: slack-infra

    # 子路由 3:业务告警 → 业务开发团队
    - matchers:
        - team="dev"
      receiver: dingtalk-dev

# 抑制规则
inhibit_rules:
  - source_matchers:
      - severity="critical"
    target_matchers:
      - severity="warning"
    equal: ['alertname', 'cluster', 'namespace']
    # 当同一 alertname/cluster/namespace 已有 critical 告警时,
    # 屏蔽对应的 warning 告警(减少噪音)

# 接收者定义
receivers:
  - name: 'default'
    email_configs:
      - to: 'oncall@company.com'

  - name: 'pagerduty'
    pagerduty_configs:
      - integration_key: 'xxx'

  - name: 'slack-infra'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/xxx'
        channel: '#infra-alerts'

  - name: 'dingtalk-dev'
    webhook_configs:
      - url: 'http://dingtalk-webhook:8060/dingtalk/dev/send'

二、路由树详解

# 路由树的匹配逻辑:
# 从根 route 开始,遍历子路由
# 找到第一个匹配的子路由就停止(默认)
# continue: true 可以继续匹配后续路由(允许一条告警发给多个接收者)

route:
  receiver: default
  routes:
    # 精确匹配:job 标签
    - matchers:
        - job="mysql"
      receiver: dba-team
      
    # 正则匹配
    - matchers:
        - alertname=~"KubernetesNode.*"   # 正则:以 KubernetesNode 开头
      receiver: k8s-team
      
    # 多条件匹配(AND 关系)
    - matchers:
        - namespace="production"
        - severity="critical"
      receiver: prod-oncall
      continue: true    # 继续匹配,还会发给后续匹配的接收者
      
    - matchers:
        - severity="critical"
      receiver: pagerduty   # critical 的也发 PagerDuty

三、分组配置最佳实践

# 分组的作用:把相同类型的告警合并成一条通知
# 避免:一台服务器 10 个 Pod 都挂了,发 10 条告警

route:
  # 全局分组:按集群和命名空间分组
  group_by: ['cluster', 'namespace']
  group_wait: 30s          # 等 30 秒收集同组的所有告警再一起发
  group_interval: 5m       # 同组有新告警后,5 分钟内不重复发(合并通知)
  repeat_interval: 4h      # 持续告警 4 小时重发一次

  routes:
    # 数据库告警:按实例分组(每个 DB 实例一条告警)
    - matchers:
        - job=~"mysql|postgres|redis"
      group_by: ['instance', 'alertname']
      group_wait: 10s
      
    # Kubernetes Node 告警:按节点分组
    - matchers:
        - alertname=~"KubernetesNode.*"
      group_by: ['node']
      
    # 网络告警:按机房/区域分组
    - matchers:
        - team="network"
      group_by: ['datacenter', 'alertname']

四、静默规则(Silence)

维护窗口期间,通过静默避免误告警。

# 方法1:Web UI
# http://alertmanager:9093 → Silences → New Silence
# 填写:
#   Matchers: job="mysql-prod", instance="db-01:9104"
#   Start: 2026-07-22 02:00:00
#   End:   2026-07-22 04:00:00
#   Comment: 数据库维护窗口

# 方法2:amtool 命令行
amtool --alertmanager.url=http://alertmanager:9093 \
  silence add \
  --start "2026-07-22T02:00:00+08:00" \
  --end "2026-07-22T04:00:00+08:00" \
  --comment "数据库维护" \
  'job="mysql-prod"' 'instance="db-01:9104"'

# 查看所有静默
amtool silence list

# 取消静默
amtool silence expire <silence-id>

五、钉钉/飞书告警集成

Alertmanager 通过 webhook 接入国内通讯工具。

使用 prometheus-webhook-dingtalk

# docker-compose.yml(或 Kubernetes Deployment)
version: '3'
services:
  dingtalk-webhook:
    image: timonwong/prometheus-webhook-dingtalk:v2.1.0
    ports:
      - "8060:8060"
    volumes:
      - ./dingtalk.yml:/etc/prometheus-webhook-dingtalk/config.yml

  alertmanager:
    image: prom/alertmanager:v0.28.0
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
# dingtalk.yml(webhook 服务配置)
targets:
  dev:
    url: https://oapi.dingtalk.com/robot/send?access_token=xxxxx
    secret: SECxxxxxxx    # 加签密钥(钉钉机器人设置里获取)
    
  ops:
    url: https://oapi.dingtalk.com/robot/send?access_token=yyyyy
    secret: SECyyyyyyy
    mention:
      all: false           # 不 @全员
      mobiles: ["138xxxxxxxx", "139xxxxxxxx"]  # @特定人
# alertmanager.yml 中配置钉钉接收者
receivers:
  - name: dingtalk-ops
    webhook_configs:
      - url: 'http://dingtalk-webhook:8060/dingtalk/ops/send'
        send_resolved: true   # 恢复时也发通知

  - name: dingtalk-dev
    webhook_configs:
      - url: 'http://dingtalk-webhook:8060/dingtalk/dev/send'
        send_resolved: true

自定义钉钉消息模板

# 在 dingtalk.yml 中自定义消息格式
targets:
  ops:
    url: https://oapi.dingtalk.com/robot/send?access_token=xxx
    message:
      title: '{{ template "ding.link.title" . }}'
      text: '{{ template "ding.link.content" . }}'
# templates/dingtalk.tmpl
{{ define "ding.link.title" }}
{{ if eq .Status "firing" }}🔥 [告警] {{ else }}✅ [恢复] {{ end }}
{{ .GroupLabels.alertname }}
{{ end }}

{{ define "ding.link.content" }}
**状态:** {{ if eq .Status "firing" }}🔴 告警中{{ else }}🟢 已恢复{{ end }}

{{ range .Alerts }}
**告警:** {{ .Annotations.summary }}
**描述:** {{ .Annotations.description }}
**集群:** {{ .Labels.cluster }}
**命名空间:** {{ .Labels.namespace }}
**开始时间:** {{ .StartsAt.Format "2006-01-02 15:04:05" }}
{{ if ne .EndsAt.String "0001-01-01 00:00:00 +0000 UTC" }}
**恢复时间:** {{ .EndsAt.Format "2006-01-02 15:04:05" }}
{{ end }}
---
{{ end }}
{{ end }}

六、告警升级策略

# 场景:critical 告警 30 分钟未处理,升级到电话通知(PagerDuty)

route:
  routes:
    - matchers:
        - severity="critical"
      receiver: team-slack       # 第一时间发 Slack
      continue: true             # 继续执行下面的路由

    - matchers:
        - severity="critical"
      receiver: pagerduty
      group_wait: 30m            # 等 30 分钟(如果 30 分钟后还在 firing,PagerDuty 通知)
      repeat_interval: 30m

# 另一种方案:通过 Alertmanager 的 repeat_interval 实现
# 第一次:Slack(立即)
# 30 分钟后告警还在:PagerDuty(升级)
receivers:
  - name: 'escalation-receiver'
    slack_configs:
      - channel: '#oncall'
        send_resolved: true
    pagerduty_configs:
      - integration_key: 'xxx'
        description: '{{ .GroupLabels.alertname }} - 已持续超过 30 分钟'

七、验证与调试

# 检查 Alertmanager 配置语法
amtool check-config /etc/alertmanager/alertmanager.yml

# 测试路由(告警会路由到哪个 receiver)
amtool --alertmanager.url=http://alertmanager:9093 \
  config routes test \
  severity="critical" cluster="prod" namespace="production"
# Output: prod-oncall   ← 会路由到这个接收者

# 查看当前 firing 的告警
amtool --alertmanager.url=http://alertmanager:9093 alert query

# 测试发送告警(调试 webhook 通道)
curl -XPOST http://alertmanager:9093/api/v1/alerts \
  -H 'Content-Type: application/json' \
  -d '[{
    "labels": {
      "alertname": "TestAlert",
      "severity": "critical",
      "cluster": "test"
    },
    "annotations": {
      "summary": "测试告警",
      "description": "这是一条测试告警"
    }
  }]'

# 查看 Alertmanager 日志
kubectl logs -n monitoring alertmanager-0 -c alertmanager --tail=50

小结

Alertmanager 的核心是路由树 + 分组 + 静默 + 抑制。路由树决定告警发给谁;分组合并同类告警(group_wait 等待聚合,group_interval 控制通知频率);静默在维护窗口屏蔽告警;抑制规则让 critical 发生时自动屏蔽 warning(减少噪音)。生产中钉钉/飞书集成推荐使用 prometheus-webhook-dingtalk 中间件,配合自定义模板让通知更可读。amtool config routes test 是验证路由配置的必备工具,可以在修改配置前验证路由逻辑是否符合预期。