告警规则 243 砍到 27:Prometheus 降噪实战,日均打扰 47 次 → 3 次

【掘金摘要】3.18 那次 47 分钟没人发现的故障之后,我把 243 条 Prometheus 告警规则砍到 27 条,日均告警从 47 次降到 3 次,MTTD 从 47 分钟压到 4 分钟,本文是完整的 5 步操作记录,脚本和配置可直接抄。


先交代背景。3 月 18 号晚上我们出过一次故障:下单接口成功率从 99.94% 掉到 91.2%,跑了 47 分钟才被发现------不是告警发现的,是客服在群里喊人。事后复盘发现系统里躺着 243 条告警规则,真正会响的 11 条,故障当晚响的 3 条全是噪音(磁盘路过 78%、证书 45 天后到期、一个 pod 重启又自己好了)。

值班同学不是不负责。是你天天给他推 40 多条垃圾短信,他迟早学会"先看一眼不用管"。等真那一条混进来的时候,待遇跟前面 240 条一样。

这篇文章不复盘观点,只讲我之后花两周做的降噪实操。目标三个:日均告警降到个位数;补上"成功率跌破红线必须响"这类 SLI 告警;建立防复发的清理机制。环境是 Prometheus 2.45.0 + Alertmanager 0.26.0,规则都在 Git 仓库里管着。

第一步:先数数,别拍脑袋

动手砍之前,我拉了一周的数据,回答一个问题:哪条规则响过几次?

Prometheus 的 /api/v1/rules 只给当前状态,历史触发记录要从 Alertmanager 的 API 拿。写了个脚本按 alertname 聚合:

python 复制代码
# count_alerts.py  统计近7天每条规则的触发次数
import requests, collections

AM = "http://alertmanager:9093"
end = int(time.time()); start = end - 7 * 86400

resp = requests.get(f"{AM}/api/v2/alerts",
                    params={"active": "false", "silenced": "false"})
counts = collections.Counter()
for a in resp.json():
    ts = a["updatedAt"]  # 仅作近似,见下文注意事项
    counts[a["labels"].get("alertname", "unknown")] += 1

for name, c in counts.most_common():
    print(f"{c:5d}  {name}")

如果你的 Alertmanager 没开持久化(默认就没开),历史数据拿不全,那就用兜底方案:Grafana 里建个面板,count_over_time(ALERTS{alertstate="firing"}[7d]) 按 alertname 聚合,直接看一周趋势。ALERTS 这个内置指标每个触发实例都会记一条,够用了。

数完结果很难看:243 条规则里,一周触发 0 次的有 178 条,一周触发超过 20 次的有 9 条------这 9 条贡献了全部告警量的 76%。中间地带只有十几条。也就是说,这套体系里绝大部分规则是死字,少数规则在疯狂刷存在感。

第二步:三档分拣,砍掉两头

拿到数据后我把 243 条规则分成三档,逐条过:

杀掉(178 条):一周零触发,且阈值明显脱离现实。典型的就是那条 CPU 85% 持续 10 分钟------我们那批机器负载从没上过 70,这条规则存在的意义就是让规则数好看。判断标准很简单:问自己"这条响了之后我上一次真的去处理是什么时候"。答不上来就删。

改造(38 条):频繁触发但确实反映真实波动的,别删,改触发条件。两种改法:

yaml 复制代码
# 改法1:拉长观察窗口,毛刺不报,持续才报
- alert: HighCPU
  expr: avg(rate(node_cpu_seconds_total{mode!="idle"}[5m])) by (instance) > 0.85
  for: 15m          # 原来 10m,改成 15m 后误报砍半
  labels: {severity: P2}

# 改法2:跟自身基线比,不跟绝对阈值比
- alert: QPSAbnormalDrop
  expr: |
    sum(rate(http_requests_total[5m]))
      < 0.6 * avg_over_time(sum(rate(http_requests_total[5m]))[1h:5m])
  for: 5m
  labels: {severity: P1}

保留(27 条):杀完改完剩下的。原则是每条留存的规则必须满足:响了有人看、看了有动作、动作有文档。三样缺一样,下次清理继续砍。

这里有个取舍要说清楚:砍规则砍掉的不是"监控覆盖",是"看起来覆盖"。真正缺的那块覆盖(成功率红线)后面第四步会补回来。

第三步:Alertmanager 分级路由,P2 不许进值班群

27 条规则如果全走一个渠道,噪音还是会回来。降噪的另一半在路由层:

yaml 复制代码
route:
  receiver: p2-weekly
  group_by: [alertname, instance]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - match_re:
        severity: "P0"
      receiver: p0-phone       # 电话/短信,凌晨也叫
      group_wait: 0s
      repeat_interval: 30m
    - match_re:
        severity: "P1"
      receiver: p1-oncall      # 值班群
      repeat_interval: 2h
    - match_re:
        severity: "P2"
      receiver: p2-weekly      # 邮箱汇总,每周一发周报
      repeat_interval: 24h

关键动作是把原来混在值班群里的 P2 全部踢出去,改成邮件周报。我们统计过,改造前值班群日均 47 条,里面 41 条是 P2 级别的"知道了也没用"。踢出去之后值班群一天最多三四条,每条都值得点开。

repeat_interval 也很关键。原来有个磁盘告警每 2 小时重复一次,一晚上同一条响 4 次。改成 4h 之后,同一问题一天最多打扰两次,配合 group_by 合并同源告警。

第四步:补回真正该响的那条

前面都在做减法,这步是加法,也是最不能省的一步------3.18 那晚缺的就是它:下单成功率跌破 91.2%,没有任何一条规则响。

别上来就抄 Google 的 multi-burn-rate 全家桶,告警规则越复杂越容易变成新的噪音源。我们先上简化版:

yaml 复制代码
- alert: OrderSuccessRateLow
  expr: |
    sum(rate(order_total{status="success"}[5m]))
      / sum(rate(order_total[5m])) < 0.995
  for: 3m
  labels: {severity: P0}

- alert: HighErrorRate5xx
  expr: |
    sum(rate(http_requests_total{status=~"5.."}[5m]))
      / sum(rate(http_requests_total[5m])) > 0.01
  for: 3m
  labels: {severity: P0}

这两条上线后立刻加了一轮回归验证:找测试环境人为制造 5xx,确认电话打得进来、值班群 @ 得到人。告警链路本身也要测试,这个共识普及度比想象中低。

SLI 类规则控制在个位数,只放下单、支付、登录这些核心接口,再加就该克制了。

第五步:月度清理脚本,防止长回来

降噪最大的敌人是时间------半年后规则又会一点点长回来,就像没人记得 3.18 一样。所以最后一步是机制:每月 1 号跑一次清理脚本,输出"触发次数 top20 + 零触发规则清单",进月度例会必看。

bash 复制代码
#!/bin/bash
# alert_report.sh 每月输出告警健康度
PROM="http://prometheus:9090"
echo "== 近30天各规则触发次数 =="
curl -s "$PROM/api/v1/query" \
  --data-urlencode \
  'query=topk(20, sum(count_over_time(ALERTS{alertstate="firing"}[30d])) by (alertname))' \
  | jq -r '.data.result[] | "\(.value[1])  \(.metric.alertname)"'
echo "== 零触发规则数(对比在册规则总数)=="
curl -s "$PROM/api/v1/rules" | jq '[.data.groups[].rules[]] | length'

挂个 crontab:0 9 1 * * /opt/scripts/alert_report.sh | mail -s "告警月报" devops@team.com。机制不用重,存在就行------有个数字每个月被看一眼,规则就烂不回去。

效果对比

两周改完,跑了 60 天后的数据:

指标 改造前 改造后
告警规则总数 243 条 27 条
日均告警推送 47 次 3 次
3.18 类故障发现方式 客服喊人(47 分钟) P0 电话(4 分钟)
值班群信噪比 ~12%(3/47) ~100%

补充一个意外收获:规则少了之后,新人值班的上手时间明显缩短------原来看不懂 243 条规则各管什么,现在 27 条一下午能讲完。

注意事项

  • 砍规则前先归档再删 。我们把 216 条下线规则挪到 rules/archive/ 目录留在 Git 里,谁哪天想恢复有据可查,别直接 rm。
  • Alertmanager 默认不持久化触发历史 ,靠 ALERTS 指标统计要注意:规则被删后历史数据也就没了,所以月报脚本要趁规则还在的时候跑。
  • P0 电话渠道要单独演练。我们第一次真实触发时发现电话网关的配额只够 10 通/月,还好是在演练里发现的。
  • 别追求一次到位。multi-burn-rate、分级 SLO 那套可以后置,先把死规则清掉、路由分好层,收益已经能拿走八成。

改动清单都在仓库里,243 条删 216 条这种事,评审记录就是最好的背锅侠------不对,是知识资产。

你们现在的告警规则有多少条?上次真响是什么时候?评论区聊聊,看看谁的信噪比更惨。

觉得有用点个关注,每周五更新一篇 DevOps 实战,全是踩过坑换来的。

相关推荐
运维老郭2 小时前
别再让 Liveness Probe 背锅了:initialDelaySeconds 和 failureThreshold 的坑,一次讲透
云原生
容器魔方2 小时前
基于 KubeEdge 为云边协同 AI 流数据分析提供基础设施
大数据·云原生·容器·开源·边缘计算
weixin_435247064 小时前
微服务开发规范模版
微服务·云原生
智能运维指南11 小时前
代码管理软件如何融入 DevOps 工具链?全链路联通的 5 个关键点
运维·devops·信创适配·嘉为蓝鲸
weixin_4352470611 小时前
DevOps成熟度评估与改进方案模版
devops
rustfs13 小时前
MinIO 国产开源平替正式 GA
分布式·docker·云原生·rust
阿里云云原生17 小时前
云原生可观测性进阶:利用 MCP ToolSets 实现 Agent 在复杂排障场景中的安全与高效协作
云原生
运维老郭19 小时前
别再被 accept 骗了:TCP 连接到底开不开新端口?一次讲透
云原生
weixin_4202841420 小时前
Kubernetes 开发自定义CRD资源
云原生·kubernetes·kubelet