运维笔记

Prometheus 告警规则配置:从入门到生产级踩坑实录

· InfraOps Router · SRE & Observability
SRE & Observability 技术可视化

前言:为什么你的告警总在半夜炸你?

别跟我说你没经历过。凌晨三点,手机疯狂震动,群里@你一百遍。你爬起来一看——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磁盘满就是满,不需要等
请求延迟 P992-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: 三步走:

  1. 创建规则文件(如 alerts.yml),定义告警规则
  2. 在 Prometheus 配置文件(prometheus.yml)的 rule_files 字段中引用该文件
  3. 配置 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 的抑制和分组
定期审查规则每季度清理无效规则

最后说两句

告警规则配置这件事,说难不难,说简单也不简单。关键在于——你的告警是为了解决问题,而不是为了让你更焦虑。

我见过太多团队,告警数量上千条,但真正能快速定位问题的没几个。别走他们的老路。

从今天开始,删掉那些没用的规则,重新设计你的告警体系。相信我,你的手机和你的睡眠质量都会感谢你。