前言:为什么你的告警总在半夜炸你?
别跟我说你没经历过。凌晨三点,手机疯狂震动,群里@你一百遍。你爬起来一看——CPU 负载高?哦,是批处理任务在跑。内存快满了?哦,是缓存预热。然后你默默把告警阈值调高,回去继续睡。
这他妈的叫告警疲劳。
我踩了这个坑三年,才真正搞明白 Prometheus 告警规则该怎么写。不是抄几个 Awesome Prometheus Alert Rules 里的模板就完事了——那些模板能让你快速跑起来,但要让告警真正有用,你得自己动手调。
今天这篇,我就把我这些年攒下来的经验全倒出来。从最基础的规则结构,到 for 子句的正确用法,再到怎么避免告警风暴,一条一条说清楚。
告警规则的基础结构
先看一个标准的告警规则长什么样。别跳过,我知道你觉得简单,但你确定你真的懂每个字段?
groups:
- name: node-alerts
rules:
- alert: HighCpuUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
annotations:
summary: "Instance {{ $labels.instance }} CPU usage is high"
description: "Instance {{ $labels.instance }} has been above 80% for more than 5 minutes (current value: {{ $value }}%)"
看着眼熟是吧?但这里有个陷阱——大多数人把 for 子句当成摆设。
那个该死的 for 子句到底怎么用?
FAQ 里有人问:“When configuring Prometheus alerting rules, what is the purpose of the for clause?”
官方文档说:for 让 Prometheus 等待一段时间,确认条件持续成立,才触发告警。
但实际生产里,我见过两种极端:
极端一:for: 0s 或者不写。 结果呢?网络抖动一下,CPU 瞬间飙高,告警来了;5 秒后恢复,告警又消了。Alertmanager 疯狂发通知,你手机比女朋友还粘人。
极端二:for: 30m。 等半小时才告警?黄花菜都凉了。用户已经投诉了三轮,你才收到告警。
我的建议是:根据指标的波动特性来设 for。
| 指标类型 | 推荐 for 时长 | 原因 |
|---|---|---|
| CPU 使用率 | 3-5m | 短时波动正常,持续高才危险 |
| 磁盘空间 | 0-1m | 磁盘满就是满,不需要等 |
| 请求延迟 P99 | 2-5m | 避免偶发慢请求触发告警 |
| 错误率 | 1-3m | 看业务容忍度,核心接口建议短 |
| 节点宕机 | 1-2m | 等太久可能影响故障恢复时间 |
我的原则: 宁可让告警晚到 2 分钟,也不要在凌晨 3 点被误报吵醒。但核心业务指标,for 时间可以短一些。
告警规则的设计哲学:少即是多
去年我们团队接手了一个遗留系统,Prometheus 里挂了 400 多条告警规则。猜猜有多少是真正有用的?不到 20%。
剩下的全是垃圾。
比如有个规则:node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100 < 10。看起来合理对吧?但那个节点的内存就是设计成用满的——跑的是 Redis。所以这个告警每周触发 3 次,每次都没人处理。
设计告警规则的第一条铁律: 告警必须能导向一个明确的行动。如果收到告警你不知道该干啥,那这条规则就该删掉。
第二条铁律:不要为每个指标都建告警。 把同类指标聚合起来。
比如,不要这样写:
- alert: HighCpuOnNode1
expr: node_cpu_...{instance="node1"} > 80
- alert: HighCpuOnNode2
expr: node_cpu_...{instance="node2"} > 80
而是这样:
- alert: HighCpuUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
一条规则覆盖所有节点。多简单。
告警规则 vs 记录规则:别搞混了
Prometheus 支持两种规则:告警规则(alerting rules)和记录规则(recording rules)。
记录规则的作用是:预计算复杂的查询,存成新的时间序列,然后告警规则可以直接引用。
我见过有人把复杂的 PromQL 直接怼进告警规则里,结果 Prometheus 每次评估都卡半天。正确的做法是:
# 先定义记录规则
groups:
- name: node-recording-rules
rules:
- record: job:node_cpu_usage:avg_rate5m
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# 再定义告警规则
- name: node-alerting-rules
rules:
- alert: HighCpuUsage
expr: job:node_cpu_usage:avg_rate5m > 80
for: 5m
这样 Prometheus 评估告警规则时,只需要查一个简单的指标,不用每次都跑复杂的 rate() 计算。性能提升肉眼可见。
告警规则配置的常见坑
坑一:标签滥用
有些人在 labels 里写 severity: critical,结果所有告警都是 critical。那 critical 还有什么意义?
我的分级标准:
| 级别 | 含义 | 响应时间 |
|---|---|---|
| critical | 服务不可用或数据丢失 | 15 分钟内 |
| warning | 服务质量下降或风险 | 1 小时内 |
| info | 需要关注但不紧急 | 下一个工作日 |
坑二:annotation 里不写当前值
看这个:
annotations:
summary: "CPU usage is high"
高是多少?80%?95%?收到告警的人还得自己去查 Prometheus。正确的写法是加上 {{ $value }}:
annotations:
summary: "CPU usage is high ({{ $value }}%)"
坑三:忘了配置 Alertmanager
Prometheus 只负责生成告警,发送通知是 Alertmanager 的事。很多人配好了告警规则,发现没收到通知——哦,Alertmanager 没配。
Alertmanager 的配置核心是路由:
route:
receiver: 'team-email'
routes:
- match:
severity: critical
receiver: 'team-pager'
receivers:
- name: 'team-email'
email_configs:
- to: 'team@example.com'
- name: 'team-pager'
webhook_configs:
- url: 'http://pagerduty-webhook'
实战:一个完整的告警规则配置
我直接贴一个我们生产环境在用的配置。已经运行了 8 个月,误报率低于 5%。
groups:
# 节点基础监控
- name: node-alerts
rules:
- alert: NodeDown
expr: up{job="node-exporter"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Node {{ $labels.instance }} is down"
description: "Node {{ $labels.instance }} has been unreachable for more than 2 minutes"
- alert: HighDiskUsage
expr: (1 - (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"})) * 100 > 85
for: 5m
labels:
severity: warning
annotations:
summary: "Disk usage > 85% on {{ $labels.instance }} ({{ $labels.mountpoint }})"
description: "Current value: {{ $value | humanizePercentage }}"
# 应用层监控
- name: app-alerts
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) * 100 > 5
for: 3m
labels:
severity: critical
annotations:
summary: "Error rate > 5% on {{ $labels.instance }}"
description: "Current error rate: {{ $value | humanizePercentage }}"
FAQ:那些你该知道的事
Q: 当配置 Prometheus 告警规则时,for 子句的作用是什么?
A: 看前面说的。简单总结:for 让 Prometheus 在条件满足后等待指定时间,确认问题持续存在才触发告警。避免瞬态波动导致的误报。建议根据指标特性设置 1-5 分钟。
Q: 如何在 Prometheus 中设置告警?
A: 三步走:
- 创建规则文件(如
alerts.yml),定义告警规则 - 在 Prometheus 配置文件(
prometheus.yml)的rule_files字段中引用该文件 - 配置 Alertmanager 并设置接收器(邮件、Slack、PagerDuty 等)
Q: 什么是 Prometheus 告警规则?
A: 告警规则是 YAML 格式的配置,定义了基于 PromQL 表达式的告警条件。当表达式返回非空结果时,告警被触发。每条规则包含 alert 名称、expr 表达式、for 持续时间和 annotations 注释。
Q: 如何创建告警规则?
A: 创建 .yml 文件,按以下格式编写:
groups:
- name: example
rules:
- alert: AlertName
expr: promql_expression
for: duration
labels:
severity: level
annotations:
summary: "description"
然后在 Prometheus 配置中加载该文件。
最佳实践总结
| 实践 | 说明 | 优先级 |
|---|---|---|
| 合理设置 for 时长 | 根据指标特性设 1-5m,避免误报 | 高 |
| 记录规则分离 | 复杂查询先用记录规则预计算 | 高 |
| 精简规则数量 | 只保留能导向行动的告警 | 高 |
| 注释包含当前值 | 使用 {{ $value }} 传递上下文 | 中 |
| 分级标签 | 区分 critical/warning/info | 中 |
| 避免告警风暴 | 使用 Alertmanager 的抑制和分组 | 中 |
| 定期审查规则 | 每季度清理无效规则 | 低 |
最后说两句
告警规则配置这件事,说难不难,说简单也不简单。关键在于——你的告警是为了解决问题,而不是为了让你更焦虑。
我见过太多团队,告警数量上千条,但真正能快速定位问题的没几个。别走他们的老路。
从今天开始,删掉那些没用的规则,重新设计你的告警体系。相信我,你的手机和你的睡眠质量都会感谢你。