Appearance
告警设计
看板上可以放很多东西——CPU 曲线、连接数趋势、P95 延迟、各种汇总表。告警不能这样做。告警的价值是"把需要处理的问题挑出来",不是把看板上每条波动都变成通知。
告警和看板是两件事
| 对象 | 谁看 | 用途 | 放什么 |
|---|---|---|---|
| 看板 | 人主动打开去看 | 观察趋势、对比、排查细节 | 放多一点指标,越全越好 |
| 告警 | 平台主动通知人 | 提醒处理持续异常 | 只放需要人介入的问题 |
| 日志 | 人查到异常后翻 | 看每次错误的细节和上下文 | 错误堆栈、请求参数、trace_id |
几个具体场景:CPU 偶尔冲到 80%——放看板上观察趋势没问题,告警不合适,因为它很可能马上回落。磁盘 90% 了——适合告警,因为很快就满了且不会自动缩小。接口 5xx 错误率连续 5 分钟超过 1%——适合告警,说明上线了什么东西或者出了什么故障。单个请求出错——进日志就行,不是告警。
一条告警通常满足三个条件才有价值:异常能被指标稳定捕捉到(不是偶尔一闪)、持续一段时间仍然有问题(不是瞬间自动恢复)、需要人来处理(不是系统自己能修复)。缺一个就可能变成噪声。
Prometheus 告警规则的结构
一条告警规则由五部分构成。看一个实际的例子——监控 Node Exporter 是否被抓到:
yaml
groups:
- name: node-basic
rules:
- alert: NodeExporterDown
expr: up{job="node"} == 0
for: 1m
labels:
severity: warning
annotations:
summary: "Node Exporter down on {{ $labels.instance }}"
description: "Prometheus cannot scrape node_exporter for more than 1 minute."| 字段 | 作用 | 这个例子写了什么 |
|---|---|---|
alert | 告警名称 | NodeExporterDown |
expr | PromQL 表达式——返回非空序列时才产生告警 | 查哪些 node 的 up=0(抓取失败) |
for | 表达式持续满足多久才真正 firing | 1m——持续一分钟抓不到 |
labels | 告警标签——影响分级、路由和分组 | severity: warning |
annotations | 告警描述——放进通知内容 | summary 和 description 用模板变量拼 |
for 是关键设计。Prometheus 每 15 秒抓一次,抓取偶尔失败一次很正常——网络抖一下、目标机器在做短暂维护。for: 1m 的意思是"连续一分钟都失败"才当异常处理。这能过滤掉大部分瞬时抖动。
但 for 设多久没有统一值。在线接口的抓取失败可以设短一些(1m),因为影响面大;低频批任务的抓取失败可以设长一些(3m~5m),因为样本本身就少。同样设 1m,抓取间隔 15s 等于连续失败 4 次;抓取间隔 60s 只够失败 1 次——样本量和间隔会影响 for 的实际效果。
当前实验中的两条规则
yaml
groups:
- name: node-basic
rules:
- alert: NodeExporterDown
expr: up{job="node"} == 0
for: 1m
labels:
severity: warning
annotations:
summary: "Node Exporter down on {{ $labels.instance }}"
description: "Prometheus cannot scrape node_exporter for more than 1 minute."
- alert: LabNodeExporterAlwaysFiring
expr: up{job="node",instance="192.168.10.13:9100"} == 1
for: 30s
labels:
severity: info
annotations:
summary: "Lab alert for screenshot and routing verification"
description: "This rule is intentionally true in the lab to verify Alertmanager flow."第一条是真正有用的告警。第二条是实验专用——只要 cache01 的 Node Exporter 正常跑着,这条规则就持续触发,用来验证"Prometheus 规则计算 → 发给 Alertmanager → receiver 收到通知"这条链路是通的。生产环境不需要保留这种"永远是 true"的规则。
规则写好之后,先检查语法再热加载:
bash
# 检查主配置和规则文件引用
promtool check config /etc/prometheus/prometheus.yml
# 单独检查规则文件——PromQL 语法错、YAML 缩进错会在这里暴露
promtool check rules /etc/prometheus/rules/node.yml
# 热加载——避免重启造成短暂的采集空白
curl -X POST http://127.0.0.1:9090/-/reload页面路径看告警状态:Alerts(中文:告警)
操作步骤:
- 修改规则文件后执行
promtool check rules - 热加载 Prometheus
- 打开 Prometheus Web 的
Alerts页面 - 搜索
LabNodeExporterAlwaysFiring - 看状态是
inactive、pending还是firing,展开能看到 labels
| 状态 | 含义 |
|---|---|
inactive | 表达式当前不满足,没有异常 |
pending | 异常出现了,但 for 持续时间还没到 |
firing | 异常持续满足了 for,正在触发告警 |

规则没出现在 Alerts 页面上,先检查 promtool check config 确认规则文件是否被 rule_files 引用。规则存在但一直是 inactive——到 Query 页面执行那条 expr,看是不是表达式本身没返回任何序列。规则一直 pending——for 时间还没跑到。
告警分级:severity 的意义
级别是用来决定"处理方式"的,不是装饰。只写 warning、critical 但没有不同的处理动作,级别就失去意义了:
| 级别 | 大概含义 | 处理方式 |
|---|---|---|
info | 状态变化、实验验证 | 低优先级通知,或只记不通知 |
warning | 已有异常迹象,但影响还可控 | 值班关注,自己判断要不要马上处理 |
critical | 已经影响业务了,或很快就会 | 立即处理 |
告警进入 Alertmanager 时,severity 标签可以决定发到哪个群、什么频率、是否抑制低级别告警。比如 severity=critical 发到高优先级通知渠道,severity=warning 走正常值班渠道。
常见标签的作用
告警上的标签会流到 Alertmanager,决定分组、静默、抑制和通知路由:
| 标签 | 有什么用 |
|---|---|
severity | 决定通知级别 |
env | 区分环境——dev/staging/prod 通知到不同的地方 |
job | 区分哪类采集 |
instance | 哪台机器——没这个就定位不到具体机器 |
hostname | 辅助定位——比 IP 更直观 |
role | 机器用途——dba 的问题不该发给前端团队 |
team | 负责团队,决定通知路由到谁 |
标签是不是要保留,可以简化为一个判断:去掉这个标签后收到告警时,还需要再查一遍才能知道找谁处理吗?如果是,就保留。
几类基础告警方向
主机侧:
| 场景 | 表达式基本方向 |
|---|---|
| Node Exporter 抓取失败 | up{job="node"} == 0 |
| CPU 持续高位 | CPU 使用率 > 某阈值,并持续一段时间 |
| 内存可用量持续偏低 | MemAvailable / MemTotal 的比值低于阈值 |
| 磁盘快满 | 按挂载点查可用空间比例 |
| inode 快满 | inode 可用率低——和容量不一样,有空间但 inode 满了也写不进去 |
服务侧:
| 场景 | 表达式基本方向 |
|---|---|
| MySQL Exporter 连不上数据库 | mysql_up == 0 |
| Redis Exporter 连不上缓存 | redis_up == 0 |
| Redis 在持续淘汰 key | rate(redis_evicted_keys_total[5m]) > 0 |
| Nginx stub_status 不可用 | nginx_up == 0 |
| 某台机器失联 | up == 0 |
控制告警数量
告警多不一定是规则写错了——维度太细也会让一条规则触发很多条告警。比如磁盘告警每条规则带 instance 和 mountpoint,每台机器的每个挂载点都是一条独立告警。
| 设计选择 | 影响 |
|---|---|
PromQL 保留 instance | 能定位机器,但告警数 = 机器数 × 规则数 |
保留 mountpoint | 能定位挂载点,但磁盘告警再翻倍 |
for 时间 | 太短接更多抖动,太长错过短故障 |
Alertmanager group_by | 合并粒度——按 alertname+instance 合并后同类告警拢在一起发 |
repeat_interval | 告警一直不恢复时多久重复一次 |
一个典型的"告警风暴"场景:某台节点网络断了,同时触发 NodeExporterDown、几个服务 Exporter 的 *_up=0、磁盘指标缺失、进程指标缺失——根本原因就一个(节点失联),但发了十几条通知。这种场景更适合在 Alertmanager 里配抑制规则:把"节点不可达"作为根因告警,同实例上的低级别派生告警自动压制,通知会干净很多。
Runbook:让收到告警的人知道怎么看
告警通知里放一个 Runbook 地址,或者在 annotations 里写清第一检查方向,不需要很长——能让人快速进入排查方向就够了:
| 告警 | 处理时先看什么 |
|---|---|
NodeExporterDown | Prometheus Targets、目标 9100 端口、systemd 状态、机器连通 |
DiskUsageHigh | 挂载点、哪个目录在增长、日志轮转正常吗、有没有可以清的临时文件 |
MysqlConnectionHigh | 连接来源、慢查询、锁等待、应用连接池配置是不是刚改过 |
RedisEvictedKeys | maxmemory 限制、淘汰策略、热点 key 是否变化、命中率趋势 |
NginxExporterDown | stub_status 页面、nginx_exporter 进程、Nginx 本身 |
告警只是一个入口。通知里带上当前值、持续时间、机器、环境,处理人不用再回平台搜一遍基本信息。真正有价值的是排查处理中留下的记录:原因、临时措施、根本修复——这些内容比告警通知本身的文本重要得多。