4. 告警规则设计:基于PromQL编写告警规则,配置Alertmanager实现分级告警与通知

告警这件事,说起来简单——指标超了,发个消息。但真到了生产环境,你会发现事情远没那么简单。

我记得刚接手一个大型电商平台时,告警量每天上千条,值班同学直接麻木了。后来我花了两周时间重新梳理告警规则,把有效告警率从不到10%提升到了85%以上。核心就两点:规则要准,分级要清。

4.1 告警规则的核心设计原则

写告警规则前,先想清楚几个问题:

  • 这个指标真的需要告警吗?——很多指标只是"看起来重要"
  • 阈值设多少才合理?——拍脑袋的阈值等于没有阈值
  • 告警频率怎么控制?——5分钟发一次还是1小时发一次?

我的经验法则:一条好的告警规则,应该让值班同学看到后能立刻知道"发生了什么、影响多大、该找谁"。如果看完还要查半天文档,那这条规则就是失败的。

4.2 PromQL告警规则实战

Prometheus的告警规则写在YAML文件里,通过rules字段加载。先看一个最基础的例子:

groups:
  - name: node_alerts
    rules:
      - alert: NodeDown
        expr: up{job="node_exporter"} == 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "节点 {{ $labels.instance }} 已宕机"
          description: "节点 {{ $labels.instance }} 已失联超过5分钟"

这里有几个关键字段:

  • expr:PromQL表达式,决定什么时候触发
  • for:持续时间,避免瞬时抖动误报
  • labels:给告警打标签,用于后续分级
  • annotations:告警内容模板,支持模板变量

小技巧:for字段别设太短。我见过有人设for: 30s,结果网络抖动一下告警就炸了。一般建议:critical级别设1-5分钟,warning级别设5-15分钟。

4.2.1 常见场景的PromQL写法

实际项目中,我常用的几个模式:

场景 PromQL表达式 说明
CPU使用率过高 (1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)) > 0.9 CPU超过90%持续5分钟
内存使用率过高 (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.9 内存超过90%
磁盘空间不足 node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} < 0.1 根分区剩余不足10%
HTTP错误率过高 sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.05 5xx错误率超过5%

嗯,这里要注意一点:聚合函数的使用。很多人写PromQL时容易忽略by子句,结果出来的数据完全不对。比如计算每台机器的CPU使用率,一定要加上by (instance),否则聚合出来的就是一个全局平均值,毫无意义。

4.3 分级告警设计

为什么要分级?说白了,就是让不同的人关注不同的事。我在项目中一般分三级:

  • critical(严重):服务不可用、数据丢失、核心链路中断。需要立即响应,通常是电话+短信。
  • warning(警告):资源即将耗尽、错误率上升、延迟增加。需要关注,但可以等几分钟再处理。
  • info(信息):日常变更通知、版本发布、计划内维护。看看就行,不需要立即处理。

避坑指南:我曾经在一个项目里把"磁盘使用率超过80%"设成了critical,结果一周内所有值班同学都被告警轰炸到麻木。后来改成"超过90%持续15分钟"才设为warning,超过95%才设为critical,告警量直接降了80%。

4.3.1 通过标签实现分级

在告警规则中,通过labels字段的severity来标记级别:

groups:
  - name: web_alerts
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m])) 
          / sum(rate(http_requests_total[5m])) > 0.1
        for: 3m
        labels:
          severity: critical
          team: backend
        annotations:
          summary: "后端服务错误率超过10%"
          
      - alert: ElevatedErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m])) 
          / sum(rate(http_requests_total[5m])) > 0.03
        for: 5m
        labels:
          severity: warning
          team: backend
        annotations:
          summary: "后端服务错误率超过3%"

你看,同一个指标可以设置多个规则,只是阈值和级别不同。这样既不会漏掉严重问题,也不会被小问题打扰。

4.4 Alertmanager配置实战

Prometheus负责"发现"告警,Alertmanager负责"处理"告警。它做的事情包括:去重、分组、抑制、静默,以及最终的通知发送。

4.4.1 路由配置

Alertmanager通过route来匹配告警并决定发送到哪里:

route:
  receiver: 'default'
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty-critical'
      repeat_interval: 10m
    - match:
        severity: warning
      receiver: 'email-warning'
      repeat_interval: 1h
    - match_re:
        severity: info
      receiver: 'slack-info'

这里的关键参数:

  • group_wait:同一组告警等待多久再发送,用于合并
  • group_interval:同一组告警的发送间隔
  • repeat_interval:告警未恢复时,多久重复通知一次

我的习惯:critical级别的repeat_interval设短一些(10-30分钟),warning级别设长一些(1-4小时)。这样严重问题不会被淹没,小问题也不会频繁打扰。

4.4.2 接收器配置

不同的告警级别走不同的通知渠道:

receivers:
  - name: 'pagerduty-critical'
    pagerduty_configs:
      - routing_key: 'your-pagerduty-key'
        severity: critical
        
  - name: 'email-warning'
    email_configs:
      - to: 'ops-team@company.com'
        send_resolved: true
        
  - name: 'slack-info'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/xxx'
        channel: '#ops-info'
        title: '{{ .GroupLabels.alertname }}'
        text: '{{ .CommonAnnotations.description }}'

4.5 告警规则设计全景图

下面这张图展示了从指标采集到告警通知的完整链路:

告警规则设计全景图 指标采集 Node Exporter / 应用埋点 Prometheus Server 抓取 & 存储 告警规则 PromQL 表达式 Alertmanager 去重 → 分组 → 抑制 → 静默 路由匹配 → 发送通知 Critical 电话 / PagerDuty 立即响应 Warning 邮件 / 企业 关注处理 Info Slack / 钉钉 仅通知 值班人员处理 & 告警恢复 确认 → 排查 → 修复 → 复盘 核心:规则准 + 分级清

4.6 告警规则优化技巧

写告警规则不难,写好告警规则才难。分享几个我踩过的坑:

  1. 避免"抖动告警":用for字段加持续时间,或者用avg_over_time做平滑处理。我曾经有个告警,因为CPU瞬时飙高,每5分钟触发一次,持续了整整一天。
  2. 合理设置重复间隔:告警没恢复不代表要一直发。critical级别10-30分钟重复一次就够了,warning级别可以延长到1-4小时。
  3. 善用告警分组:同一台机器上的多个告警,可以按instance分组,避免"一台机器挂了,收到20条告警"的惨剧。
  4. 添加"已恢复"通知:send_resolved: true这个配置一定要开。不然值班同学处理完问题,还得手动确认告警是否恢复。

曾经踩过的坑:有一次我把告警规则的for设成了0,结果Prometheus重启时所有指标短暂不可用,瞬间触发了上百条告警。值班同学直接崩溃了。从那以后,我所有告警规则都至少设了1分钟的for。

4.7 告警规则测试与验证

规则写完了,怎么验证对不对?我一般用这几个方法:

  • Prometheus UI:在/alerts页面查看规则状态,PENDING表示即将触发,FIRING表示已触发
  • promtool:官方提供的规则检查工具,promtool check rules可以检查语法
  • 模拟测试:用amtool模拟告警发送,验证Alertmanager路由是否正确
# 检查告警规则语法
promtool check rules /etc/prometheus/rules/*.yml

# 模拟告警发送
amtool alert add --alertmanager.url=http://localhost:9093 \
  --annotation=summary="测试告警" \
  --label=severity=critical \
  --start=$(date +%s) \
  test_alert

嗯,说到测试,我建议你在上线前至少跑一轮完整的"告警演练"。找个业务低峰期,手动触发几个典型告警,看看通知能不能到、内容对不对、值班同学能不能看懂。这一步虽然麻烦,但能避免很多线上事故。