生产环境 Prometheus 告警从一天 500 条到只剩 30 条,我做了这些事
上个月我负责的业务线出了一次不大不小的事故,事后复盘发现一个很尴尬的事情:告警其实提前 20 分钟就触发了,但当时值班的同事压根没看到。为什么?因为那天光钉钉群里的告警就有 400 多条,绝大部分是无意义的抖动和重复,大家早就免疫了。
这事之后我花了两周把告警体系重新治理了一遍,从每天 500 条干到了 30 条左右。这 30 条里没有一条是废的。整个过程记录一下。
动手之前先做了个统计。Alertmanager 有 API,直接用脚本把过去两周的告警 dump 下来,Python 做个分类。结果挺触目惊心的:
- CPU/内存瞬时抖动触发的:45%
- 同一个故障不同实例重复报的:25%
- 刚恢复又触发来回抖的:15%
- 真正需要人处理的:15%
85% 是噪音。这还算好的,我见过有些团队 95% 以上都是噪音。
问题集中在几个地方。一个一个说。
最容易改也最有效的就是 for 持续时间。很多人写告警规则 for 要么不写,要么就 1m,结果任何毛刺都会触发。
原来的 CPU 告警长这样:
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[2m])) * 100) > 80
for: 1m
labels:
severity: warning
annotations:
summary: "CPU使用率超过80%"问题是 for: 1m 太短了。线上服务流量高峰 CPU 蹦到 85% 很正常,过几十秒就下来了,这种报警没有意义。
改成:
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} CPU持续5分钟超过80%"rate 窗口从 2m 拉到 5m,计算结果更平滑。for 从 1m 改 5m,得持续超才报。就这一个改动,CPU 相关告警直接少了 70%。
不过不是所有告警都能加长 for。磁盘空间这种变化慢的,for 可以长;5xx 错误率飙升的,for 就不能太长,不然等你收到告警用户早骂了半天了。
我大概是这么分的:资源类(CPU/内存/磁盘)5-10m,网络类 3-5m,业务类(5xx/SLA)1-2m,基础设施宕机 30s-1m。
然后是 group_by。解决"同一个问题报 N 遍"的问题。
Redis 集群挂了,依赖它的 50 个服务实例全报"连接超时"。不做合并就是 50 条基本一样的消息刷屏。
route:
receiver: 'default'
group_by: ['alertname', 'cluster', 'service']
group_wait: 30s
group_interval: 5m
repeat_interval: 4hgroup_wait: 30s 是收到第一条告警后等 30 秒,把这段时间内同组的合一起发。设太短合并效果不好,设太长收到告警就晚了。
repeat_interval 我建议别设太短。之前有同事设了 30 分钟,一个没人处理的告警一天发了 48 次。改成 4h,critical 的 1h,既提醒又不烦。
接下来是抑制(Inhibition),这个功能很多人不知道,但真的好用。
场景:宿主机挂了,上面 20 个容器全触发"容器不可达"。但根因是宿主机,容器不可达只是衍生问题,不需要单独报。
inhibit_rules:
- source_match:
severity: 'critical'
alertname: 'NodeDown'
target_match:
severity: 'warning'
equal: ['instance']当 NodeDown 这个 critical 告警存在时,同 instance 的 warning 全部抑制。
还加了一条:整个集群网络不通时,抑制掉集群内所有服务的健康检查失败:
inhibit_rules:
- source_match:
alertname: 'ClusterNetworkPartition'
target_match_re:
alertname: 'ServiceHealthCheckFailed|PodCrashLooping|EndpointDown'
equal: ['cluster']遇到大面积故障的时候告警从上百条压缩到个位数,值班人员可以迅速看到根因在哪。这个在去年双十一前的压测里帮了大忙,模拟了一次网络分区的情况,如果没做抑制,光告警就够喝一壶的。
告警分级这个事说起来简单,做好了效果很明显。我们分三级:
- critical:服务完全不可用或数据丢失风险。打电话 + 短信 + 钉钉
- warning:有问题但服务还在跑。钉钉 + 邮件
- info:仅供参考。只发邮件摘要
路由配置:
route:
receiver: 'default-email'
group_by: ['alertname', 'service']
group_wait: 30s
routes:
- match:
severity: critical
receiver: 'pager-duty'
repeat_interval: 1h
continue: false
- match:
severity: warning
receiver: 'dingtalk-oncall'
repeat_interval: 4h
continue: false
- match:
severity: info
receiver: 'email-digest'
group_wait: 10m
repeat_interval: 12h还有时间段策略。非工作时间只有 critical 打电话,warning 攒到第二天早上:
time_intervals:
- name: 'offhours'
time_intervals:
- weekdays: ['saturday', 'sunday']
- times:
- start_time: '00:00'
end_time: '09:00'
- times:
- start_time: '18:00'
end_time: '24:00'warning 在 offhours 期间静默,critical 不受影响。周末终于能安心睡觉了,之前三天两头半夜被 warning 吵醒真的崩溃。
Grafana Alerting 我们也用了,主要做两类事情。
一是多条件联合判断,比如"CPU 超过 80% 且 QPS 下降 30%"。在 PromQL 里写起来很别扭,Grafana 里可以用 Multi-dimensional alert 很方便地做。
二是基于 Loki 日志的告警。检测到"OOM Killed"、"panic"、"fatal"这些关键词就报警。这种用 Prometheus 本身没法做,得靠 Grafana 查 Loki 数据源来实现。
有个坑:Grafana Alerting 和 Alertmanager 的告警别混用同一个通知渠道。我们的做法是在钉钉消息模板里标注 [Grafana] 还是 [Prometheus] 来源,不然接到告警都不知道去哪里看。
最后一个不是技术活但很重要的事:定期 Review。
告警规则不是写完就不管了。业务在变架构在变,之前合理的阈值过段时间可能就不合理了。我们每两周做一次,半小时搞定,主要看:
- 触发频率最高的 Top 10,逐一分析是否需要调阈值或者删掉
- 触发后没人响应的告警——说明大家觉得不重要,那就降级或删
- 新上线的服务有没有漏加告警
- 误报率
坚持做两三个月之后你会发现告警质量越来越高。
还有个实用的做法:告警 annotation 里加 runbook_url。如果一条告警连处理手册都没有,那它存在的必要性就值得质疑。要么补手册,要么删告警。
annotations:
summary: "磁盘使用率超过85%"
runbook_url: "https://wiki.internal/runbooks/disk-usage-high"
description: "实例 {{ $labels.instance }} 磁盘 {{ $labels.mountpoint }} 使用率 {{ $value }}%"对了,消息模板也值得花点时间搞。区别有多大你感受一下:
差的:HighCPUUsage - FIRING
好的:
🔥 [CRITICAL] 生产环境-订单服务
CPU持续10分钟超过90%(当前值:93.5%)
实例:10.0.3.47:9100
影响范围:订单创建 P99 延迟从 200ms 升至 1.2s
处理手册:https://wiki.internal/runbooks/order-svc-cpu-high
一眼就能知道出了什么问题、在哪里、严重程度、怎么处理。别小看这个细节,凌晨两点被叫醒看到第一种消息和第二种消息,心理状态完全不一样。
记得设 send_resolved: true,告警恢复了也通知一下,省得值班人还以为问题在持续。
最后说一下效果。优化前日均 500 条告警,噪音率 85%,平均响应时间 15 分钟以上,MTTR 大概 45 分钟。优化后日均 30 条,噪音率低于 5%,响应时间 3 分钟,MTTR 缩短到 20 分钟。
最直观的变化是现在群里一有告警大家会真的去看,因为知道既然报了就是有问题。之前那种一天到晚响个不停、所有人都当没看见的状态没了。
说白了核心就一件事:把噪音干掉,让每一条告警都值得被认真对待。技术上没什么高深的,都是 Prometheus 和 Alertmanager 本身的能力,关键是得有意识去做这个事情。监控系统搭完不是终点,告警治理才是真正影响你能不能睡好觉的东西。
觉得有用的话转发给你的同事看看,这些都是线上踩出来的坑,能帮一个是一个。
公众号:耕云躬行录
个人博客:躬行笔记