本篇是《Prometheus 从入门到生产实践》第 13 / 24 篇。配置以文中版本与环境前提为准,实验和故障均为教学场景。完整程序、配置及校验范围见配套实验包。

能查到异常指标,只说明你能发现问题;要让值班人员持续、准确地得到提醒,还需要把查询转换成有清晰身份和状态的告警。本篇建立规则、运行单元测试,并区分短暂异常、持续故障与数据消失。
1. 规则产生的是告警实例
告警表达式在求值时返回若干向量元素,每一组标签可以形成一个实例。比如两台 demo exporter 同时抓取失败,会产生两条带不同 instance 的告警,而不是一个没有对象的“系统异常”。
本地实验使用 up{job="demo"}。先确认 Targets 中确实存在 job=demo 的目标,再观察 up。抓取成功时为 1,失败时为 0;没有这条时间序列时,不能自动按 0 处理。
for 作用于同一个告警实例:持续满足条件达到指定时间后才进入 firing。它不是“每隔两分钟检查一次”。规则组的 interval 才控制该组的求值节奏。状态机制见 Prometheus 告警规则文档。
2. 写一条可解释的抓取告警
在实验配置目录保存 b-rules.yml:
groups:
- name: demo-availability
interval: 30s
rules:
- alert: DemoTargetDown
expr: up{job="demo"} == 0
for: 2m
labels:
severity: warning
environment: lab
annotations:
summary: '采集失败:{{ $labels.instance }}'
description: 'demo 目标持续两分钟抓取失败,先检查 Targets 最近错误。'
将它加入现有 prometheus.yml 的 rule_files 列表,而不是再创建第二个同名键:
rule_files:
- b-rules.yml
这是配置片段,仍需要已有 global、scrape_configs 和 alerting 配置。相对规则路径按配置加载语境解析,最好保持实验配置和规则同目录,避免拷贝后找不到文件。
这条教学告警只说“抓取失败”,不宣称服务器已经宕机,也不直接推断用户请求失败。影响范围和处理入口比一个笼统的严重级别更有帮助。severity 是标签约定,需要通知路由消费才会决定去哪里提醒。
3. 理解 pending、firing 与恢复
假设规则每 30 秒求值,首次在 0 分钟看见 up=0,之后标签和条件保持不变:
| 时间 | 条件 | 预期状态 |
|---|---|---|
| 0 分钟 | 首次成立 | pending |
| 1 分钟 | 仍成立 | pending |
| 2 分钟 | 仍成立 | firing |
| 3 分钟 | 已变为 up=1 | 当前规则不再活动 |
真实故障发生时间可能早于首次采样和求值,因此不要拿故障时刻简单加两分钟作为短信必达时间。抓取间隔、求值间隔、发送与通知等待都会进入链路。
本例没有设置 keep_firing_for,条件解除后,不需要再等同样的 for 才恢复。可选的 keep_firing_for 会在条件不再满足后继续保持 firing 一段时间,适合评估抖动场景,但也会延迟恢复,应核对所用版本并单独测试。
4. 用合成输入验证,不必人工等两分钟
保存同目录的 b-rules.test.yml:
rule_files:
- b-rules.yml
evaluation_interval: 30s
tests:
- interval: 30s
input_series:
- series: 'up{job="demo",instance="127.0.0.1:19100"}'
values: '0x5 1x4'
alert_rule_test:
- eval_time: 1m
alertname: DemoTargetDown
exp_alerts: []
- eval_time: 2m
alertname: DemoTargetDown
exp_alerts:
- exp_labels:
job: demo
instance: '127.0.0.1:19100'
severity: warning
environment: lab
exp_annotations:
summary: '采集失败:127.0.0.1:19100'
description: 'demo 目标持续两分钟抓取失败,先检查 Targets 最近错误。'
- eval_time: 3m
alertname: DemoTargetDown
exp_alerts: []
- interval: 30s
input_series:
- series: 'up{job="demo",instance="127.0.0.1:19100"}'
values: '0 0 1 1 1 1'
alert_rule_test:
- eval_time: 2m
alertname: DemoTargetDown
exp_alerts: []
执行:
promtool check rules b-rules.yml
promtool test rules b-rules.test.yml
第一组输入每 30 秒一个点,0x5 是一个 0 再重复五次,之后从第 3 分钟出现 1。测试检查 1 分钟尚未 firing、2 分钟形成指定告警、3 分钟恢复。第二组模拟一分钟后恢复的短暂失败,预期不会达到 for 门槛。
这些测试验证规则对指定输入的行为,不验证真实 DNS、采集网络、Alertmanager 连接和接收通知。测试格式与更多用法见 官方规则测试说明。
5. 标签要稳定,动态描述放 annotations
告警身份包含标签。若把实时错误率、当前时间或不断变化的文字放进 labels,每次求值都可能变成一个新实例,pending 计时重来,通知去重也会被打散。
适合标签的是稳定路由维度,如 service、cluster、environment、severity。当前值、排障说明和链接放 annotations。不是所有现有指标标签都应该一直带到告警层:保留什么,要看你准备由谁处理、按什么范围去重。
同一个 alertname 若表示完全不同业务,也容易在路由和抑制时混淆。名称应描述可行动的症状,annotations 应告诉接手者先检查哪一层,而不是只输出“超过阈值”。
6. 数据缺失是一种独立故障
up == 0 只能匹配存在且等于 0 的序列。目标被错误配置过滤掉、服务发现失效或 job 被删掉后,它可能根本不再有 up 数据。
可以对一个必须存在的固定 job 练习:
absent_over_time(up{job="demo"}[5m])
它检查这组选择条件在窗口内是否完全没有数据。它不能凭空重建每台应该存在但已经消失的机器清单;要逐实例检测缺失,需要另外维护可信资产或期望目标指标。
也不要为了“让图不断线”到处使用 or vector(0):它不会为所有缺失标签组合自动补齐零,还可能把无数据和真正的零混在一起。
7. 从技术阈值走向可行动告警
抓取失败只是最容易验证的起点。真实业务还需要错误比例、延迟和容量趋势。写错误率告警时,先明确哪些请求算失败、哪些请求进入分母,分子和分母是否在同一服务、路由与窗口下聚合。
一个低流量接口只有一次请求、恰好失败,错误率就会达到百分之百;另一个高流量接口失败比例较低,却可能影响更多用户。告警中可以结合最小流量条件与持续窗口,但这不是把阈值随意提高以减少消息,而是明确你准备为哪一种用户影响负责。
还要考虑分母为零的含义。没有请求时,比例可能是无穷值、非数或没有输出,取决于表达式和数据存在情况。无请求是否属于故障,不能交给一个默认补零技巧决定。面向批处理的业务,则可能更适合监控最近成功时间和数据积压,而不是套用在线请求错误率。
8. 发布规则也需要变更流程
一条规则从笔记走向值班,可以按三步推进。先用固定输入做边界测试,包含短暂异常、持续异常和恢复;再在测试或旁路实例中观察真实数据,确认标签和数量符合预期;最后接入适当的接收人,并准备简短排障入口。
规则本身也可能失效:表达式始终报错、求值超时,或者负责规则的实例已经不可用。只监控被管理系统,却没人确认监控系统是否仍在执行,会形成盲区。因此应有独立视角检查规则评估、实例可用性和通知链路。
验证覆盖范围时,注意同一规则的实例数量。原来每个服务一条,变更后突然变成每个容器一条,既可能增加通知数量,也可能改变持续计时和处置范围。这个变化应在发布前通过标签集合对照被看见,而不是等值班电话响后再发现。
9. 运行验收与练习
规则检查通过后,按实验已有重载方式加载,再在 Prometheus 的 Rules、Alerts 页面确认规则出现且健康。查询 ALERTS{alertname="DemoTargetDown"} 可以观察 pending 或 firing 的状态序列。
在专用实验中停止 exporter,确认 up=0 和持续时间;恢复 exporter,确认规则解除。再单独从采集配置移除目标,比较“采集失败”和“没有目标”在查询与告警上的差别。把两种情况都能解释清楚,才算写出了一条可维护的告警。
上一篇:Grafana 与 SLO:正确展示、错误预算和多窗口燃尽 · 系列目录 · 下一篇:Alertmanager:路由、分组、静默、抑制与恢复通知