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 告警规则设计全景图
下面这张图展示了从指标采集到告警通知的完整链路:
4.6 告警规则优化技巧
写告警规则不难,写好告警规则才难。分享几个我踩过的坑:
- 避免"抖动告警":用
for字段加持续时间,或者用avg_over_time做平滑处理。我曾经有个告警,因为CPU瞬时飙高,每5分钟触发一次,持续了整整一天。 - 合理设置重复间隔:告警没恢复不代表要一直发。critical级别10-30分钟重复一次就够了,warning级别可以延长到1-4小时。
- 善用告警分组:同一台机器上的多个告警,可以按
instance分组,避免"一台机器挂了,收到20条告警"的惨剧。 - 添加"已恢复"通知:
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
嗯,说到测试,我建议你在上线前至少跑一轮完整的"告警演练"。找个业务低峰期,手动触发几个典型告警,看看通知能不能到、内容对不对、值班同学能不能看懂。这一步虽然麻烦,但能避免很多线上事故。