运维笔记

Prometheus告警规则别乱写:我踩过的坑和最佳实践总结

SRE & Observability 技术可视化

兄弟们,今天聊聊Prometheus告警规则。

这东西看着简单,不就是写个PromQL吗?但真到了生产环境,你会发现——告警规则写得烂,比没写还可怕。凌晨三点被无关告警叫醒的感觉,懂的都懂。

我接手过一个系统,光是CPUUsageHigh的告警规则就有7条,互相覆盖、阈值混乱。最后我全删了,从零重写。今天把这些经验倒出来,希望能帮你少踩坑。

核心原则:告警不是监控

这是最大的误区。很多人把Prometheus当监控面板用,恨不得每个指标都配个告警。

Stop it.

告警的目的是让人采取行动。如果收到告警但无事可做,那就是垃圾告警。

我团队内部有个硬性规定:每条告警规则必须附带一个runbook_url label,指向具体的操作文档。写不出runbook的告警规则,不准上线。

怎么写一条合格的告警规则?

先看个反面教材:

groups:
- name: bad_examples
  rules:
  - alert: HighCPU
    expr: avg(rate(node_cpu_seconds_total{mode="user"}[5m])) > 0.9
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "CPU is high"

这条规则的问题多了去了:

  • 阈值写死,不考虑机器规格差异
  • 1分钟的for太短,瞬间抖动就告警
  • 告警信息等于没写,收到告警根本不知道哪台机器出了问题

改一下:

groups:
- name: node_alerts
  rules:
  - alert: NodeCPUUsageHigh
    expr: |
      (
        1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)
      ) > 0.9      
    for: 5m
    labels:
      severity: warning
      team: infra
    annotations:
      summary: "Node {{ $labels.instance }} CPU usage > 90%"
      description: "CPU usage on {{ $labels.instance }} has been at {{ $value | humanizePercentage }} for more than 5 minutes."
      runbook_url: "https://wiki.ourteam.com/runbooks/node-high-cpu"

这里做了几件事:

  • by (instance)保留实例信息
  • 拉长for到5分钟,滤掉毛刺
  • 告警信息里带上实例名和具体数值
  • 加了个team label,方便Alertmanager路由

告警命名的艺术

我见过最离谱的命名是Alert_1Alert_2… 这种命名等于告诉所有人:我不在乎我的系统。

命名规则我们内部是这样定的:

组件命名格式示例
节点Node<Metric><Condition>NodeDiskSpaceFull
容器Container<Metric><Condition>ContainerOOMKilled
应用<AppName><Metric><Condition>APIServerLatencyHigh
业务Business<Metric><Condition>BusinessOrderFailureSpike

命名要包含三要素:什么组件 + 什么指标 + 什么异常

告警严重级别怎么定?

别把所有问题都标成critical。我见过一个团队,90%的告警都是critical。结果呢?没人看,真正的critical反而被淹没了。

我们用的是三级制:

级别含义响应时间示例
critical服务不可用或数据丢失立即响应,15分钟内磁盘写不进去了,API全挂
warning服务质量下降,但还能用工作时间1小时内P99延迟翻倍,但还在SLA内
info需要关注,但不用立即处理证书30天后过期

关键点critical一定要和for配合使用。我见过有人设for: 0s的critical告警,结果网络抖动一下,全员被叫醒。

常见踩坑现场

1. 聚合粒度搞错

# 错误:这告警的是集群整体CPU,但你不知道是哪台机器
expr: avg(node_cpu_seconds_total) > 0.9

# 正确:按实例聚合
expr: avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) < 0.1

2. 忘记处理重启后的指标重置

计数器类型的指标(比如http_requests_total)在进程重启后会重置。直接用rateincrease可以避免这个问题,但如果你用delta,就要小心了。

# 错误:进程重启后可能触发假告警
expr: delta(http_requests_total[5m]) < 0

# 正确:用rate,它会正确处理重置
expr: rate(http_requests_total[5m]) < 0.01

3. 告警沉默(Alert Fatigue)

这是最致命的。告警太多 -> 没人看 -> 真出事时错过 -> 系统崩了。

解决方案:定期做告警审计。我每季度做一次,把所有告警规则的触发次数和响应情况拉出来看。触发次数多但没人响应的规则,要么删掉,要么降低级别。

高级技巧:用Recording Rules预计算

如果你的告警规则涉及复杂的聚合计算,别让每次评估都重新算。用Recording Rules预计算:

groups:
- name: recording_rules
  rules:
  - record: job:node_cpu_usage:avg_5m
    expr: avg by(job) (rate(node_cpu_seconds_total{mode="idle"}[5m]))
    
- name: alert_rules
  rules:
  - alert: NodeCPUUsageHigh
    expr: (1 - job:node_cpu_usage:avg_5m) > 0.9
    for: 5m

这样做的好处:

  • 减少Prometheus的计算压力
  • 告警规则更简洁
  • 可以在Grafana里复用同一个指标

告警规则测试

别以为写好了就完事了。Prometheus 2.28+支持promtool测试告警规则:

# test_rule.yaml
rule_files:
  - alerts.yml

evaluation_interval: 1m

tests:
  - interval: 1m
    input_series:
      - series: 'node_cpu_seconds_total{instance="node1",mode="idle"}'
        values: '0 0 0 0 0'  # 5分钟都是0,说明CPU 100%繁忙
    alert_rule_test:
      - eval_time: 5m
        alertname: NodeCPUUsageHigh
        exp_alerts:
          - exp_labels:
              severity: warning
              instance: node1
            exp_annotations:
              summary: "Node node1 CPU usage > 90%"

运行测试:

promtool test rules test_rule.yaml

这个步骤被99%的团队忽略了。我敢说,没经过测试的告警规则,上线后大概率会出事。

最佳实践总结表

实践说明推荐度
告警要有runbook每条规则必须有对应的操作文档⭐⭐⭐⭐⭐
合理使用for至少2-5分钟,避免毛刺⭐⭐⭐⭐⭐
命名规范统一组件+指标+异常⭐⭐⭐⭐
告警级别分级别让critical泛滥⭐⭐⭐⭐⭐
用Recording Rules预计算减轻Prometheus压力⭐⭐⭐⭐
定期审计告警清理无效规则⭐⭐⭐⭐⭐
测试告警规则用promtool验证⭐⭐⭐⭐
避免告警信息为空带上实例和具体数值⭐⭐⭐⭐⭐

FAQ

Q: 告警规则太多怎么办?

A: 做告警聚合。把类似的告警合并成一条,比如把DiskUsageHighInodeUsageHigh合并成StorageUsageHigh,在描述里区分具体问题。我们团队从120条规则砍到了45条,告警噪音下降了70%。

Q: for参数设多久合适?

A: 取决于场景。基础设施告警(CPU、内存)建议5-10分钟;应用层面的告警(错误率、延迟)可以短一些,2-3分钟。核心原则:宁可慢报,不要误报

Q: 怎么处理重复告警?

A: 利用Alertmanager的group_byrepeat_interval。相同的告警聚合发送,重复的告警不要频繁发。我一般设repeat_interval: 4h,意思是同一个告警如果没解决,4小时后才再次提醒。

Q: 阈值怎么定才合理?

A: 别拍脑袋。先收集两周的基线数据,看正常情况下的P99和P95值。然后基于这些数据设定阈值。我见过最离谱的是把CPU阈值设为5%,结果天天告警——那台机器本来就跑着高负载任务。


告警规则写得好不好,直接决定你晚上能不能睡个好觉。别等到凌晨三点被叫醒才后悔。

有问题欢迎留言,我们评论区见。