告警体系搭建:AlertManager + 钉钉/企微通知 + 告警降噪——拒绝告警疲劳

引言

凌晨 3 点,值班手机第 137 次震动。迷迷糊糊划开一看,还是那条「磁盘使用率超过 80%」——同一块盘,Prometheus 每 30 秒评估一次,没人处理就一直报。

第二天早上复盘,最讽刺的一幕来了:真正的大故障恰恰发生在凌晨 4 点,一条 P0「订单服务全部实例下线」,被 137 条 P2 的重复轰炸彻底淹没。值班同学后来承认:"我把告警群设成免打扰了,反正是磁盘告警。"

这不是个例。我们统计了自己环境的 30 天告警数据,结果触目惊心:

指标数值
日均告警条数24,187 条
单人单夜被通知次数平均 9.2 次
有效告警占比(需要人介入的)2.7%
P0 告警平均响应时间11 分钟(且漏报 2 次/月)

97% 的告警是噪音。而噪音的代价不是"烦",是把真正致命的告警稀释掉——这就是告警疲劳(Alert Fatigue)。

这篇文章把我们从 2.4 万条/天优化到 362 条/天的完整方案讲一遍,覆盖四块内容:

  • 告警分级设计:P0(服务挂了)→ P1(P99 > 1s)→ P2(磁盘 > 80%),不同级别不同响应策略
  • AlertManager 三大利器:分组(group_by)、抑制(inhibition)、静默(silence)
  • 钉钉/企微机器人接入:含 P0 电话强通知的实现
  • 降噪三板斧:相同告警 5 分钟只发一次、凌晨非 P0 延迟到早上、告警聚合

所有配置都可直接抄走,文末有完整的 alertmanager.yml 全景和降噪前后对比数据。


一、告警体系全景:先看图,再动手

1.1 整体架构

┌──────────┐    ┌──────────────┐    ┌─────────────────┐    ┌──────────────┐
│ Prometheus │───→│  Alertmanager │───→│  通知中间件层     │───→│  值班同学      │
│  告警规则   │    │  分组/抑制/静默 │    │  dingtalk-wehook │    │  P0: 电话+短信 │
│  (rules.yml)│   │  路由/时间窗口  │    │  wecom-webhook   │    │  P1: 群@值班   │
└──────────┘    └──────────────┘    └─────────────────┘    │  P2: 工作时间  │
                                                              └──────────────┘
       ↑                                              │
  ┌────┴─────┐                              ┌─────────┴────────┐
  │ 自监控     │                              │ 升级 Worker(可选) │
  │ DeadMans  │                              │ 15min 未 ack      │
  │ Switch    │                              │ → 自动电话升级      │
  └──────────┘                              └──────────────────┘

一句话概括每个组件的职责:

  • Prometheus:执行告警规则(expr + for),产生告警事件,推给 Alertmanager
  • Alertmanager:去重、分组、抑制、静默、路由,决定"要不要发、发给谁、什么时候发"
  • 通知中间件:prometheus-webhook-dingtalk / 企微机器人,负责"发出去"
  • 升级 Worker(可选):自研的小程序,处理"P0 15 分钟没响应自动打电话给二线"

1.2 好告警的三条标准

动手配规则之前,先统一认知。一条"好告警"必须同时满足:

  1. 可行动(Actionable):收到的人知道下一步干什么。做不到就删掉这条规则
  2. 有分级:凌晨 3 点值不值得把人叫醒,由 severity 决定,而不是由告警数量决定
  3. 去重与收敛:同一故障只让人看到一次,而不是每 30 秒轰炸一次

我们后来把所有告警规则按这三条过了一遍,第一轮就删掉了 61 条"看起来有用、实际没人处理"的规则。


二、告警分级设计:P0 / P1 / P2

2.1 分级标准

级别定义典型场景通知方式响应 SLA
P0服务已不可用,用户直接受损服务全部实例下线、数据库主库宕机、支付成功率归零电话 + 短信 + 钉钉强提醒5 分钟响应
P1核心功能劣化,尚可用但危险P99 > 1s、错误率 > 1%、连接池使用率 > 90%钉钉群 @值班人30 分钟响应
P2隐患类,暂不影响用户磁盘 > 80%、证书 30 天内过期、副本数不足钉钉群,工作时间发送当天处理

分级的本质是回答一个问题:"这条告警触发时,我愿不愿意被电话叫醒?" 愿意 → P0;叫醒但不用电话 → P1;睡醒再说 → P2。

2.2 Prometheus 告警规则(可直接抄)

P0:服务挂了

groups:
  - name: p0-critical
    rules:
      # 实例下线:连续 1 分钟无心跳
      - alert: ServiceInstanceDown
        expr: up == 0
        for: 1m
        labels:
          severity: P0
          team: sre
        annotations:
          summary: "实例下线:{{ $labels.job }} / {{ $labels.instance }}"
          description: "{{ $labels.instance }} 已 1 分钟无心跳,请立即确认流量是否被正常摘除"
          runbook_url: "https://wiki.example.com/runbook/service-down"

      # 整体可用性:5xx 错误率 > 50%,视为事实性故障
      - alert: HighErrorRateCritical
        expr: |
          sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) by (job)
            / sum(rate(http_server_requests_seconds_count[5m])) by (job) > 0.5
        for: 2m
        labels:
          severity: P0
        annotations:
          summary: "5xx 错误率超过 50%:{{ $labels.job }}"

两个设计细节:

  • P0 不加 for: 30s 以下的等待。瞬时网络抖动就会把值班电话打爆,for: 1m 已经足够快(发现 1 分钟 + 通知 10 秒,远快于人睡醒的时间)
  • annotations 里写清楚下一步动作。凌晨 3 点被电话叫醒的人没有耐心看仪表盘,description 要直接告诉他"先看流量摘除"

P1:性能劣化

- name: p1-degraded
    rules:
      # P99 延迟 > 1s(订单/支付核心链路)
      - alert: HttpP99LatencyHigh
        expr: |
          histogram_quantile(0.99,
            sum(rate(http_server_requests_seconds_bucket{job=~"order|payment"}[5m]))
              by (le, job)
          ) > 1
        for: 5m
        labels:
          severity: P1
        annotations:
          summary: "P99 延迟超标:{{ $labels.job }}"
          description: "近 5 分钟 P99 = {{ $value | humanizeDuration }},阈值 1s,持续 5 分钟"

      # JVM 堆内存 > 85%(Full GC 前兆)
      - alert: JvmHeapUsageHigh
        expr: |
          jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} > 0.85
        for: 10m
        labels:
          severity: P1

细节:P1 用 for: 5m + rate(...[5m]) 双重平滑,把"瞬时抖动"挡在外面——这正是降噪的第一道防线,比 Alertmanager 层面的降噪更早生效。

P2:隐患类

- name: p2-hygiene
    rules:
      # 磁盘 > 80%
      - alert: DiskUsageHigh
        expr: |
          (1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay|nfs"}
             / node_filesystem_size_bytes{fstype!~"tmpfs|overlay|nfs"}) * 100 > 80
        for: 10m
        labels:
          severity: P2
        annotations:
          summary: "磁盘使用率 {{ $value | printf \"%.1f\" }}%"
          description: "{{ $labels.instance }} 挂载点 {{ $labels.mountpoint }} 超过 80%"

      # 证书 30 天内过期
      - alert: TlsCertExpiringSoon
        expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 30
        for: 1h
        labels:
          severity: P2

注意 P2 特意把 for 拉长(10m ~ 1h)——磁盘从 79% 涨到 81% 再跌回去是常态,不值得发。

2.3 分级常见误区

误区后果正确做法
所有告警都是 P0电话打爆,P0 失去意义严格执行"愿不愿意被叫醒"测试
用 alertname 表达级别(规则名带 Critical)没法按级别路由用 labels.severity 字段
磁盘告警写 P0凌晨被一块慢盘叫醒磁盘 80% 是 P2,95% 可以升 P1
没配 runbook_url收到告警不知道干嘛每条 P0/P1 必须带处理手册链接

三、AlertManager 核心配置:分组、抑制、静默

这是降噪的主战场。Alertmanager 收到告警后做四件事:去重 → 分组 → 抑制 → 静默/路由

3.1 分组(group_by):把相关告警捆成一封通知

route:
  receiver: dingtalk-p2
  group_by: ['alertname', 'cluster']   # 同名告警按集群聚合
  group_wait: 30s        # 首条告警先攒 30s,凑一批一起发
  group_interval: 5m     # 同组新告警,距上次通知至少 5m 才再发
  repeat_interval: 5h    # 同组告警未恢复,5h 重发一次提醒

三个时间参数最容易混淆,单独说清楚:

参数含义我们取值效果
group_wait第一条告警到达后,先等多久再发第一条通知30s攒批:30 秒内到达的同组告警合并成一条
group_interval组内来了新告警,距上一次通知的最小间隔5m5 分钟内的增量合并
repeat_interval未恢复的告警,重复通知的周期5h(P2)/ 30m(P1)同一故障不会无限轰炸

"5 个服务同时报 CPU 高"合并成一条,靠的就是 group_by 不带 instance

routes:
  - matchers: [ 'alertname="NodeCpuHigh"' ]
    receiver: dingtalk-p2
    group_by: ['alertname']        # ← 注意:不按 instance 分组

配合消息模板列出所有实例(见 4.4 节模板),最终钉钉里只收到一条:

【FIRING】NodeCpuHigh @ P2  (共 5 个实例)
- node-01 (92%)
- node-02 (88%)
- node-03 (91%)
- node-07 (87%)
- node-11 (90%)

代价与权衡:分组粒度越粗,单条通知信息越密集、总量越少,但"哪台机器"的细节被折叠了。我们的原则:P0 粒度细(每次都单独发),P1/P2 粒度粗(聚合为主)。

3.2 抑制(inhibition):A 故障发生时,B/C/D 都是它的衍生噪音

最经典的场景:一台机器宕机(P0),它上面的磁盘、进程、连接数告警全炸了——根因只有宕机这一个

inhibit_rules:
  # 规则 1:实例 P0 下线时,抑制该实例的一切 P1/P2
  - source_matchers: [ 'severity="P0"' ]
    target_matchers: [ 'severity=~"P1|P2"' ]
    equal: ['instance']            # 只有同 instance 才抑制

  # 规则 2:MySQL 主库宕机,抑制同实例的慢查询/连接数告警
  - source_matchers: [ 'alertname="MySQLDown"' ]
    target_matchers: [ 'alertname=~"MySQLSlowQuery|MySQLTooManyConnections"' ]
    equal: ['instance']

equal: ['instance'] 是灵魂:只有同一台实例的 P1/P2 被抑制,其他机器不受影响。

一个必须知道的细节:抑制不会阻止告警进入 Alertmanager,只是不让它发通知。等 P0 恢复后,如果 P1 还在 firing,通知会正常发出。这个语义正好符合直觉:"根因修好了,但衍生问题还在"。

3.3 静默(silence):发布窗口和计划内维护

发布期间机器重启、指标抖动是必然的,靠"忍"不行,用静默:

# 方式一:命令行 amtool(适合脚本化,发布流水线里自动创建)
amtool --alertmanager.url=http://alertmanager:9093 silence add \
  alertname=~"ServiceInstanceDown|JvmHeapUsageHigh" \
  instance=~"order-service-.*" \
  --duration=30m \
  --comment="发布 order-service v1.9.0"

# 方式二:Web UI(http://alertmanager:9093)
# Alerts → Silence → New Silence → 填 matcher + 时长

生产实践:把静默做进 CI/CD。发布流水线第一步自动创建静默,最后一步自动过期,从此"发布引发的告警风暴"归零:

# GitLab CI 示例
deploy:
  script:
    - amtool silence add job=~"$SERVICE.*" --duration=30m --comment="CI deploy $CI_COMMIT_SHA"
    - kubectl apply -f deploy.yaml
  after_script:
    - amtool silence expire $(amtool silence query --comment="CI deploy" -q | head -1)

3.4 时间窗口(time_intervals):凌晨非 P0 延迟到早上

这是降噪三板斧之二,Alertmanager 0.22+ 原生支持:

route:
  routes:
    # P2:只在工作时间激活,其余时间"静音"——告警不会丢,窗口打开时照发
    - matchers: [ 'severity="P2"' ]
      receiver: dingtalk-p2
      active_time_intervals: [ worktime ]

    # P1:夜间降级为低频(每 2h 一条),白天正常
    - matchers: [ 'severity="P1"' ]
      receiver: dingtalk-p1-night
      mute_time_intervals: [ nighttime ]   # 夜间不发这条路由
      continue: false
    - matchers: [ 'severity="P1"' ]
      receiver: dingtalk-p1                # 夜间被上面规则拦截后白天走这条

时间窗口定义:

time_intervals:
  - name: worktime
    time_intervals:
      - times:
          - start_time: '09:00'
            end_time: '19:00'
        weekdays: ['monday:friday']
        location: 'Asia/Shanghai'    # 别漏了时区,默认 UTC!

两个关键语义

  1. active_time_intervals:路由只在窗口内生效。凌晨 3 点触发的 P2 不会立刻发,早上 9 点窗口打开时,如果还在 firing 就发出——这就是"延迟到早上"的官方实现,告警不丢失
  2. location: Asia/Shanghai 不写的话默认 UTC,"9:00-19:00" 会变成北京时间 17:00-次日 3:00——我们已经替你踩过这个坑

四、钉钉/企微机器人接入

4.1 钉钉机器人:创建与安全设置

  1. 钉钉群 → 群设置 → 智能群助手 → 添加机器人 → 自定义(通过 Webhook 接入)
  2. 安全设置三选一,生产推荐"加签"
安全方式说明适用
自定义关键词消息必须含关键词,如"告警"简单,但所有告警都要带关键词
加签(HMAC)时间戳 + secret 签名,最安全生产推荐
IP 白名单限定来源 IP网络可控的内网

拿到 Webhook URL 和 secret 后,部署 prometheus-webhook-dingtalk(Alertmanager 官方生态最常用的钉钉适配器):

# dingtalk-config.yml
targets:
  p0:
    url: https://oapi.dingtalk.com/robot/send?access_token=8f5xxxx
    secret: SEC8a1b2c3dxxxx          # 加签密钥
    mention:
      mobiles: ['13800001111']       # P0 顺带 @值班人手机号
    message:
      title: '{{ template "dd.title" . }}'
      text:  '{{ template "dd.text" . }}'
  p1:
    url: https://oapi.dingtalk.com/robot/send?access_token=2a6xxxx
    secret: SEC9f2e3d4cxxxx
  p2:
    url: https://oapi.dingtalk.com/robot/send?access_token=7c1xxxx

Alertmanager 侧的路由对接:

receivers:
  - name: dingtalk-p0
    webhook_configs:
      - url: 'http://dingtalk-webhook:8060/dingtalk/p0/send'
        send_resolved: true          # 恢复时也通知,不然告警没闭环
  - name: dingtalk-p1
    webhook_configs:
      - url: 'http://dingtalk-webhook:8060/dingtalk/p1/send'
        send_resolved: true
  - name: dingtalk-p2
    webhook_configs:
      - url: 'http://dingtalk-webhook:8060/dingtalk/p2/send'
        send_resolved: false         # P2 恢复不用打扰人

4.2 消息模板:让一条通知自己会说话

# /etc/prometheus-webhook-dingtalk/templates/dingtalk.tmpl
{{ define "dd.title" }}【{{ .Status | toUpper }}】{{ .CommonLabels.alertname }} @ {{ .CommonLabels.severity }}{{ end }}

{{ define "dd.text" }}
#### {{ template "dd.title" }}

**级别**:{{ .CommonLabels.severity }}
**触发时间**:{{ (.StartsAt.Add 2880000000000).Format "2006-01-02 15:04:05" }}
**告警数**:{{ len .Alerts }} 条

{{ range .Alerts }}
---
- **实例**:{{ .Labels.instance }}
- **详情**:{{ .Annotations.description }}
{{ end }}

{{ with .CommonAnnotations.runbook_url }}[📖 处理手册]({{ . }}){{ end }}
{{ end }}

对应到钉钉里的最终效果:

【FIRING】HttpP99LatencyHigh @ P1

级别:P1
触发时间:2026-08-22 14:32:05
告警数:1 条
────────────────
- 实例:order-service-7f9d (10.2.30.11:8080)
- 详情:近 5 分钟 P99 = 1.8s,阈值 1s,持续 5 分钟

[📖 处理手册]

4.3 企业微信机器人:两种姿势

姿势一:群机器人(最简单)。企微群 → 添加群机器人 → 拿到 key,经一个轻量适配器转发(企微机器人要求特定 JSON 格式,Alertmanager 原生 webhook 直接发会报错):

# 用 wechat-webhook 适配器(社区有多个实现),Alertmanager 侧配置同钉钉
receivers:
  - name: wecom-p1
    webhook_configs:
      - url: 'http://wecom-webhook:9094/wecom/send?key=693axxxx-xxxx-xxxx'
        send_resolved: true

姿势二:应用消息(wechat_configs 原生支持)。注册企业微信应用后,Alertmanager 可以原生直连,支持 @指定成员:

global:
  wechat_api_url: https://qyapi.weixin.qq.com/cgi-bin/
  wechat_api_corp_id: 'ww1234567890'
  wechat_api_secret: 'your-app-secret'
  wechat_api_agent_id: '1000002'

receivers:
  - name: wecom-p0
    wechat_configs:
      - to_party: '2'                # 发给部门
        to_user: '@all'
        send_resolved: true
        message_type: markdown

4.4 P0 怎么"打电话"?

钉钉/企微机器人本质是 IM 消息,消息在群里,人在被窝里。P0 必须穿透到手机级别:

方案成本效果
钉钉 DING 消息(机器人发不出,需 API)应用内强提醒
阿里云/腾讯云语音通知 API低(按条计费)真电话,P0 首选
短信网关P0 兜底
PagerDuty / 睡眠网高(按人月订阅)预算充足的团队

我们的做法:一个 200 行的自研 webhook Worker,专门接 P0:

# p0-caller.py(核心逻辑示意)
from flask import Flask, request
import alibabacloud_dysmsapi as sms   # 阿里云语音通知 SDK

app = Flask(__name__)

@app.post('/webhook/p0')
def on_p0():
    alerts = request.json['alerts']
    for a in alerts:
        summary = a['annotations']['summary']
        # 1. 打电话给当班 oncall(号码从值班表 API 获取)
        sms.call_and_say(phone=oncall_api.current_phone(),
                         tts_code='TTS_100001',
                         tts_param={'alert': summary[:50]})
        # 2. 电话未接 3 分钟 → 升级打二线(Worker 内的延迟队列)
        escalation_queue.schedule_delay_call(seconds=180)
    return 'ok', 200

Alertmanager 侧把 P0 路由到这个 Worker(continue: true 同时发钉钉群,一个兜记录一个负责叫醒)。


五、降噪三板斧落地效果

5.1 板斧一:相同告警 5 分钟内只发一次

配置(P1 路由):

- matchers: [ 'severity="P1"' ]
  receiver: dingtalk-p1
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 30m     # P1 未恢复每 30m 提醒一次

语义再强调一遍:repeat_interval: 30m = 同一组未恢复的告警每 30 分钟重发;配合 group_interval: 5m,5 分钟内的同类新告警合并进同一条通知。如果需求是"严格 5 分钟最多一条",把两者都设为 5m 即可。

5.2 板斧二:凌晨非 P0 延迟到早上

3.4 节的 active_time_intervals: [worktime] 就是全部答案。上线当晚的效果:

  • 凌晨 2:47,某台测试机磁盘 81% 触发 P2 → 没有通知任何人
  • 早上 9:00 窗口打开 → 告警状态还是 firing → 自动发出通知
  • 处理人白天正常跟进,一个美梦没被打断

5.3 板斧三:告警聚合("5 个服务同时报 CPU 高"合并成一条)

两步组合拳:

  1. 路由级聚合group_by: ['alertname'](不带 instance),同名告警进同一组
  2. 模板级展开:消息里列出全部实例(4.2 节模板的 {{ range .Alerts }}

实际效果对比:

聚合前(5 条独立通知):
【FIRING】NodeCpuHigh @ P2   实例: node-01 (92%)
【FIRING】NodeCpuHigh @ P2   实例: node-02 (88%)
【FIRING】NodeCpuHigh @ P2   实例: node-03 (91%)
【FIRING】NodeCpuHigh @ P2   实例: node-07 (87%)
【FIRING】NodeCpuHigh @ P2   实例: node-11 (90%)

聚合后(1 条通知):
【FIRING】NodeCpuHigh @ P2(共 5 个实例)
- node-01 (92%)  - node-02 (88%)  - node-03 (91%)
- node-07 (87%)  - node-11 (90%)
👉 怀疑节点级故障或 heat,查看集群热力图:https://grafana/xxx

5.4 降噪前后对比(我们环境 30 天实测)

指标降噪前降噪后变化
日均告警通知条数24,187362-98.5%
单人单夜被打扰次数9.2 次0.4 次-95.7%
有效告警占比2.7%86%×32
P0 平均响应时间11 分钟3.5 分钟-68%
P0 漏报(月)2 次0 次归零

注意最后一行:降噪之后漏报反而归零。原因很朴素——噪音少了,告警群重新变回了"值得看"的群,没人再设免打扰。


六、完整 alertmanager.yml 全景

把前面所有配置拼成一份可直接落地的全景(脱敏版):

global:
  resolve_timeout: 5m

route:
  receiver: dingtalk-p2                 # 默认兜底接收器
  group_by: ['alertname', 'cluster']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 5h

  routes:
    # ---- P0:电话(继续) + 钉钉强提醒,7×24 ----
    - matchers: [ 'severity="P0"' ]
      receiver: p0-phone
      group_by: ['alertname', 'instance']
      group_wait: 10s
      group_interval: 1m
      repeat_interval: 5m
      continue: true
    - matchers: [ 'severity="P0"' ]
      receiver: dingtalk-p0
      group_wait: 10s
      group_interval: 1m
      repeat_interval: 5m

    # ---- P1:钉钉 @值班,夜间降频 ----
    - matchers: [ 'severity="P1"' ]
      receiver: dingtalk-p1
      group_by: ['alertname', 'cluster']
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 30m

    # ---- P2:仅工作时间,聚合为主 ----
    - matchers: [ 'severity="P2"' ]
      receiver: dingtalk-p2
      active_time_intervals: [ worktime ]
      group_by: ['alertname']
      group_wait: 5m
      group_interval: 30m
      repeat_interval: 12h

    # ---- 心跳告警:只进自监控,不发人 ----
    - matchers: [ 'alertname="DeadMansSwitch"' ]
      receiver: heartbeat
      group_wait: 0s
      group_interval: 1m
      repeat_interval: 1m

# 时间窗口:北京时间工作时间
time_intervals:
  - name: worktime
    time_intervals:
      - times:
          - start_time: '09:00'
            end_time: '19:00'
        weekdays: ['monday:friday']
        location: 'Asia/Shanghai'

# 抑制规则
inhibit_rules:
  - source_matchers: [ 'severity="P0"' ]
    target_matchers: [ 'severity=~"P1|P2"' ]
    equal: ['instance']
  - source_matchers: [ 'alertname="MySQLDown"' ]
    target_matchers: [ 'alertname=~"MySQLSlowQuery|MySQLTooManyConnections"' ]
    equal: ['instance']

# 接收器
receivers:
  - name: p0-phone
    webhook_configs:
      - url: 'http://p0-caller:5000/webhook/p0'
        send_resolved: true
  - name: dingtalk-p0
    webhook_configs:
      - url: 'http://dingtalk-webhook:8060/dingtalk/p0/send'
        send_resolved: true
  - name: dingtalk-p1
    webhook_configs:
      - url: 'http://dingtalk-webhook:8060/dingtalk/p1/send'
        send_resolved: true
  - name: dingtalk-p2
    webhook_configs:
      - url: 'http://dingtalk-webhook:8060/dingtalk/p2/send'
        send_resolved: false
  - name: heartbeat
    webhook_configs:
      - url: 'http://heartbeat-recorder:9099/push'
        send_resolved: false

配置写完先验证再上线:

# 语法校验(必做)
amtool check-config alertmanager.yml

# 本地起一个假告警看通知效果
curl -XPOST http://localhost:9093/api/v2/alerts -H 'Content-Type: application/json' -d '[{
  "labels": {"alertname":"NodeCpuHigh","severity":"P2","instance":"node-01","cluster":"prod"},
  "annotations": {"description":"CPU 92%,用于验证聚合效果"}
}]'

七、常见问题

7.1 group_wait / group_interval / repeat_interval 到底啥区别?

参数作用于一句话记忆
group_wait第一条告警先攒 30 秒凑一批发
group_interval组内后续新告警距上次通知至少 5 分钟才补发增量
repeat_interval未恢复的老告警每 30 分钟/5 小时重申一次"还没好"

调参原则:P0 三个值都往小调(10s/1m/5m),P2 三个值都往大调(5m/30m/12h)。

7.2 为什么配了 repeat_interval=5m 还是收到一堆重复告警?

90% 是分组维度太细group_by: ['alertname', 'instance'] 时,同一 alertname 的 10 个 instance 是 10 个独立组,各自有独立的 repeat 计时器。想合并就把 instance 从 group_by 里去掉。

7.3 Alertmanager 挂了告警不就断了?怎么保证自身高可用?

官方方案是多副本 + gossip 协议:

# 三个节点互相感知,通知去重、静默、抑制状态全量同步
alertmanager --cluster.listen-address=0.0.0.0:9094 \
             --cluster.peer=am-1:9094,am-2:9094

任意一个节点收到告警后,集群协商由一个 leader 发通知,天然去重。记得同时给 Prometheus 配多个 Alertmanager 地址

7.4 怎么知道"告警链路自己挂了"?——DeadMansSwitch

最隐蔽的故障是:Prometheus 挂了,一条告警都不会发,你却以为"天下太平"。官方套路是造一条永远在 firing 的心跳告警

- alert: DeadMansSwitch
  expr: vector(1)
  labels:
    severity: none

它永远触发、每分钟推给一个只记录不通知的接收器;再用外部拨测(云监控/另一套 Zabbix)盯着这个心跳是否消失——心跳消失 = 告警链路挂了,比任何业务告警都可靠。

7.5 P0 一直没人处理,能自动升级吗?

Alertmanager 原生不支持升级(escalation),两种落地方式:

  1. 自研 Worker(4.4 节思路):webhook 进来后起延迟任务,N 分钟未 ack 就打下一级别电话。需要在通知里带一个 ack 回调链接
  2. 现成产品:PagerDuty / 睡眠网 / FlashDuty 都支持 escalation policy,webhook 接进去即可

7.6 企微机器人和应用消息该选哪个?

| | 群机器人 | 应用消息(wechat_configs) |
|--|---------|--------------------------|
| 接入成本 | 极低(一个 key) | 中(corp_id + secret + agent_id) |
| 群内可见 | ✅ | ❌(应用会话) |
| 精准 @个人 | 有限 | ✅ to_user 精确到人 |
| 建议 | P1/P2 日常 | P0 精准触达值班人 |

我们两个都配:机器人进大群保记录,应用消息精准找人。


八、总结

降噪三板斧速查卡

┌─────────────┬─────────────────────────────────────────────┐
│ 板斧         │  配置                                        │
├─────────────┼─────────────────────────────────────────────┤
│ 相同告警5min │  group_interval: 5m + repeat_interval: 5m    │
│ 只发一次     │  (同组去重,未恢复按周期重申)                │
├─────────────┼─────────────────────────────────────────────┤
│ 凌晨非P0     │  active_time_intervals: [worktime]           │
│ 延迟到早上   │  + time_intervals 定义 9:00-19:00 工作时间    │
│             │  (告警不丢,窗口打开自动补发)                 │
├─────────────┼─────────────────────────────────────────────┤
│ 告警聚合     │  group_by: ['alertname'] 不带 instance       │
│             │  + 模板 {{ range .Alerts }} 展开实例列表       │
└─────────────┴─────────────────────────────────────────────┘

关键参数推荐值

参数P0P1P2
group_wait10s30s5m
group_interval1m5m30m
repeat_interval5m30m12h
通知渠道电话+短信+钉钉钉钉@值班钉钉(工作时间)
响应 SLA5 分钟30 分钟当天

一句话

告警体系的核心不是"多告",而是"该响的时候一定响,不该响的时候绝对安静"。分级定优先级,Alertmanager 管收敛,机器人管触达,三板斧管降噪——四件事做完,告警从骚扰变回工具。

给团队的建议

阶段建议
刚起步(无告警体系)先把 P0/P1/P2 分级定下来,接入钉钉机器人
已有告警但噪音大先上 group_by + inhibition + repeat_interval,一天见效
噪音已治理加时间窗口 + 消息模板 + 静默进 CI/CD
成熟阶段P0 电话链路 + DeadMansSwitch 自监控 + escalation 升级

互动话题:你们团队的告警群现在是什么状态?有没有被"磁盘告警风暴"轰炸到设免打扰的经历?P0 电话你们是自研还是买的第三方服务?欢迎留言讨论!


参考资料


标题:告警体系搭建:AlertManager + 钉钉/企微通知 + 告警降噪——拒绝告警疲劳
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/08/29/1787966401481.html
公众号:服务端技术精选
    评论
    0 评论
avatar

取消