服务端监控这件事,做久了你会发现一个很拧巴的现实:功能越全的工具,部署和维护成本越高;而真正在事故现场能救命的,往往是那种“扔上去就能跑、跑完就能给出一份完整证据”的轻量工具。Nmon 就是这类工具里我用了最久的一个。它不依赖数据库,不需要服务端和客户端配合,一个几百 KB 的可执行文件拷到机器上,一条命令就能把 CPU、内存、磁盘、网络、进程、文件系统这些指标按固定节奏记录下来,产出一个后缀为.nmon的文本文件。事故过去了三天,老板问“那天下午三点到底卡在哪”,你把文件拖进分析表格里,几分钟就能画出一条完整的时间曲线。这篇内容面向的是需要在生产环境做性能取证、容量评估、故障复盘的运维和开发同学,不管你是第一次听说 Nmon,还是已经在用但只会敲nmon进交互界面看看,下面这些关于采集参数、指标解读、开销控制和踩坑经验,应该都能直接用上。
1. 为什么这类老牌监控工具仍然值得放进工具箱
1.1 从一次“说不清”的卡顿说起
我经历过的最典型的一类场景是这样的:业务方反馈某个时间段接口变慢,但等运维同学登上去的时候,系统已经恢复正常了,top里一片祥和,日志里也没有明显报错。这时候如果没有事前落盘的原始数据,基本上就只能靠猜。有人会说“上监控平台啊”,没错,长期趋势和告警确实应该交给平台,但平台通常有两个短板:一是采集粒度粗,一分钟一个点,几秒钟的抖动全被平均掉了;二是平台记录的是它关心的指标,你事后想看某个维度的时候,往往发现当初没开。
Nmon 补的正是这个缺口。它的定位很明确——单机、高频、全量、离线留存。你让它每 10 秒采一次,它就把那一刻 CPU 每个核的使用率、内存的 free/cached/swap、每块磁盘的读写次数和等待时间、每张网卡的收发字节和丢包、文件系统使用率和 inode、CPU 占用最高的若干进程,全部写成一行行结构化的文本。这些文本不需要任何服务支撑,直接打包带走,在任何一台装了分析工具的机器上都能还原现场。
我一般的做法是:所有核心机器常驻一个 Nmon 采集任务,间隔 60 秒,保留 7 天;遇到压测、上线、割接这种高风险窗口,临时起一个 5 秒间隔的采集任务,跑完就归档。这套组合的成本极低,但关键时刻拿得出证据。
1.2 它到底采集了哪些数据
很多人以为 Nmon 就是“命令行版的 top”,其实差别很大。交互模式确实像 top,但真正有价值的是它的表格输出模式。在这种模式下,它按固定格式把数据分段写入文件,每一段有一个前缀标识,常见的段包括:
CPU_ALL/CPU_P:整体 CPU 使用率拆成用户态、系统态、等待、空闲,以及每个逻辑核的明细MEM:物理内存总量、空闲、缓存、缓冲,以及 swap 的换入换出速率VM:虚拟内存统计,包括缺页、扫描、回收相关计数NET/NETPACKET:每张网卡的字节速率、包速率、错误与丢包DISKBUSY/DISKREAD/DISKWRITE/DISKXFER/DISKSERV:每块磁盘的繁忙度、读写吞吐、每秒 IO 次数、平均服务时间JFSFILE/JFSINODE:每个挂载点的空间使用率和 inode 使用率TOP:按 CPU 排序的进程快照,带进程名、PID、CPU%、内存%SYS_SUMM:系统概要,包括负载、CPU 总数等
这些分段的好处是“按需取用”。你做容量评估,重点看DISKSERV和NET;你查内存泄漏,重点看MEM和TOP;你定位 IO 抖动,DISKBUSY加DISKSERV一起看,基本能判断是磁盘扛不住还是应用发起的请求本身就是随机小 IO。
注意:
TOP段是快照,不是累计值。它只反映采样瞬间占用最高的那几个进程,如果某个进程是“短促尖峰”型,很可能被完全错过。这也是为什么高频采集在小窗口排查中不可替代。
1.3 什么场景该用它,什么场景别硬上
用不用 Nmon,我的判断标准很简单,看三件事:
| 场景特征 | 适合用 Nmon | 建议换别的方案 |
|---|---|---|
| 单机性能取证、故障复盘 | 非常适合,原始的、高频的、完整的数据 | —— |
| 容量规划、资源趋势评估 | 适合,采集一周数据做分布统计 | 需要跨几十台机器做聚合看板时,应交给集中式平台 |
| 压测期间的资源画像 | 非常适合,5 到 10 秒粒度足够 | —— |
| 秒级告警、自动止损 | 不适合,它没有告警能力 | 用采集代理加告警规则 |
| 容器化、弹性伸缩环境 | 需要额外处理,容器内视角不完整 | 用 cgroup 指标为主的方案 |
| 需要跨机上百年历史数据关联分析 | 不适合,文件是分散的 | 落库后用查询引擎分析 |
我在容器环境里的做法是:采集跑在宿主机上,容器内的应用指标另算。原因很实在,容器里看到的/proc是做过隔离和映射的,很多磁盘和网络指标读出来是宿主机的视角,混在一起看容易得出错误结论。
2. 部署之前必须先确认的三件事
2.1 发行版差异与二进制兼容性
Nmon 的安装方式有两种,包管理器装和二进制直放。包管理器最省事,主流发行版的仓库里基本都有,apt、yum、dnf、zypper都能直接装。但这里有个坑:仓库里的版本往往偏旧,而不同版本支持的命令行参数不完全一样。我曾经在一个环境里按文档写了-T参数,结果那个版本不认,进程直接退出,日志里只有一行用法提示,排查了半天才发现是版本问题。
所以我现在的习惯是,装完第一件事先看版本和帮助:
nmon -V nmon -h 2>&1 | head -60-h的输出里会列出当前版本实际支持的参数,以它为准,别照抄网上的文章。
二进制直放适合那些不允许装包、或者仓库里根本没有的环境。做法是从可信渠道拿到对应架构的可执行文件,放到/usr/local/bin下加执行权限。要注意两点:一是架构必须匹配,x86_64 的文件放到 ARM 机器上跑不起来;二是动态库依赖,用ldd看一眼,确认libncurses之类的依赖在目标机上存在,否则交互模式会起不来。静态编译的版本能省掉这个麻烦,体积大一点但迁移性强,我通常优先选它。
# 直放后的基本检查 chmod 755 /usr/local/bin/nmon ldd /usr/local/bin/nmon /usr/local/bin/nmon -V2.2 采集文件的落盘位置与权限
这一条特别容易被忽略。采集文件默认落在当前工作目录,如果你从/root下敲的命令,文件就在/root下;如果从某个业务用户的 home 下敲,文件就在那儿。等到要去找的时候,往往已经忘了当初在哪敲的。
我的规范是:统一落在独立的采集目录,比如/var/log/nmon/,用-m参数指定。这个目录单独规划,理由有三个。第一,采集文件会持续增长,放在系统盘上有把根分区写满的风险,虽然单个文件不大,但按天累积起来不容小觑;第二,独立目录方便做轮转清理,一个find -mtime +7 -delete就搞定;第三,权限清晰,只有采集用户能写,其他人只读,避免误删。
用-m而不是靠cd,还有个实际好处:定时任务里的工作目录是不确定的,靠cd切换很容易出问题,-m显式指定就稳。
mkdir -p /var/log/nmon chown nmon:nmon /var/log/nmon chmod 750 /var/log/nmon如果你的环境里不能新建用户,那就用现有的低权限账号跑。不建议用 root 长期跑采集,虽然读/proc需要一定权限,但绝大多数指标普通用户就能读到,只有极少数进程信息(比如其他用户的命令行参数)会受限。为了多读那几个字段而常年挂一个 root 进程,不划算。
2.3 时钟与时区:事后对时的生命线
这一条是血泪教训。有一次排查一个跨系统的调用超时,我把 Nmon 的时间轴和应用日志的时间轴摆在一起,怎么都对不上,差了整整八个小时。最后发现是采集机时区没配,跑的是 UTC,而应用日志用的是本地时间。那半天时间全浪费在对时间上。
所以部署采集任务之前,务必确认:
timedatectl status date cat /etc/timezone 2>/dev/null集群内所有机器必须统一时区,并且开启时间同步。这件事平时看起来无关紧要,出事的时候就是能不能把两条时间线对齐的关键。我现在会在采集脚本的开头顺手把当前时间和时区写进一个附属说明文件,跟采集文件放在一起,归档的时候一起打包,以后谁拿到这份数据都不用再猜。
3. 第一次跑通:从交互界面到文件输出
3.1 交互模式先摸一遍底
虽然最终目标是自动化采集,但我建议第一次上手还是先进交互界面走一圈。直接敲nmon就进去了,界面是字符版的仪表盘。常用按键我整理成了下面这张表,建议截图存一下:
| 按键 | 作用 | 使用场景 |
|---|---|---|
c | CPU 明细,含每核 | 看是否有单核打满 |
m | 内存与 swap | 判断内存压力 |
d | 磁盘繁忙度与吞吐 | 定位 IO 瓶颈 |
n | 网络接口速率 | 排查带宽打满 |
k | 内核统计 | 看上下文切换、负载 |
j | 文件系统使用率 | 查磁盘写满 |
t | 进程列表,按 CPU 排序 | 找热点进程 |
. | 只显示繁忙的磁盘 | 磁盘数量多时过滤 |
h | 帮助 | 忘了按键就看这个 |
q | 退出 | —— |
交互模式最大的价值是“手感”。你在这上面看一眼磁盘服务时间的量级、看一眼 CPU 等待占比,再看文件的时候就知道哪些数字算正常、哪些算异常。这个基准感是纯看文档建立不起来的。
3.2 一次最小可用的采集命令
跑通交互模式之后,就可以上命令行采集了。最小可用的一条命令长这样:
nmon -f -s 60 -c 1440 -m /var/log/nmon -t拆开解释一下每个参数的含义,以及我为什么这么选:
-f:表格输出模式。这是核心开关,不开这个就只是交互界面。-s 60:采样间隔 60 秒。常驻采集用 60 秒我认为是甜点值,既能反映趋势,文件又不会太大。-c 1440:采样 1440 次。1440 × 60 秒 = 86400 秒 = 整整 24 小时。用次数而不是时长来算,是 Nmon 的设计,所以每次调整间隔都要重算次数,这里很容易出错。-m /var/log/nmon:输出目录。显式指定,不依赖工作目录。-t:记录 TOP 进程。没有这个参数,事后你只能看到“CPU 高”,不知道“谁在搞”。
采完的文件名是自动生成的,格式大致是主机名_年月日_时分.nmon。这个命名规则挺好,天然带主机名和时间,多机归档的时候不会打架。
注意:
-c的含义是次数不是分钟数。我见过有人写-s 60 -c 60想要采一小时,结果只采了 60 分钟——这个例子是对的;但也有人写-s 10 -c 60以为是一小时,实际只有 10 分钟。每次算一遍,别凭印象。
3.3 采集完成后的自检
采集跑完,别急着拿去分析,先做三步自检。
第一步,确认文件非空且有完整头尾:
ls -lh /var/log/nmon/*.nmon head -5 /var/log/nmon/xxxx.nmon tail -3 /var/log/nmon/xxxx.nmon文件头会有几行以AAA开头的元信息,记录程序名、版本、主机信息;数据段前面会有ZZZZ开头的时间戳行,标记每一个采样点。如果头尾都在,说明采集完整。
第二步,确认采样点数量对得上。数一下时间戳行的数量,应该和-c的值接近:
grep -c '^ZZZZ' /var/log/nmon/xxxx.nmon如果数量明显少于设定值,说明采集进程中途被杀了,或者机器重启过。这种情况要在脚本里做补采或者告警。
第三步,确认关键段都在。至少要有 CPU、内存、磁盘、网络这四个:
cut -d',' -f2 /var/log/nmon/xxxx.nmon | sort -u | head -40看一眼输出的段名列表,缺哪个补哪个参数。这套自检我写成了一个小脚本,每次采集结束自动跑一遍,有问题直接发通知,省得等到要用的时候才发现数据是残的。
4. 命令行采集模式的完整拆解
4.1 参数逐项过一遍,别只会 -f -s -c
只用-f -s -c能跑,但会丢掉不少信息。下面这些参数是我实际会用到的,按重要性排:
| 参数 | 含义 | 我的使用建议 |
|---|---|---|
-f | 表格输出模式 | 必开 |
-s N | 采样间隔(秒) | 常驻 60,专项排查 5 到 10 |
-c N | 采样次数 | 总时长 = s × c,每次重算 |
-m DIR | 输出目录 | 必开,统一目录 |
-t | 记录 TOP 进程 | 必开,否则事后不知道谁干的 |
-T | 记录 TOP 进程并包含命令行参数 | 排查时开,能直接看到启动参数 |
-d | 记录最繁忙的磁盘 | 磁盘数量多时开,聚焦热点 |
-K | 磁盘吞吐以 KB/s 为单位 | 数值小时开,避免都是 0.0 |
-N | 包含网络文件系统段 | 有共享存储时开 |
-J | 包含文件系统段 | 关注磁盘写满时开 |
-X | 包含文件系统容量与 inode | 与上一条配合 |
-I | 不采集进程信息 | 进程数量极大、开销敏感时用 |
-g FILE | 自定义磁盘分组 | 磁盘命名混乱、想按业务归类时用 |
其中-T和-t的区别值得单独说一句:-t只记录进程名,-T会把完整的命令行参数也记下来。排查的时候差这一条有时候很关键,比如你看到一堆同名的进程实例,只有带上参数才能区分出是哪个业务在跑。
-K这个小参数很多人不知道,但它能救命。默认磁盘吞吐以 MB/s 为单位,一块低速盘每秒几十 KB 的写入在文件里全是0.0,你根本看不出有没有在写。加上-K之后变成 KB/s,数字立刻有分辨力了。
4.2 采集频率和文件体积的估算
这是必须算的一笔账,不然容易把盘写满或者把机器拖慢。我自己实测的经验值是这样的:
| 采样间隔 | 单次采集时长 | 单机文件体积(约) | 典型用途 |
|---|---|---|---|
| 60 秒 | 24 小时 | 15 到 30 MB | 常驻留存 |
| 30 秒 | 24 小时 | 30 到 60 MB | 重要机器的常驻 |
| 10 秒 | 2 小时 | 8 到 15 MB | 压测、上线窗口 |
| 5 秒 | 30 分钟 | 5 到 10 MB | 精细排查 |
体积和三个因素相关:采样点数、机器上的磁盘和网卡数量、是否开了进程段。磁盘多的机器,DISK*段每行会很长,体积翻倍很正常。所以上面的数字是大致量级,别当精确值用,你可以在自己的环境里跑一天看实际大小再定策略。
按这个量级算,一台机器保留 7 天,占用在 100 到 200 MB 之间,这个成本基本可以忽略。但如果机器有几十块盘、上百个挂载点,就要重新评估了。
4.3 定时采集与开机自启,别用 crontab 硬拼
最常见的做法是写 crontab,每天定时起一个采集进程。这样能跑,但有个明显问题:采集进程会跨越零点。如果你设成每天 00:05 启动、采 24 小时,那么实际覆盖的是 00:05 到次日 00:05,中间有个几分钟的空档;如果设成每 24 小时重启一次,又可能撞上正在采的进程。更麻烦的是机器重启后,那一段数据就永久缺失了。
我现在的做法是用一个常驻循环脚本,采集完成之后自动开下一轮,同时处理好文件名和清理:
#!/bin/bash # nmon_loop.sh - 常驻采集循环 OUT_DIR="/var/log/nmon" INTERVAL=60 COUNT=1440 KEEP_DAYS=7 mkdir -p "$OUT_DIR" while true; do /usr/local/bin/nmon -f -s "$INTERVAL" -c "$COUNT" -m "$OUT_DIR" -t -K # 采集结束后清理过期文件 find "$OUT_DIR" -name "*.nmon" -type f -mtime +"$KEEP_DAYS" -delete sleep 10 done配合 systemd 做守护,比 crontab 靠谱得多:
# /etc/systemd/system/nmon-collect.service [Unit] Description=Nmon collector loop After=network.target [Service] Type=simple User=nmon ExecStart=/usr/local/bin/nmon_loop.sh Restart=always RestartSec=30 Nice=10 IOSchedulingClass=idle [Install] WantedBy=multi-user.target这里有两个细节值得说。Restart=always保证进程挂了能自动拉起来,RestartSec=30避免疯狂重启。Nice=10和IOSchedulingClass=idle是给采集进程降优先级,让它在系统繁忙时自动退让,这一点在压测场景里很重要——你的监控工具不能成为被测系统的一部分。
4.4 归档与轮转:把数据管起来
单个文件管好之后,还要管整体。我的策略是三层:
第一层是本机留存,保留 7 天,用于即查即用。
第二层是集中归档,每天把前一天的采集文件同步到一个归档目录,按月打包压缩。.nmon是纯文本,压缩比很高,通常能压到原体积的十分之一左右。
第三层是异常快照,一旦监控平台发出告警,就自动触发一次高频采集(比如 5 秒间隔跑 30 分钟),单独存到事故目录里。这一层是真正体现价值的地方,因为事后复盘要的就是那几分钟的细节。
# 归档示例:按月打包 ARCHIVE_DIR="/data/nmon-archive/$(date +%Y%m)" mkdir -p "$ARCHIVE_DIR" tar -czf "$ARCHIVE_DIR/nmon-$(date +%Y%m%d).tar.gz" /var/log/nmon/*.nmon归档这件事最忌讳“等要用的时候再找”。我在一次容量评估里,需要三个月前的数据做同比,结果发现早就被清理掉了,只能从头开始重新采一个月。从那以后,归档策略我就写进了标准交付里。
5. 数据落盘之后:如何把数字变成结论
5.1 三种分析路径的取舍
拿到.nmon文件之后,有三条路可以走,各有适用场景:
| 分析方式 | 上手难度 | 输出效果 | 适合谁 |
|---|---|---|---|
| 官方配套的表格分析工具 | 低 | 多张折线图,直观 | 一次性复盘、给非技术同学看 |
| 文本命令行查看 | 低 | 原始数字,无图 | 快速确认某几个点 |
| 自写脚本解析 | 中 | 可定制、可批量 | 批量统计、定期报告 |
表格分析工具是大多数人第一次接触的方式,把.nmon文件拖进去,自动生成十几张图表页。优点是零门槛,缺点是文件大时打开慢,而且它是交互式的,没法塞进自动化流程。
文本命令行适合快速确认。比如只看 CPU:
grep '^CPU_ALL' /var/log/nmon/xxxx.nmon | head -20输出是一行行的 CSV,第一个字段是采样点编号,后面依次是用户态、系统态、等待、空闲、CPU 核数。肉眼看几行就能判断有没有明显异常。
自写脚本是我现在的主力方式。原因很简单,我需要的是“结论”不是“图”。比如“这台机器在过去 7 天里,CPU 用户态超过 80% 的时间占比是多少”、“磁盘服务时间的 95 分位是多少”,这些问题用脚本几行代码就能回答,而看图只能靠眼估。
5.2 一个能直接用的解析脚本
下面这个脚本我用了很久,思路是先把文件读成结构化的行,再按段分组,最后算统计值。你可以直接拿去改成自己需要的指标。
#!/usr/bin/env python3 import sys from collections import defaultdict def parse_nmon(path): """把 .nmon 文件解析成 {段名: [行数据列表]}""" sections = defaultdict(list) with open(path, 'r', errors='ignore') as f: for line in f: line = line.strip() if not line or ',' not in line: continue parts = line.split(',') head = parts[0] # 数据段的格式通常是 BBBB,段名,... if head.startswith('BB') and len(parts) > 1: name = parts[1] sections[name].append(parts[2:]) elif head.isupper() and len(parts) > 1: # 部分版本直接以段名开头,如 CPU_ALL,... sections[head].append(parts[1:]) return sections def summarize_cpu(rows): """统计 CPU_ALL 段的均值与高分位""" user, sys_, wait, idle = [], [], [], [] for r in rows: try: # 字段顺序:采样点, 用户%, 系统%, 等待%, 空闲%, 核数... vals = [float(x) for x in r[1:5]] except (ValueError, IndexError): continue user.append(vals[0]); sys_.append(vals[1]) wait.append(vals[2]); idle.append(vals[3]) if not user: return None n = len(user) user_sorted = sorted(user) p95 = user_sorted[int(n * 0.95) - 1] if n > 1 else user_sorted[0] return { 'samples': n, 'user_avg': round(sum(user) / n, 2), 'sys_avg': round(sum(sys_) / n, 2), 'wait_avg': round(sum(wait) / n, 2), 'idle_avg': round(sum(idle) / n, 2), 'user_p95': round(p95, 2), 'user_max': round(max(user), 2), 'high_load_ratio': round( sum(1 for v in user if v > 80) / n * 100, 2), } if __name__ == '__main__': data = parse_nmon(sys.argv[1]) print('可用段:', ', '.join(sorted(data.keys()))) cpu = summarize_cpu(data.get('CPU_ALL', [])) if cpu: for k, v in cpu.items(): print(f'{k:>16}: {v}')跑出来的效果大致是这样:
可用段: CPU_ALL, CPU_P, DISKBUSY, DISKREAD, JFSFILE, MEM, NET, TOP samples: 1440 user_avg: 23.41 sys_avg: 6.18 wait_avg: 1.02 idle_avg: 69.39 user_p95: 61.77 user_max: 92.30 high_load_ratio: 3.61“95 分位”和“超过 80% 的时间占比”这两个数字,比平均值有用得多。平均值会被大量空闲时间稀释,你会觉得机器很闲,但实际上每天下午有半小时是打满的。容量评估看的是峰值,不是均值。
注意:不同版本 Nmon 的段前缀和数据列顺序略有差异,脚本里的字段位置要按你实际文件调。稳妥的做法是先
head看几行,确认列的含义再写死索引。
5.3 关键指标怎么读,阈值怎么定
这是最需要经验的部分。同样一条曲线,老手看一眼就知道有问题,新手看不出所以然。我把常看的几个指标和判断逻辑列出来:
CPU 的“等待”比“占用”更值得关注。用户态高,说明业务真在算;系统态高,说明系统调用多,可能是频繁 IO 或者锁竞争;等待高,说明 CPU 在等磁盘,这时候加 CPU 是浪费钱,该查的是存储。我的一般判断是,等待占比长期超过 5% 就要看一眼,超过 10% 基本可以确认存在 IO 瓶颈。
内存看的是趋势不是绝对值。空闲内存低不代表有问题,缓存占满是很健康的。要看的是 swap 的换入换出速率,只要这个值持续大于零,说明物理内存真的不够了。另外看TOP段里的进程内存占比,如果某个进程的内存曲线是单调上升、从不回落,那大概率是泄漏。
磁盘看服务时间,不看繁忙度。繁忙度 100% 不代表有问题——现代存储队列深,跑满很常见。真正要看的是平均服务时间,机械盘超过 20 毫秒、固态盘超过 5 毫秒,就需要警惕了。这个数字反映的是“一次 IO 要等多久”,直接对应应用感知到的延迟。
网络看的是错误和丢包,不是带宽。带宽打满是一个渐进的过程,能提前看到;但错误和丢包是突发问题,一旦出现就说明有链路或者配置问题。NETPACKET段里的 error 和 drop 计数,只要在增长就得查。
下面这张表是我自己用的阈值参考,具体数字要结合业务基线调整:
| 指标 | 关注阈值 | 严重阈值 | 说明 |
|---|---|---|---|
| CPU 等待占比 | > 5% | > 15% | 指向 IO 瓶颈 |
| CPU 用户态 95 分位 | > 70% | > 90% | 容量不足或热点代码 |
| 可运行队列长度 | > 2 倍核数 | > 4 倍核数 | 排队严重 |
| swap 换出速率 | 持续 > 0 | 持续增长 | 内存不足 |
| 磁盘服务时间 | > 20 ms(机械) | > 50 ms | 存储响应慢 |
| 磁盘队列长度 | > 2 | > 8 | 请求堆积 |
| 网卡丢包 | 出现即关注 | 持续增长 | 链路或配置问题 |
| 文件系统使用率 | > 80% | > 90% | 有写满风险 |
6. 几个真实场景的排查实录
6.1 CPU 很高,但进程列表里找不到元凶
这是我遇到过最迷惑的一类问题。采集数据显示某个时段 CPU 用户态冲到 90% 以上,但TOP段里所有进程的 CPU 占比加起来还不到 30%。第一次遇到的时候我怀疑是数据有问题,后来才想明白几个可能。
第一,采样错位。TOP段记录的是采样瞬间的进程排名,而 CPU 使用率是一个区间内的平均值。如果负载是短促脉冲式的,两者天然对不上。这种情况要把采样间隔调小,用 5 秒间隔重采一次。
第二,进程被合并统计。有些短生命周期进程频繁创建销毁,在快照里根本抓不到,但它们的 CPU 消耗实实在在发生了。这时候VM段里的进程创建相关计数会有明显升高,可以作为佐证。
第三,虚拟化层的开销。如果是虚拟机,宿主机层面的调度、中断处理会算在系统态里,但不会体现为某个具体进程。这时候看系统态占比,如果系统态明显偏高而进程列表干净,基本可以往这个方向想。
第四,容器视角问题。采集跑在宿主机上,进程列表可能被过滤掉了。这时候要确认采集时用的账号权限是否足够看到所有进程。
排查顺序我一般是这样:先确认采样间隔是否足够细,再看系统态和用户态的比例,最后看VM段的相关计数。三步走完,大多数“找不到元凶”的情况都能有个方向。
6.2 内存“被吃光了”,其实什么事都没有
这个坑新手几乎必踩。看到空闲内存只剩几百 MB,第一反应是内存不够,赶紧加内存或者重启服务。实际上 Linux 的内存管理策略是“能用就用”,页缓存会把空闲内存占满,这是设计如此,不是故障。
正确的看法是看三个数:free、available、swap 使用。available才是应用真正能拿到的内存,它等于空闲加上可回收的缓存。如果available还很充裕,swap没有换出,那就完全不用担心。
真正要警惕的是这几种组合:
available持续下降,同时swap开始换出 → 内存真的不够swap换入换出速率持续很高 → 已经开始影响性能了- 某个进程的常驻内存单调上升,从不回落 → 大概率泄漏
我踩过一次坑:某服务的内存曲线一路向上,我判断是泄漏,准备安排重启。结果仔细看TOP段发现是缓存目录在涨,因为业务在读大量小文件,页缓存自然增长。等内存压力上来,系统会自动回收。这件事让我记住一个原则:在没搞清楚内存被谁占之前,不要急着下“泄漏”的结论。
6.3 磁盘 IO 抖动的定位思路
有一类问题是“每隔十几分钟就卡一下”,这种周期性抖动最烦人,因为持续时间短,肉眼抓不到。Nmon 的高频采集在这种场景下几乎是唯一手段。
我的定位流程是三步:
第一步,看DISKBUSY段,确认抖动是集中在某一块盘还是所有盘。单盘抖动,可能是那块盘的硬件或者 RAID 卡缓存策略问题;全盘抖动,更像是上层应用的一次批量操作。
第二步,看DISKSERV段,确认服务时间的峰值。如果服务时间在抖动期间翻了好几倍,说明是存储侧响应慢;如果服务时间平稳但队列长度上升,说明是请求量突增。
第三步,回到TOP段找时间点上对应的进程。把抖动的时间戳和进程快照对上,经常能锁定是哪个定时任务在跑。
有个细节很关键:时间戳要对齐。ZZZZ行的采样时间是你唯一的对齐依据,所有段的采样点编号是一一对应的。我一般会先写个小脚本把某个时间段的采样点编号提取出来,然后只筛这个范围的数据看,避免被大量无关数据干扰。
# 提取指定时间段的采样点编号 grep '^ZZZZ' xxx.nmon | grep '14:2[0-9]' | cut -d',' -f2拿到编号区间之后,再按编号过滤其他段,这样分析起来干净很多。
7. 常见问题速查与开销控制经验
7.1 问题速查表
| 现象 | 大概率原因 | 处理方式 |
|---|---|---|
| 交互界面打不开,报终端相关错误 | 缺少 ncurses 依赖或TERM设置异常 | 装依赖,或改用-f文件模式 |
| 命令执行后立即退出,无输出 | 参数不被当前版本支持 | nmon -h看支持的参数 |
| 采出的文件很小,段不全 | 参数缺失,或进程被中途杀掉 | 检查参数,加进程守护 |
| 文件名里的主机名是 localhost | 主机名未配置 | 统一配置主机名,便于归档识别 |
| 磁盘吞吐全是 0.0 | 单位是 MB/s,数值太小 | 加-K改为 KB/s |
| 时间轴和应用日志对不上 | 时区不一致 | 统一时区并开启时间同步 |
| 采集进程占用明显 | 间隔太短或进程数太多 | 放宽间隔,或加-I跳过进程段 |
| 分析文件打开极慢 | 文件过大、采样点过多 | 缩短采集时长,或先筛段再分析 |
| 文件里的进程信息不全 | 采集账号权限不足 | 换有权限的账号,或接受缺失 |
7.2 采集本身的开销,怎么压到最低
监控工具拖慢业务是真实存在的风险,尤其是采样间隔很短的时候。我做过几次对比,影响主要来自三个地方:
进程段是最耗的。遍历/proc下所有进程的状态和命令行,在进程数量上千的机器上开销不小。如果只关心系统级指标,直接加-I关掉进程采集,开销能降一大截。
采样间隔是线性因素。5 秒一次和 60 秒一次,开销差 12 倍。所以高频采集只用在短窗口,不要长期挂着。
写文件的量也要考虑。每次采样都会追加写入,如果采集目录和业务数据在同一个盘上,高频写入会干扰业务 IO。我的做法是把采集目录放到独立的盘上,或者至少限制文件增长速度。
另外就是前面提到的降优先级。Nice值和 IO 调度类的设置,能让采集进程在系统繁忙时自动让路。这个配置看起来不起眼,但在压测场景里,有没有它差别很明显。
注意:任何采集工具都会有开销,别相信“零影响”的说法。正确的心态是量化影响、控制影响,而不是假设它不存在。上线前在预发环境跑一轮,对比开采集和不开采集时的业务指标,心里有个数。
7.3 和集中式监控怎么配合
我见过两种极端:一种是什么都靠 Nmon,机器上跑一堆采集脚本,出问题还得手动捞文件;另一种是全面上平台,本地什么都不留,结果平台一挂或者数据被覆盖,彻底抓瞎。
合理的分工是这样的:
集中式平台负责“面”,也就是长期趋势、跨机对比、阈值告警、大屏展示。这些是它的强项,一分钟一个点的粒度做趋势足够了。
Nmon 负责“点”,也就是单机的精细取证。当平台告警说某台机器异常时,Nmon 的本地文件能提供秒级的细节,这是平台给不了的。
两者之间的连接点是告警触发的自动采集。我在告警规则里加了一个动作:一旦某台机器触发高优先级告警,就在那台机器上自动起一个 5 秒间隔、持续 30 分钟的采集任务。这样等你去排查的时候,最关键的半小时数据已经完整落盘了。这个做法的成本几乎为零,但省下的排查时间非常可观。
这套组合我用了几年,评价是:平台让你知道“哪里可能有问题”,Nmon 让你知道“到底发生了什么”。两者缺一不可,但如果只能留一个,我会留 Nmon,因为它给的是原始证据,而原始证据永远不会骗你。
最后分享一个我自己坚持的小习惯:每次处理完一次故障,我会把当时用到的.nmon文件单独归档到一个“案例库”目录,文件名加上一句简短描述,比如“某服务IO抖动”“内存缓慢上涨”。时间长了,这个库就成了我自己的基线参考。下次再遇到类似的曲线,翻出历史案例一对比,判断速度快很多。这件事花不了几分钟,但积累下来的价值远超预期。