线上应用突然卡顿、CPU飙到 90% 却一时说不清瓶颈到底在 CPU、内存还是磁盘 IO 时,我最先敲下去的命令往往不是 top,而是 nmon。nmon(Nigel's Monitor)是一款老牌的 Linux 综合监控工具,单文件、无依赖、部署极简,打开终端就能同时看到 CPU、内存、网络、磁盘、文件系统、进程甚至内核运行队列的数据。它既能在交互界面实时观察,也能以后台采集模式把指标落盘成 .nmon 文件,供事后用 Excel 模板或图表工具分析。这篇文章就是我在服务器排障、压测摸底、嵌入式 ARM 设备(AArch64,社区里常写作 arrch64)上多年用 nmon 的完整经验汇总,适合刚接触 Linux 运维的人,也适合手里攒了一堆 .nmon 文件但不知道怎么高效利用的老手。
1. 在监控矩阵里 nmon 的位置:为什么应急排障时我总先敲它
1.1 和 top、sar、Prometheus 这些工具比,nmon 强在哪
现在监控栈越来越重,Prometheus 加 Grafana 几乎是标配,但真到了单机应急那一两分钟,nmon 仍然是最顺手的工具。top 只能交互式看进程,sar 虽然能回溯历史但默认没开,Prometheus 要部署 exporter 还要等抓取周期。nmon 不需要 agent,不需要服务端,不需要往业务容器里塞探针,一个二进制文件扔上去就能跑,数据格式又是纯文本,后面想用 awk、Python 二次处理都很方便。
不过 nmon 解决的核心问题不是“长期趋势”,而是“当前这台机器到底哪一项先被打满”。它能在同一个滚动界面里把 CPU、内存、磁盘、网络全部铺开,并且随着刷新实时变化。我记得有一次压测,业务方坚持说数据库连接池不够,我打开 nmon 看了一眼磁盘 BUSY 指标直接飙到 85%,再按一下 t 看进程,发现其实是日志同步线程在刷盘,根本不是连接池的问题。这种多维度同时可见的特性,正是 nmon 不可替代的地方。
1.2 轻量、可移植、跨架构,决定了它的适用范围
nmon 的轻量体现在两个层面。一是运行时开销低,正常情况下对 CPU 的消耗几乎可以忽略不计;二是部署成本低,二进制拷贝过去就能执行。到了嵌入式 Linux 或者国产化环境,资源紧张、没有包管理器或不能随便联网安装时,nmon 依然能用,我用过它在 ARM64 的小盒子、兆芯 x86 工控机上跑过,只要 /proc 虚拟文件系统还在,nmon 就能拿到 CPU、内存和磁盘的数据。
我在 ARM 设备上有一个很深的体会:很多监控工具在 x86 上有现成包,一到 AArch64 就要重新编译,而 nmon 官方一直提供多架构构建,就算默认源里没有,源码也只有一个 C 文件,交叉编译成本很低。对于嵌入式 Linux 场景,比如 CNC 控制设备、PLC 网关这种机型,nmon 是少数能在老内核上稳定跑起来的监控工具之一。这也解释了为什么这么多年过去了,nmon 依然活跃在 Linux 常用命令清单和运维面试题里,因为它解决的问题太实在。
2. 从安装到验证:包管理器、静态二进制与 ARM64 适配
2.1 各大发行版的安装套路
多数发行版仓库里都有 nmon,名字就叫 nmon,不需要额外加第三方源。
- RHEL、CentOS、Rocky、AlmaLinux 这类系统,先确保 EPEL 仓库可用,然后执行
sudo dnf install epel-release,再执行sudo dnf install nmon。如果网络环境受限,也可以直接找静态编译版本。 - Ubuntu、Debian 更简单,
sudo apt update && sudo apt install nmon即可。 - openSUSE 系用
sudo zypper install nmon。 - 常规 Linux 服务器如果没法装包,直接从官网或可信镜像下载编译好的二进制,chmod +x 之后放到 /usr/local/bin 就能跑。我自己的经验是尽量用发行版源里的版本,因为构建参数和当前内核匹配度更稳,省去自己折腾的麻烦。
安装完成后验证是否可用:直接执行nmon能打开交互界面,或者执行nmon -f -s 2 -c 5让它采集 10 秒后生成文件,如果当前目录出现 nmon 开头的 .nmon 文件,说明安装没有问题。
2.2 ARM64(AArch64)环境下的编译与坑
在 ARM64 机器上,如果源里没有 nmon,需要自己交叉编译。nmon 源码其实就一个 C 文件,通常还需要一个编译脚本,因为不同操作系统版本、不同内核版本需要定义不同的宏。编译前先确认内核版本号,比如uname -r,如果内核太新,源码里读取 CPU 数量的函数可能需要打补丁,否则会出现 CPU 数据全为 0 或者只显示 1 个核的怪问题。
我踩过的坑是:在 AArch64 小主机上直接拿了 x86 的 RPM 包强制安装,结果是能装上但一跑就报错,提示无法识别平台。正确的做法是找官方构建的 aarch64 版本,或者用源码在目标机器上本地编译,编译过程一般只需要 gcc 和 make。对于没有 gcc 的精简系统,就提前在另一台环境相同的机器上编译好,再拷贝过去,前提是 glibc 版本兼容,否则会提示找不到共享库。遇到这种情况,用静态编译最省心,gcc -static编出来的二进制几乎能在同架构任何发行版上运行,我在嵌入式 Linux 上长期就是这么干的。
2.3 基本运行模式与文件输出验证
nmon 的运行模式大致分三类:交互模式、采集模式、容量估算模式。交互模式直接敲 nmon 即可,适合现场看;采集模式靠 -f、-s、-c、-t 这些参数控制,适合计划任务和夜间压测;容量估算模式用 -x 或 -X,可以在正式采集前预估 .nmon 文件会占多少磁盘空间。
验证采集模式时我习惯这样写:
nmon -f -s 2 -c 10 -t -m /tmp/nmon_test命令含义是:立即生成数据文件,每 2 秒记录一次,一共记录 10 次,总共 20 秒,-t 让数据里带上时间戳,-m 指定输出目录。跑完后检查 /tmp/nmon_test 目录,如果看到类似主机名_日期时间.nmon的文件,并且用head查看能看到以 AAA、CPU001、MEM、NET 开头的行,说明采集链路已经通了。
3. 终端里的实时体检:交互模式按键即战场
3.1 最常用的几个按键和看数据的方式
nmon 交互界面最初显示的是一个概览面板,此时按不同按键,顶部和下半部分会动态叠加对应的指标区,再按一次就会隐藏。我实战中常用的是:
- 按 c,显示 CPU 核心利用率,可以看到每个逻辑核的 User%、Sys%、Wait%、Idle%,还能看到整机平均值。
- 按 m,显示内存,包括总内存、可用内存、缓存、页交换情况,内存泄漏排查主要靠它。
- 按 d,显示磁盘,能看到每块盘的读速率、写速率、busy 百分比,这是定位磁盘瓶颈最快的入口。
- 按 n,显示网络,按网卡分别展示收包、发包速率和错误包计数。
- 按 t,进入 Top Process 排行,类似 top,但可以直接看到每个进程占用的 CPU 和内存。
- 按 k,显示内核统计,比如运行队列长度、上下文切换、swap 换入换出,配合 CPU 的 Wait% 可以判断系统是否在跑 IO 重负载。
- 按 j,显示文件系统挂载点的使用率。
读数据的顺序也有技巧。我通常先按 c 看 CPU,如果 User% 高说明是业务计算密集;如果 Wait% 高说明进程在等 IO;如果 Sys% 高再看 k 里面的上下文切换次数,通常能判断是不是锁竞争导致的内核开销。第二眼再看 d 的 DISK BUSY,如果磁盘 busy 持续超过 70%,基本可以断定 IO 侧有问题。最后用 n 看网络收发包,判断流量是否异常。
3.2 快捷键之外的小细节:刷新间隔和颜色含义
nmon 默认刷新间隔一般是 2 秒,如果想让数据滚动更紧凑,可以用交互模式启动参数指定,比如nmon -s 1表示 1 秒刷新一次。拉高刷新频率适合压力测试时观察瞬时抖动,但屏幕数据跳动太快反而不容易抓住规律,我自己的习惯是压测场景用 1 秒,平时巡检用 2 秒。
颜色方面,不同发行版终端里表现不完全一样,但大体上绿色表示正常范围,黄色接近警戒,红色代表高风险。这块要注意,不要在纯黑底终端和浅色终端之间对颜色作过度解读,关键是看数值而不是看颜色。另外,按 q 退出交互模式,按 h 可以调出帮助页,帮助页会列出当前版本支持的全部按键,记不住按键的时候先按 h 比乱按强得多。
交互模式下还有一个实用技巧:把 nmon 放到 tmux 或 screen 会话里跑,即使中途 SSH 断开,重新连上后数据还在继续刷新。这个习惯帮我避免过好几次“人在现场等数据、网络一抖全白看”的尴尬。
4. 无人值守采集:参数套路与定时任务落地
4.1 采集参数怎么组合才有意义
采集模式的参数是 nmon 最有价值也最容易被用错的地方。核心参数就四个:
- -f:开启文件输出模式,自动生成标准命名的输出文件。
- -s:每次采样间隔,单位是秒。
- -c:一共采样多少次。
- -t:在输出文件里记录每行的时间戳,强烈建议加上,不加的话后面做时间序列分析非常难受。
组合逻辑很简单,总采集时长 = 采样间隔 × 采样次数。比如我想要 5 分钟每 2 秒一个点的数据,就是-s 2 -c 150;想要 24 小时每 5 分钟一个点,就是-s 300 -c 288。这里要特别提醒,别把 -s 和 -c 写反。我见过有人写成-s 288 -c 300,结果变成了 288 秒采一次、采 300 次,总共跑了整整一天,拿到数据后才发现采样密度完全不符合预期。
除了这四个,-m 用来指定输出目录,避免把文件写到根分区;-r 可以在文件头写入一个自定义标签,比如-r 压测0701,分析时更容易识别哪份数据对应哪场测试。如果希望每次采集完立即生成独立文件,-f 本身就会把时间戳带进文件名,不需要额外处理。
4.2 定时采集与文件轮转的最佳实践
生产环境不能总是手工登录去敲命令,我一般用 crontab 在业务低峰期自动采集,比如下面这个模板:
# 每15分钟采集一次,每次持续10分钟(每10秒一个点,共60次) */15 * * * * /usr/bin/nmon -f -s 10 -c 60 -t -m /var/log/nmon如果是为了 7×24 小时长期留底,更好的做法是每小时生成一个文件,避免单个文件过大。比如:
# 每小时的第5分钟开始采集,持续5分钟 5 * * * * /usr/bin/nmon -f -s 5 -c 60 -t -m /var/log/nmon/hourly这里要注意 crontab 用的解释器环境很干净,建议写命令的绝对路径,不要只写 nmon。同时确保 /var/log/nmon 目录存在且有写权限,否则采集出来的文件是 0 字节。
文件轮转同样重要。nmon 文件虽然不大,但长期不清理也会占满磁盘。我习惯用 logrotate 管理 /var/log/nmon 目录,保留 30 天即可。也就是配置 logrotate 规则把 .nmon 文件按天轮转、旧文件自动删除。就我观察,以 5 分钟一个点、24 小时连续采集为例,单文件大概几 MB,30 天量级不会造成压力,但不管不顾让它攒一年,再小的文件也会积累成隐患。
4.3 采集前如何估算文件大小并防止写爆磁盘
nmon 官方给出了一个小工具参数 -x,它可以进入“容量估算模式”,根据当前系统里的 CPU 数量、磁盘数量、内存大小估算默认参数下产生的 .nmon 文件大小,然后直接退出;-X 则是乐观估算,采用更小的平均值。实际使用时,我建议在手工发起长时间采集前,先跑一次nmon -x看看估算值,再决定输出目录所在分区是否够用。
另外需要注意,如果系统磁盘数量特别多,比如有几十块盘做 RAID 或者有大量 LVM 逻辑卷,nmon 文件里每一行数据都会随之膨胀,文件大小会比估算值大不少。所以宁可把输出目录放在一个独立的数据分区,也不要放在根分区/下面,否则采集数据写满根分区会导致系统服务写日志失败,那是非常低级但非常致命的失误。
5. 数据不吃灰:nmon 出图的几种常用姿势
5.1 用 Excel 模板批量生成可视化图表
nmon 采集出来的 .nmon 文件本质上是个文本文件,直接用 vim 或 less 看很不直观,所以业内最经典的做法是用 IBM 退出的 nmon analyser Excel 模板来出图。这套模板是一个带宏的 Excel 工作簿,打开后启用宏,点击界面上的 Analyse nmon data 按钮,选择 .nmon 文件,它就会自动生成包含 CPU、内存、磁盘、网络、TOP 进程等几十张图表的工作表。
我在压测报告中长期用这个方案,理由是它不需要安装额外软件,只要有 Excel 就能出图,而且图表格式、配色非常整齐。需要注意的点有两个:一是启用宏时 Excel 的安全性设置要放开,否则没法运行;二是文件里如果包含大量磁盘数据,分析过程会偏慢,内存小的机器容易卡住。如果是在 WPS 里用,部分版本对 VBA 宏的支持不完整,出图可能失败,建议还是用正版 Excel 或者直接用下面的开源替代。
5.2 nmonchart 与 nmon2rrd:适合 Linux 服务器的自主分析
如果不想依赖 Excel,可以在服务器上直接用 nmonchart,这个工具由 IBM 开发者发布,作用是把 .nmon 文件转换成 SVG 格式的图表,再用浏览器打开。还有 nmon2rrd,它把 nmon 数据灌进 RRDtool 的轮询数据库,适合自己搭一个轻量趋势展示页。
我的实践经验是:单机看报告用 nmonchart 最省事,命令大概是这样:
nmonchart 主机名_20250701.nmon 报告.html生成的 HTML 里已经内嵌了图表,直接下载到本地用浏览器打开就能看,不需要服务器额外装 Web 服务。如果要在多台机器之间汇总对比,可以先把多个 .nmon 文件弄到一台机器上分别转换,再拼成一份汇总文档。不过这些工具在普通发行版仓库里不一定有,常用做法是从官方渠道下载脚本,然后根据本机环境手动配置依赖。
5.3 不借助工具,用 Python 和 awk 快速提取关键指标
有时候我只想要某一时间段的 CPU 平均值,或者抽查某个瞬间的磁盘读写情况,用 Excel 模板反而慢。这种场景我直接写脚本解析 .nmon 文件。
.nmon 文件每一行都有明确的节标识,比如 CPU_ALL 行代表整机 CPU 数据,MEM 行代表内存数据,NET 行代表网络数据。下面这个 Python 脚本可以粗略提取 CPU 整机数据:
import sys def parse_cpu_all(path): rows = [] with open(path, "r", errors="ignore") as fh: for line in fh: line = line.strip() if line.startswith("CPU_ALL,"): parts = line.split(",") rows.append(parts) return rows if __name__ == "__main__": for row in parse_cpu_all(sys.argv[1]): print(row[1], row[2], row[3], row[4])这只是示例,实际分析时需要结合文件头的时间字段,把每一行的采样时间和指标对应起来,再计算均值、峰值。用 awk 也能达到同样的效果,而且不需要装 Python 环境:
awk -F, '/^CPU_ALL/{print $2, $3, $4}' 主机名_20250701.nmon这里我刻意没有给每个字段命名,因为不同 nmon 版本的字段位置略有差异,自己拿到数据后先 head 看一行确认再处理,比照搬网上解析脚本更可靠。这个习惯值得多说一句:任何监控数据的二次分析,第一步永远是用 head 看原始结构,而不是直接套公式。
6. 实战排障场景与避坑清单
6.1 一次 CPU 飚高排查:现场怎么一步步找到真凶
有一回线上服务突然变慢,我登录服务器后先用top看到 CPU 使用率很高,但一时说不清是业务进程还是内核在做冤大头。这时候我敲了nmon -s 1,进入交互模式后同时按 c、k、t 三个键。
第一眼看 c 面板,发现 User% 稳定在 70% 以上,Sys% 只有 6%,这说明主要消耗在用户态业务进程上,基本排除了内核 bug 和上下文切换失控。再按 t 看进程排行榜,果然有个 Java 进程的 CPU 使用率接近 400%,对应 4 个逻辑核。到这里还不能直接断定是这个进程的问题,因为 Java 进程的 GC 线程也会表现为高 CPU。我接着按 k 看内核统计,发现上下文切换数值不算离谱,磁盘 Wait% 也不高,说明不是锁竞争也不是 IO 等待,大概率是真正的计算密集或者 GC 循环。
这样一层层收窄,整个排查过程不到两分钟。排查结束后我没有直接杀掉进程,而是用采集模式留了一份证据:
nmon -f -s 1 -c 60 -t -m /var/log/nmon/case_20250701这份数据后来配合 nmon analyser 生成了 CPU 和 GC 相关图表,用来和开发团队对齐。事后复盘时,我觉得 nmon 最有价值的不是单看某个指标,而是它让 CPU、进程、内核三块数据无缝联动起来,这比一个个命令来回切换高效得多。
6.2 内存泄漏与磁盘 IO 瓶颈分别怎么看
内存泄漏类的故障,nmon 的 m 面板最有说服力。重点不是看内存总数,而是看 Used 是否会随着时间不断爬升,同时 Buffer/Cache 不降。我习惯先用交互模式盯 5 分钟,如果 Used 匀速上涨而业务流量没有同步变化,基本可以判断存在泄漏。为了留下证据,再用采集模式每 5 秒一个点连续采集半小时,拿到 Excel 模板里看 Used 的趋势曲线,比口头汇报有说服力得多。
磁盘 IO 瓶颈的识别要区分两种场景。如果 CPU 面板里 Wait% 高、磁盘面板里 DISKBUSY 也高,说明业务进程在等磁盘完成读写,瓶颈确实在 IO 侧;如果 DISKBUSY 不高但 Wait% 依然很高,就要怀疑是不是进程数太多导致等待队列偏长,也就是 CPU 调度的问题而不是盘的问题。这个区分很关键,因为前者加磁盘就能解决,后者削进程并发或者优化锁更有效。
我在压测环境里见过一个误区:只盯着磁盘的读写吞吐,忽略了磁盘 busy 时间。实际上对机械盘来讲,busy 百分比比吞吐量更能反映饱和程度,因为小块随机读的吞吐可能很低,但盘已经忙得喘不过气了。nmon 的 d 面板里重点看 busy 列,这块经验在实际排障中屡试不爽。
6.3 高频问题速查:从命令找不到到文件时间乱了
我按出现频率整理了一份 nmon 常见问题清单,基本都是我这些年自己被坑过或者在社区里反复看到的问题。
| 现象 | 常见原因 | 解决办法 |
|---|---|---|
| 敲 nmon 提示 command not found | 包没装或 PATH 不包含安装目录 | 用发行版包管理器安装,或用绝对路径 /usr/local/bin/nmon |
| 采集文件是 0 字节 | 输出目录不存在、没有写权限、磁盘满 | 先手动创建目录,检查 du -sh 当前分区剩余空间 |
| 看不到 CPU 数据或只看到 1 个核 | 内核过新与旧版 nmon 不兼容 | 升级 nmon 版本或源码重新编译 |
| .nmon 里的时间超过 24 小时还在涨 | 采集时间超过一天,时间字段变成累加值 | 每次采集不超过 24 小时,跨天任务拆成多个文件 |
| 磁盘很多,文件膨胀快 | DISK 行数据量随磁盘数量增长 | 提前用 -x 估算大小,输出目录放到独立分区 |
| 容器里采集数据缺项 | 容器 /proc 受限,看不到宿主机全部设备 | 尽量在宿主机层面采集,容器内数据仅作参考 |
这里面最容易被忽略的是时间字段问题。因为 nmon 默认带一个从当天零点开始计算的时间戳,如果采集超过 24 小时,HH 部分会继续往后累加,超过 23 也不会归零,外部解析工具很容易误读。解决方案就是任务拆短,或者用 -t 配合外部脚本记录真实开始时间,后期分析时做偏移校正。我个人的习惯是单文件采集时长控制在一小时以内,再通过 crontab 去轮转,这样数据文件小、解析方便、时间也不乱。
另外一个很细节但实用的建议:给计划任务里的 nmon 加-r 标签,比如-r web压测或-r 0701故障,后期把几十台机器的数据混在一起时,能一眼认出每个文件对应的场景,不用再去翻系统日志。这个习惯在看大促压测数据时帮了我大忙。
6.4 采集输出与后续处理的三个独家技巧
最后分享几个比较零散但非常实用的细节。
第一个技巧是采集时加上-I参数,nmon 会把每个进程的累计 CPU 使用情况记录到文件中。默认情况下,nmon 在采集模式里对进程数据的采样频率和 CPU 面板不同,加了-I之后,后续能用 Excel 模板或者脚本把进程排行拉出来,特别适合复盘“哪个进程在什么时间段突然吃满 CPU”。不过代价是文件体积会明显增大,不是每场采集都有必要开。
第二个技巧是利用-^参数限制交互模式显示的 CPU 核心数。当服务器是 32 核、64 核时,交互界面一屏根本看不完,数据会滚动得非常快。用-^ 16可以只显示前 16 个核心的数据,避免重要数值被挤出屏幕。数据文件里仍然保留全部核心的数据,不用担心影响后期分析。
第三个技巧是尽量保持同一批服务器上的 nmon 版本一致。因为不同版本的 .nmon 文件字段格式会略有差异,如果混用多个版本,Excel 模板解析起来可能出现列错位,脚本分析更是容易出错。我一般会在巡检脚本里固定 nmon 版本,升级前先在测试机验证一次,确认生成的文件能被现有分析流程正常读取,再批量下发。听起来很保守,但在大规模服务器环境里,这比用最新版本带来的收益要确定得多。
在实际操作中我还有一个体会:nmon 适合快速取证,但不适合替代完整的监控系统。如果你需要的是长期历史趋势、告警通知、多机聚合,那还得靠 Prometheus、Zabbix 这类平台。nmon 的价值在于,它用最小的成本和最短的时间,把一台机器当前的真实健康状态说清楚,在应急和压测这两个场景里几乎没有对手。平时养成随手开采集的习惯,真出故障时手里有数据,分析起来才有底气。