装完监控之后会面临一个更实际的问题:指标有几百上千个,到底该看哪几个?如果把所有指标都设成告警,结果必然是每天收到几百条通知,然后所有人对告警视而不见——这比完全没有监控更糟,因为它给了你一种虚假的安全感。
这篇文章给出一份经过筛选的清单:四类系统资源各看哪几个指标(而不是全部)、应用层必须有的三个指标、RED 与 USE 怎么落地成具体面板、以及最关键的——阈值该怎么定才能既不漏报又不吵。最后讨论一个容易被忽略的理念问题:监控显示的是症状,不是原因。
文中的每个指标都会给出"为什么重要"和"看的时候要注意什么",而不是罗列一大堆名字让你自己挑。
一、筛选指标的原则
在展开清单之前,先确立三个筛选原则,它们能帮你面对任意新指标时做出判断。
原则一:能触发行动的才报警,只能提供上下文的只做展示。 CPU 使用率 85% 且持续 10 分钟可能需要处理;而"某个 exporter 的 scrape 耗时"通常只需要在排查问题时看。把后者设成告警就是噪音。
原则二:优先选择饱和类指标而不是使用率类指标。 使用率高是"忙",饱和是"排队"。排队会直接导致延迟劣化,是更早、更准的信号。典型例子:CPU 的运行队列长度比使用率更早反映问题,磁盘的 await 比 %util 更有判断力。
原则三:每个指标都要知道"异常时下一步做什么"。 如果一个指标异常了但你不知道该查什么,那它暂时不该进告警列表。
还有一个数量上的经验:一套覆盖几十台机器的监控系统,核心告警控制在 20 条以内。超过这个数量,值班人员不可能每条都认真处理。
二、CPU:四个指标,不止使用率
| 指标 | 来源 | 为什么看 | 注意点 |
|---|---|---|---|
| CPU 使用率(分项) | top / node_exporter |
基础容量信号 | 必须拆成 us/sy/wa/st 看,总数没意义 |
| Load Average | /proc/loadavg |
反映排队程度 | 必须除以逻辑核数判断 |
| 运行队列长度 | vmstat 的 r 列 |
瞬时排队,比 Load 更敏感 | Load 是平均值会掩盖毛刺 |
| 上下文切换率 | vmstat 的 cs 列 |
发现"没干活但在切换"的浪费 | 持续数万以上需要关注 |
CPU 使用率的四个分项各有不同含义,分开看才能定位:us 高说明应用在做计算、sy 高说明系统调用或切换太多、wa 高说明卡在 IO(这时候加 CPU 没用)、st 高说明虚拟机被宿主机抢走了 CPU(guest 内无解)。
相关 PromQL:
# 总使用率
100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# 分项使用率(user / system / iowait / steal)
avg by(instance)(rate(node_cpu_seconds_total{mode="user"}[5m])) * 100
avg by(instance)(rate(node_cpu_seconds_total{mode="system"}[5m])) * 100
avg by(instance)(rate(node_cpu_seconds_total{mode="iowait"}[5m])) * 100
avg by(instance)(rate(node_cpu_seconds_total{mode="steal"}[5m])) * 100
# 每核归一化后的 Load(按CPU 核数归一化的 1 分钟负载)
node_load1 / count by(instance)(node_cpu_seconds_total{mode="idle"})
# 上下文切换速率
rate(node_context_switches_total[5m])
告警建议:
- 使用率(排除 idle)> 85% 持续 10 分钟 → warning
- Load/核数 > 3 持续 10 分钟 → warning
- wa > 20% 持续 5 分钟 → warning,但告警内容应该指向"去查磁盘 IO"而不是"去查 CPU"
- st > 10% 持续 10 分钟 → warning,内容是"宿主机超卖,考虑迁移"
不要设"CPU 使用率 > 60% 就告警"。 这个阈值会导致每天几百条噪音,因为任何批处理、备份、日志压缩都会瞬时冲到 60%。
三、内存:可用量远比使用率重要
| 指标 | 来源 | 为什么看 | 注意点 |
|---|---|---|---|
| MemAvailable | /proc/meminfo / node_exporter |
最关键的单一指标 | 已包含可回收缓存 |
| swap 使用量与趋势 | free |
增长趋势比绝对值重要 | 少量使用不一定有问题 |
| si / so(换页速率) | vmstat / node_exporter |
持续非 0 就是问题 | 这是最灵敏的内存压力信号 |
| OOM 事件 | dmesg / journal |
已造成伤害的事后信号 | 需要日志采集才能发现 |
| Slab(SUnreclaim) | /proc/meminfo |
发现内核态泄漏 | 增长异常时才看 |
# 可用内存百分比
100 * node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes
# swap 使用百分比
100 * (1 - node_memory_SwapFree_bytes / node_memory_SwapTotal_bytes)
# 换入换出速率(KB/s)
rate(node_vmstat_pswpin[5m]) * 4 # 页面数 × 4KB
rate(node_vmstat_pswpout[5m]) * 4
# 主要缺页率(需要磁盘访问的缺页,内存不足的典型表现)
rate(node_vmstat_pgmajfault[5m])
告警建议:
- 可用内存百分比 < 10% 持续 5 分钟 → warning
- 可用内存 < 5% 持续 2 分钟 → critical
- swap 使用率 > 20% 且 1 小时内增长 > 5% → warning(用增长率而不是绝对值)
- pswpin/pswpout 持续 > 0 超过 5 分钟 → warning
- 检测到 "Out of memory" 内核日志 → critical
为什么用趋势而不是绝对值判断 swap? 因为很多内核会在系统空闲时把很少使用的匿名页提前换出,以腾出更多内存给文件缓存,这是正常的内存管理行为,swap 占用可能有 10~20% 但系统完全健康。真正危险的是"持续增长不回落"。
四、磁盘:空间、IOPS、await 三个维度
磁盘要分成两件事监控:容量和性能,它们完全独立,经常被混为一谈。
容量维度
| 指标 | 为什么看 | 告警建议 |
|---|---|---|
| 空间使用率 | 满了会导致写失败 | > 80% warning,> 90% critical |
| inode 使用率 | 空间没满但 inode 耗尽同样无法创建文件 | > 80% warning |
| 剩余天数预测 | 给出行动窗口,比静态阈值好 | < 4 小时 critical |
# inode 使用率(最容易漏的一项)
100 - (100 * node_filesystem_files_free{mountpoint="/"} / node_filesystem_files{mountpoint="/"})
# 4 小时后是否会被写满(线性预测)
predict_linear(node_filesystem_avail_bytes{fstype!~"tmpfs|squashfs"}[6h], 4*3600) < 0
# 文件系统只读检测(文件系统进入只读往往是磁盘故障前兆)
node_filesystem_readonly == 1
inode 耗尽极具迷惑性:df -h 显示还剩 40% 空间,但应用报 "No space left on device"。海量小文件场景(邮件队列、PHP session 文件、容器镜像层、日志切割碎片)经常遇到。排查用 df -i。
性能维度
| 指标 | 含义 | 注意点 |
|---|---|---|
| await(r_await/w_await) | 请求平均等待时间 | 最可靠判断依据,需与自家基线对比 |
| IOPS(r/s + w/s) | 每秒请求数 | 对比设备规格 |
| 吞吐(rkB/s + wkB/s) | 每秒字节数 | 对比带宽上限 |
| avgqu-sz | 平均队列长度 | 持续 > 1 说明排队 |
| %util | 设备忙碌占比 | SSD 上参考价值下降 |
# 磁盘忙碌程度(等价于 iostat 的 %util)
rate(node_disk_io_time_seconds_total[5m]) * 100
# 平均请求等待时间(await,单位秒)
rate(node_disk_read_time_seconds_total[5m]) / rate(node_disk_reads_completed_total[5m])
rate(node_disk_write_time_seconds_total[5m]) / rate(node_disk_writes_completed_total[5m])
# IOPS
rate(node_disk_reads_completed_total[5m]) + rate(node_disk_writes_completed_total[5m])
# 吞吐
rate(node_disk_read_bytes_total[5m]) + rate(node_disk_written_bytes_total[5m])
告警建议:
- 磁盘 4 小时内将写满 → critical(这条优先级最高)
- await > 基线 2 倍持续 5 分钟 → warning
- IOPS 达到设备规格 80% → warning
- SMART 错误计数增长 → critical(磁盘故障前的信号)
五、网络:包量常被忽略,重传最重要
| 指标 | 为什么看 | 参考阈值 |
|---|---|---|
| 带宽(rxkB/s、txkB/s) | 基础容量 | 接近网卡规格 70% 关注 |
| 包速率(rxpck/s) | 小包洪水时带宽不高但 CPU 已吃紧 | 单机几十万 pps 需关注 |
| 重传率 | TCP 重传会触发拥塞控制降窗,吞吐骤降 | 超过 0.1% 就值得查 |
| 丢包与错误计数 | 硬件或驱动问题 | 出现增长即排查 |
| 连接数与 TCP 状态分布 | TIME_WAIT 过多可能是端口耗尽前兆 | 结合业务判断 |
# 出入带宽
rate(node_network_receive_bytes_total{device!~"lo|veth.*|docker.*"}[5m]) * 8 # 比特/秒
rate(node_network_transmit_bytes_total{device!~"lo|veth.*|docker.*"}[5m]) * 8
# 包速率
rate(node_network_receive_packets_total{device!~"lo|veth.*"}[5m])
# 错误包与丢包速率
rate(node_network_receive_errs_total[5m]) + rate(node_network_transmit_errs_total[5m])
rate(node_network_receive_drop_total[5m])
# TCP 重传速率(千分之一的重传就足以让吞吐明显下降)
rate(node_netstat_TcpRetransSegs[5m])
# 处于 TIME_WAIT 的连接数(需要 tcpstat collector)
node_tcpstat_CurrEstab
用命令行快速核查:
sar -n DEV 1 # rxkB/s、txkB/s、rxpck/s、txpck/s
sar -n ETCP,TCP 1 # active/s、passive/s、retrans/s、isegerr/s
ip -s link # RX/TX 的 errors、dropped、overrun
ethtool -S eth0 | grep -iE 'drop|err|over|miss' # 网卡层的丢包与 Ring Buffer 溢出
丢包定位的顺序很重要:先看网卡统计(是否是 Ring Buffer 溢出或物理问题),再看内核 netdev_max_backlog 是否打满(ip -s link 的 dropped 或 /proc/net/softnet_stat 的相应列),最后看是否被防火墙丢弃(iptables/nftables 的规则计数)。
cat /proc/net/softnet_stat # 第二列是被丢弃的包数(每 CPU 一行)
nft list ruleset -a 2>/dev/null | head -40 # 查看 nftables 规则计数
六、应用层:三个必看指标
系统级指标只能告诉你"机器好不好",不能告诉你"服务好不好"。应用层至少要采集这三项:
| 指标 | 含义 | 为什么必看 |
|---|---|---|
| QPS / TPS | 每秒请求数或事务数 | 流量异常(暴跌或暴增)本身就是最强信号 |
| 响应时间 P95 / P99 | 分位数而非平均值 | 平均值会被长尾掩盖,用户抱怨的永远是长尾 |
| 错误率 | 失败请求占比 | 最直接的质量信号 |
为什么必须看 P95/P99 而不是平均值? 假设 100 个请求里有 95 个是 20ms、5 个是 3000ms,平均值是 169ms,看起来"还行"。但那 5 个用户体验的是 3 秒的白屏。平均值会系统性地掩盖长尾问题。P99 的含义是"99% 的请求比这个值快",直接反映最差那 1% 的体验。
配套还应该看的:
- 超时率:与外部依赖相关
- 连接池使用率:连接耗尽会导致大量请求排队,表现往往是延迟升高而非错误
- 队列积压:消息堆积、线程池队列长度
- 依赖的下游响应时间:判断是不是别人的问题
Nginx 侧可以直接从日志里算这些:
# 假设日志格式里带了 $request_time 和 $upstream_response_time
# Nginx 配置:
log_format main '... "$request" $status $body_bytes_sent ... $request_time $upstream_response_time';
access_log /var/log/nginx/access.log main buffer=32k flush=5s;
# 快速统计 P95 响应时间与错误率
awk '{print $NF}' /var/log/nginx/access.log | sort -n > /tmp/rt.txt
awk 'BEGIN{c=0} {a[c++]=$1} END{printf "count=%d p50=%.3f p95=%.3f p99=%.3f\n", c, a[int(c*0.5)], a[int(c*0.95)], a[int(c*0.99)]}' /tmp/rt.txt
# 状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 实时 QPS(统计近 1 分钟请求数)
tail -f /var/log/nginx/access.log | pv -l -i 60 -r > /dev/null
生产环境更推荐把这些做成 exporter(如 mtail、promtail 提取日志中的指标,或直接在应用里用 Prometheus client 暴露 /metrics),而不是每次都去 awk 一遍。
用 Prometheus 表达应用层指标
# QPS(假设有 http_requests_total Counter)
sum(rate(http_requests_total[1m]))
# 错误率
100 * sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
# P95 响应时间(Histogram 类型)
histogram_quantile(0.95, sum by(le)(rate(http_request_duration_seconds_bucket[5m])))
# P99
histogram_quantile(0.99, sum by(le)(rate(http_request_duration_seconds_bucket[5m])))
七、RED 与 USE 落地成面板
前面列的指标体系,其实可以用两个框架组织起来,这样不会遗漏也不会重复。
USE(资源视角):给每个资源排三列——使用率、饱和度、错误。
| 资源 | U | S | E |
|---|---|---|---|
| CPU | 分项使用率 | Load/核、运行队列、上下文切换 | — |
| 内存 | 可用量(反向) | swap 进出速率、缺页率 | OOM 事件 |
| 磁盘 | 忙碌百分比、IOPS | await、队列长度、积压请求 | 介质错误、只读挂载 |
| 网络 | 带宽占用率 | 重传率、丢包、队列丢包 | 网卡错误计数 |
RED(服务视角):速率(Rate)、错误(Errors)、耗时(Duration)。对应上面的 QPS、错误率、P95/P99。
在实际的 Grafana 面板布局中,建议采用这样的梯度:
第一行:核心健康度(RED 三项)—— 一眼知道服务好不好用
第二行:资源总览(USE 四资源的使用率)—— 知道机器是否吃紧
第三行:饱和度细节(Load、await、swap 进出、重传)—— 定位方向
第四行:分项下钻(CPU 分项、per-disk、per-interface)—— 找到具体部件
这个梯度遵循的是"从结论到细节":先看有没有问题(第一行),再看哪类资源(第二行),再看严重程度(第三行),最后才是具体是谁(第四行)。 把细节堆在第一屏会导致故障时找不到重点。
八、阈值怎么定:避免固定阈值
这是本文最想讲清楚的一点。大多数监控失效不是因为指标没采集,而是因为阈值定得太差。
固定阈值的问题
"CPU > 80% 告警"这类规则的问题在于:不同机器的基线不同,不同时段的基线也不同。一台平时 CPU 20% 的机器在促销时跑到 60% 可能已经异常了;一台常态 75% 的批处理机器跑到 80% 完全正常。固定阈值的结果是要么天天误报,要么从不触发。
四种更好的方式
方式一:基于动作的阈值。问自己"这条告警触发时我要做什么"。 如果答案是不明确的,那这个阈值就没有意义。反过来推:设定"需要在业务受影响之前处理"作为目标,然后倒推阈值。比如磁盘要在耗尽前 4 小时被告知,就有了 predict_linear 那条规则。
方式二:分位数 / 预测型阈值。 用趋势和预测代替静态比较:predict_linear() 预测几小时后耗尽、用历史同时段的 P99 做上界。
# 当前可用内存低于过去一周同时段的 5 分位数
node_memory_MemAvailable_bytes < quantile_over_time(0.05, node_memory_MemAvailable_bytes[1w] offset 1w)
方式三:同比与环比。
- 同比:今天 vs 上周同一天同一时刻。适合有周期性(工作日/周末、白天/夜间)的业务。
- 环比:现在 vs 过去 1 小时。适合发现突变。
# QPS 同比上周同一时间下降超过 30%(可能是故障)
sum(rate(http_requests_total[5m]))
/ (sum(rate(http_requests_total[5m] offset 1w)) + 1) < 0.7
这条规则能捕捉到"服务彻底不可用"以外的一类问题:半死不活的状态——机器没宕但大量请求失败、或者入口出了问题导致流量暴跌。这恰恰是最容易被漏掉的场景。
方式四:多级阈值 + 持续时间。
| 级别 | 条件 | 通知方式 | 期望响应 |
|---|---|---|---|
| P3 / info | 轻度异常 | 记录,不通知或日报 | 不处理,仅用于趋势分析 |
| P2 / warning | 明显异常持续较久 | IM 群 | 工作时间内处理 |
| P1 / critical | 业务已受影响 | 电话/短信 | 立即响应 |
每一级都要配对应的"持续时间",级别越高持续越短但不为零(P1 可以 2 分钟,P2 至少 10 分钟)。
阈值要定期回看
建议每个季度做一次告警回顾:把过去三个月触发过的告警列出来,按"是否真的需要处理"分类。从不需要处理的,调低敏感度或直接删除;应该处理的却没触发的,补上规则。告警规则是活的资产,不是设完就完。
九、监控的是症状,不是原因
最后讨论一个思维方式的问题,它决定了你在故障时能不能快速定位。
所有的监控指标,本质上都是"症状":CPU 高了、响应时间涨了、错误率上去了、磁盘满了。它们告诉你出事了,但不告诉你为什么。
举个典型的链路:
用户投诉慢 → RED 发现 P99 从 200ms 涨到 3s,错误率 2%
↓ 但没有 CPU/内存/磁盘/网络告警
↓ 继续下钻:应用层的连接池使用率指标显示 100%
↓ 原因:数据库连接池被慢查询占满,新请求在排队
↓ 下钻到中间件层:发现某条 SQL 的平均执行时间从 5ms 涨到 800ms
↓ 真正的原因:昨晚上线的一个查询没有走索引,数据量增大后全表扫描
这条链路上,RED 指标暴露了症状(响应变慢),但如果监控只到"CPU/内存/磁盘"那一层,你根本不会发现问题——因为机器确实没问题。真正定位到原因,靠的是分层指标体系加逐步下钻。
所以有几个推论:
第一,监控的层数越多,MTTR(平均修复时间)越短。 只有基础设施层,你得靠猜;有应用层连接池指标,你能秒锁定方向。
第二,要监控"因果链上的每一环",而不只是两端。 上面的例子里,"连接池使用率"就是中间的那环——它既不是纯资源指标也不是纯业务指标,但恰恰是最关键的定位点。
第三,不要期望一条告警能直达根因。 合理的目标是:告警告诉你"出问题了 + 大致方向",然后靠面板下钻找到具体原因。试图用一条超级复杂的 PromQL 直接算出根因,通常是过度设计。
第四,保留现场数据比告警本身更重要。 告警只在你睡着的时候响一次,而历史数据在排障时能被反复查阅。这就是为什么要在告警面板里附上 Dashboard 链接,为什么保留期要足够长,为什么用 Linkifying dashboard 的做法有价值。
常见问题
该监控多少指标?是不是越多越好?
采集上可以尽量多(成本低、排查时有用),但告警必须克制。合理规模是几百到几千个采集的指标,配 15~20 条核心告警规则。其余指标的作用是"下钻时提供上下文”,而不是被动等着触发通知。判断标准很简单:这条告警触发时,值班的人知道下一步干什么?不知道就别配。
CPU 告警阈值到底设多少合适?
不要用一个固定的百分比。建议组合条件:使用率 > 85% 且持续 10 分钟以上。对延迟敏感的业务可以更严(比如 Load/核数 > 1),批处理机器可以更松甚至不设。真正该优先设的是饱和类告警(Load/核数、队列长度),它们比使用率更早反映问题。
为什么我的监控全绿但用户说网站打不开?
最典型的几种原因:一是监控只覆盖基础设施层,没有应用层指标;二是监控本身部署在被监控的网络里,网络分区时监控一起挂了(需要做外部探活);三是 DNS、证书过期、第三方接口这类不在常规指标里的问题。建议至少加一条外部 HTTP 探活(从不同网络位置的 blackbox_exporter 打过来),以及证书过期告警。
平均值和 P95 该用哪个做告警?
用 P95 或 P99,不要用平均值。平均值会被大量快速请求掩盖掉长尾。如果想两者兼顾,可以平均值看总体、P99 告警。告警设在 P99 上,能更早发现"一小部分用户已经开始受不了"的情况。
告警总是半夜响,怎么处理?
先看是不是假阳性:绝大多数半夜告警都是因为阈值太敏感(尤其是用了瞬时值而不是持续 N 分钟)。加上合理的 for 持续时间和夜间的差异化阈值(很多告警系统在夜间可以放宽非核心告警)。真正的值班告警(P1)才应该半夜打电话。然后用抑制规则把同一个根因的多条告警合并成一条。最后,定期回顾哪些告警从来不需要半夜响应,把它们降级。
小公司只有一两台服务器,这么复杂的体系有必要吗?
不需要全做,但几件事值得做:装一个轻量监控(Netdata 或 node_exporter + Grafana)采集基础指标;配至少三条告警(主机失联、磁盘将满、外部 HTTP 探活失败);日志做集中或者至少做好轮转。这个规模下,"有没有"远比"全不全"重要。等业务规模上来了再逐步扩展到分层体系和 Alertmanager 路由。
总结
- 筛选指标的三原则:能触发行动的才告警、优先选饱和类而非使用率类指标、每个告警都要知道异常时下一步做什么。一套几十台机器的监控,核心告警应控制在 20 条以内。
- CPU 看四项:分项使用率(us/sy/wa/st 分开才有诊断价值)、Load(除以核数)、运行队列长度(比 Load 更敏感的瞬时排队)、上下文切换率。不要设"CPU > 60% 告警",那是噪音的来源。
- 内存的核心判据是 MemAvailable 和 swap 进出速率(pswpin/pswpout 持续非 0 才是问题),而不是使用率。swap 占用 10~20% 但趋势平稳经常是正常的内存管理行为,要用增长率而非绝对值判断。
- 磁盘要分开监控容量和性能:容量看空间、inode 使用率(耗尽时
df -h还有空间但会报 No space left on device)和predict_linear的耗尽预测;性能看 await 和队列长度,%util 在 SSD 上参考价值下降。 - 网络不能只看带宽:包速率(pps,小包洪水时带宽不高但 CPU 已吃紧)和重传率(超过千分之一就会明显拖累吞吐)比带宽更早出问题。应用层必看 RED 三项,响应时间必须用 P95/P99 而不是平均值。
- 用 USE 组织资源面板(使用率/饱和度/错误),用 RED 组织服务面板(速率/错误率/耗时)。面板布局遵循从结论到细节:先健康度、再资源总览、再饱和度细节、最后分项下钻。
- 阈值要避免固定数字:优先使用基于动作的阈值(问"触发时我要做什么")、预测型阈值(
predict_linear)、同比(对比上周同时刻)与环比(对比过去 1 小时)、以及分级+持续时间(P1 用 2 分钟,P2 至少 10 分钟)。每个季度回顾一次告警规则,删掉从不需要处理的。 - 记住监控的是症状不是原因:分层指标体系的价值在于把 MTTR 缩短——告警告诉你出事和大致方向,靠面板逐层下钻(RED → 连接池/队列 → 慢 SQL)才能找到真正的根因。同时保留足够的现场数据比告警本身更重要。
企业QQ咨询




