Skip to content

告警设计

看板上可以放很多东西——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
exprPromQL 表达式——返回非空序列时才产生告警查哪些 node 的 up=0(抓取失败)
for表达式持续满足多久才真正 firing1m——持续一分钟抓不到
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(中文:告警)

操作步骤:

  1. 修改规则文件后执行 promtool check rules
  2. 热加载 Prometheus
  3. 打开 Prometheus Web 的 Alerts 页面
  4. 搜索 LabNodeExporterAlwaysFiring
  5. 看状态是 inactivepending 还是 firing,展开能看到 labels
状态含义
inactive表达式当前不满足,没有异常
pending异常出现了,但 for 持续时间还没到
firing异常持续满足了 for,正在触发告警

Prometheus Alerts 实验告警

规则没出现在 Alerts 页面上,先检查 promtool check config 确认规则文件是否被 rule_files 引用。规则存在但一直是 inactive——到 Query 页面执行那条 expr,看是不是表达式本身没返回任何序列。规则一直 pending——for 时间还没跑到。

告警分级:severity 的意义

级别是用来决定"处理方式"的,不是装饰。只写 warningcritical 但没有不同的处理动作,级别就失去意义了:

级别大概含义处理方式
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 在持续淘汰 keyrate(redis_evicted_keys_total[5m]) > 0
Nginx stub_status 不可用nginx_up == 0
某台机器失联up == 0

控制告警数量

告警多不一定是规则写错了——维度太细也会让一条规则触发很多条告警。比如磁盘告警每条规则带 instancemountpoint,每台机器的每个挂载点都是一条独立告警。

设计选择影响
PromQL 保留 instance能定位机器,但告警数 = 机器数 × 规则数
保留 mountpoint能定位挂载点,但磁盘告警再翻倍
for 时间太短接更多抖动,太长错过短故障
Alertmanager group_by合并粒度——按 alertname+instance 合并后同类告警拢在一起发
repeat_interval告警一直不恢复时多久重复一次

一个典型的"告警风暴"场景:某台节点网络断了,同时触发 NodeExporterDown、几个服务 Exporter 的 *_up=0、磁盘指标缺失、进程指标缺失——根本原因就一个(节点失联),但发了十几条通知。这种场景更适合在 Alertmanager 里配抑制规则:把"节点不可达"作为根因告警,同实例上的低级别派生告警自动压制,通知会干净很多。

Runbook:让收到告警的人知道怎么看

告警通知里放一个 Runbook 地址,或者在 annotations 里写清第一检查方向,不需要很长——能让人快速进入排查方向就够了:

告警处理时先看什么
NodeExporterDownPrometheus Targets、目标 9100 端口、systemd 状态、机器连通
DiskUsageHigh挂载点、哪个目录在增长、日志轮转正常吗、有没有可以清的临时文件
MysqlConnectionHigh连接来源、慢查询、锁等待、应用连接池配置是不是刚改过
RedisEvictedKeysmaxmemory 限制、淘汰策略、热点 key 是否变化、命中率趋势
NginxExporterDownstub_status 页面、nginx_exporter 进程、Nginx 本身

告警只是一个入口。通知里带上当前值、持续时间、机器、环境,处理人不用再回平台搜一遍基本信息。真正有价值的是排查处理中留下的记录:原因、临时措施、根本修复——这些内容比告警通知本身的文本重要得多。