接手这套分布式系统的时候监控基本处于裸奔状态。Prometheus和Grafana是有的但覆盖的只有集群层面的几个常规指标节点存活、总CPU、总内存、网络吞吐。节点内部的性能细节完全是一团黑。真正把我逼到必须补单机监控这一步的是一次线上事故某个节点CPU被打满Prometheus告警了但等我要复盘时Grafana上只有分钟级的聚合数据根本看不出来这波异常是哪个进程先发起、哪个指标先变化、CPU Wait是不是早就开始抬头的。缺了最关键的那几分钟的细节故障复盘就只能靠猜。所以后来我给这套系统重新梳理监控体系时把节点级原始数据采集当成了一件正事来做。本文就从我的实际操作出发讲讲分布式系统监控的整体分工以及我用nmon即nmon监控工具在aarch64架构节点上做逐节点性能采集的整套过程和心得。当时团队里也有人问既然有Prometheus了为什么还要在每台机器上单独跑一个采集工具这个问题的答案其实也正是分布式系统监控里最容易被人忽视的部分——告警层面和取证层面是两回事监控方案不能只解决有没有报警还要解决出事后能不能查清楚。下面我会从监控清单怎么列、nmon这种单机工具在整个体系里扮演什么角色、以及在ARM 64位aarch64环境下怎么把nmon部署成常驻服务、采集到的文件怎么分析一步步讲清楚。1. 接手这套分布式系统后我先列了一张监控分工清单分布式系统监控最容易犯的错就是试图用一个大而全的平台解决所有问题。真正靠谱的做法是先把监控目标按层次拆开每一层用合适的工具而不是一个工具通吃。我给自己列的清单分了三层集群层、节点层、应用层。每层解决不同的问题回答不同的问题。集群层回答整个系统现在健康吗节点层回答某个节点现在具体卡在哪应用层回答用户的这次请求为什么慢。缺了任何一层排查链路都是断的。1.1 三层监控的分工集群层、节点层、应用层下面这张表是我当时梳理的基础框架后面所有工具选型都是围绕它来的监控层级核心问题常用工具典型指标集群层系统整体是否可用Prometheus Grafana、Zabbix节点存活、集群CPU总使用率、内存水位、网络吞吐、告警事件节点层单台机器发生了什么nmon、atop、sarsysstatCPU各项占比、内存、磁盘I/O、TCP连接数、上下文切换、TOP进程应用层业务请求为什么异常SkyWalking、OpenTelemetry、日志聚合链路耗时、错误率、慢SQL、接口P99延迟Prometheus这类工具在集群层做得非常好我到现在也认为它是分布式系统监控的地基。但它有个天然的特性它采集的是一组预先定义好的指标而且是按固定间隔拉取的默认15秒一次落到Grafana里还要再经过聚合。这个精度用来做告警足够了用来做故障根的细节分析远远不够。节点层恰恰是很多人会跳过的。我在梳理时发现如果不做节点级的原始数据留存遇到那些偶发性CPU飙高内存缓慢泄漏磁盘I/O抖动类问题时基本只能等下一次复现。而nmon这类单机工具的价值就是在一台机器内部做高频采样把CPU、内存、磁盘、网络、进程级别的数据按秒级间隔原原本本记录成文件放着备用。应用层则是业务可观测性的范畴和本文关系不大但我要强调三层必须齐头并进只做任何一层都会在关键时刻抓瞎。1.2 节点层工具选型时为什么nmon被留到了最后节点层可选的工具不少atop、sysstatsar、nmon是三个主流。当时我也纠结了一番。atop的功能很强支持进程级历史回放但它是一个常驻daemon需要安装完整软件包在某些精简的ARM系统上依赖库不一定齐全。sysstat是老牌工具sar采集的数据可靠同样需要安装一堆依赖而且默认采集项偏内核统计进程级细节要靠额外配置。nmon不一样的地方在于它是一个静态编译的单个二进制文件不依赖Python、不依赖动态库、不需要安装守护进程。下载下来、给个执行权限、跑起来就直接采。它背后是由IBM工程师Nigel Griffiths发起并持续维护的开源项目提供了x86、ARM、PowerPC、S390等各个架构的预编译版本。对于我这套有大量aarch64节点的分布式环境这种零依赖、单文件、多架构的特性简直是救命稻草。所以nmon反而成了我最后拍板要上的工具。它不替代Prometheus也不替代atop它替代的是节点上裸奔、没有本地性能历史这个空白。2. 为什么集群监控已经就绪我仍坚持在每个节点上跑nmon这一节我得先把一个反直觉的观点讲清楚Prometheus的告警覆盖面越广你对节点级原始数据的需求反而越强烈。因为告警只是告诉你出事了而故障复盘真正需要的是从正常到异常每个指标是怎么一步步变化的。2.1 Prometheus告诉你去哪里nmon告诉你发生了什么举一个我实际遇到的例子。那次线上事故Grafana上的集群总CPU曲线从10%突然拉到90%告警响了值班同事第一时间去查。但集群层的图表粒度是分钟级的打开看只能看到一条陡峭的上升线完全看不到上升之前有没有先兆。是哪个进程先起来的是CPU计算密集还是系统调用频繁内存有没有同步变化这些信息Prometheus的Node Exporter默认指标里其实也有但一是存储周期有限二是聚合后精度丢失三是查起来要写PromQL远不如直接翻原始采样记录来得快。nmon做的就是把每个节点的性能数据按秒级间隔写成本地文件。CPU_ALL这一行会记录User%、Sys%、Wait%、Idle%四个细分值Wait%一旦明显抬头基本可以判断问题出在磁盘I/O等系统等待上而不是单纯的运算密集。单凭这一项就能把排查方向从CPU坏了修正为存储链路有问题省下一整天的冤枉路。在我看来Prometheus解决的是监控的广度——覆盖所有节点和核心指标保证第一时间发现问题nmon解决的是监控的深度——在某个节点上留下完整、原始、秒级的性能档案保证事后能还原真相。两者不是替代关系是互补关系。特别是分布式系统里节点几十上百台如果你每台机器上都有一份完整的历史档案复盘任何一次故障都会轻松很多。2.2 nmon的运行模型一个静态二进制如何完成高频采样很多人第一次拿到nmon会有点懵就一个二进制文件双击或者说执行起来怎么就能持续采集那么多指标其实原理并不复杂。nmon本身内置了对Linux各种统计接口的读取逻辑通过读取/proc下的各种文件来拿到CPU、内存、磁盘、网络、进程等数据。它有两种工作模式交互模式直接执行./nmon进入全屏字符界面按c看CPU、按m看内存、按d看磁盘、按n看网络适合临时登录服务器快速看一眼当前负载。采样模式用-f等参数让它后台运行周期性采数据并写入文件适合做长期采集留档。我日常用的是第二种。采样模式下nmon会按你指定的间隔秒和次数持续取样所有数据按行写入一个.nmon文本文件。文件是可读文本不是二进制这意味着即使身边没有任何分析工具你也能直接用grep、awk去翻数据这一点在排查问题时非常管用。它采集的原始数据里会包含不少格式标签。注意文件里的这些行CPU_ALL开头的行整机CPU汇总细分User、Sys、Wait、Idle等。MEM开头的行内存使用情况包括物理内存、交换分区、缓存。DISKBUSY开头的行每块磁盘的忙闲百分比。NET开头的行每块网卡的读写速率。TOP开头的行占用资源最高的进程列表。这些就是后续所有图表工具的输入。理解了这一层你就能明白数据分析只是手段原始数据才是资产。nmon带给你的不是现成的报表而是一份你可以反复翻查的时间序列原始样本。3. aarch64节点落地nmon从下载到systemd托管我的这套分布式环境里有一部分节点是ARM架构的服务器系统是64位的也就是aarch64。真正动手部署nmon之前我建议你先花一分钟确认一下节点架构否则后面的功夫可能白做。另外说一句很多人搜索的时候会把架构名写成arrch64我一开始也看懵了。其实实际架构名是aarch64官方叫法AArch64指的是ARM的64位执行状态。你在Linux上执行uname -m看到的输出通常是aarch64。搜索词里出现arrch64多半是手误或者搜索引擎的联想纠偏。不管怎样认准aarch64就行。3.1 先确认架构aarch64和arrch64其实是什么部署前我习惯先跑三条命令uname -m # 查看机器架构aarch64就对了 getconf LONG_BIT # 看系统位数64就是64位 cat /proc/version # 顺带看内核版本有些老内核可能影响nmon某些指标uname -m输出aarch64getconf LONG_BIT输出64这样的节点就可以放心选择nmon的aarch64版本。如果输出是x86_64那就用x86_64对应的nmon版本。注意不要只看发行版名称很多ARM设备的发行版和x86版长得一模一样但架构完全不同下错二进制文件会直接报cannot execute binary file。另外提醒一点嵌入式ARM板子有些是32位的uname -m会输出armv7l之类那属于armhf或armel分支和aarch64不通用。我这套环境里全是64位ARM服务器所以只关注aarch64。3.2 在ARM节点上把nmon跑起来完整命令记录nmon的官方发布渠道在SourceForge上项目名是nmon。进入下载页面后你会看到类似nmon16n.tar.gz这种打包文件里面包含了各个架构的预编译二进制。我的标准部署流程如下整个过程大概三分钟# 1. 建目录下载并解压 mkdir -p /opt/nmon cd /opt/nmon # 假设压缩包已经下载到 /root 目录 tar zxvf nmon16n.tar.gz ls -la解压后你会看到很多文件命名风格类似nmon_aarch64、nmon_x86_64_centos7、nmon_s390x等。这时候选出我们需要的# 2. 把aarch64对应的二进制复制到系统目录并授权 cp nmon_aarch64 /usr/local/bin/nmon chmod x /usr/local/bin/nmon # 3. 验证版本能打印出版本号就说明二进制可用 nmon -V第一次建议先用交互模式跑一下输入./nmon看字符界面能不能正常刷新。能进入界面后按c、m各看一眼确认CPU和内存数据正常然后按q退出。这一步能提前暴露某些老内核上的兼容问题。确认没问题后就可以正式用采样模式采集了# 4. 后台采样每5秒采一次共12次1分钟数据存到/var/log/nmon同时记录TOP进程 mkdir -p /var/log/nmon nmon -f -s 5 -c 12 -m /var/log/nmon -t这条命令的参数含义我整理了一下后面调参数时用得着参数含义示例-f后台采样模式数据写入文件-f-s采样间隔单位秒-s 5表示每5秒一次-c采样次数-c 12表示采样12次-m指定输出目录-m /var/log/nmon-t额外记录TOP进程信息-t-F指定输出文件名-F node01_0517.nmon执行完后去/var/log/nmon目录看一眼会生成一个类似主机名_日期_时间.nmon的文件比如node01_250228_1500.nmon。用head -20看一眼内容如果能看到CPU_ALL、MEM这些标签行说明采集已经正常工作了。3.3 用systemd把它变成常驻服务别让人肉起停监控工具最怕的就是忘了开。重启一次机器nmon没有自动拉起来故障就正好发生在空窗期你连哭的地方都没有。我会用systemd管理nmon让它在开机时自动启动并且崩溃后自动拉起。网上不少教程推荐用crontab的reboot方式也能用但systemd能提供更细的进程管理和重启策略我这边统一的习惯是用unit文件。在/etc/systemd/system/nmon.service写入如下内容[Unit] Descriptionnmon data collector for node Afternetwork.target [Service] Typeforking ExecStart/usr/local/bin/nmon -f -s 30 -c 2880 -m /var/log/nmon -t Restarton-failure RestartSec30 [Install] WantedBymulti-user.target然后执行systemctl daemon-reload systemctl enable nmon systemctl start nmon systemctl status nmon这里有个很关键的细节我在Type这里踩过坑nmon加上-f参数后主进程会fork一个子进程去后台写数据然后自己退出。如果这里写成Typesimplesystemd会认为服务进程已经退出再配合Restarton-failure就会反复拉起多个采集进程导致数据文件混乱。所以要看清楚Type必须写成forking这样systemd会等待父进程退出并确认子进程在跑服务状态才是正常的。上面的参数-s 30 -c 2880算出来的是一次采集任务的总时长30秒x2880次86400秒正好24小时。nmon在执行完-c指定的次数后会自动退出这意味着用systemd托管时它其实是一个会自然结束的服务。对这个问题我的处理方式是unit里不追求常驻而是靠一个系统自带的timer或者crontab让它每天自动重新拉起。比如加一条crontab# 每天0点重新拉起nmon开始新一天的采集 0 0 * * * /usr/bin/systemctl restart nmon这样每天都会生成一个新的.nmon文件存档按天分隔既方便管理也避免单个文件无限增大。4. 分析.nmon文件的两个思路命令行快查与图表复现数据采下来了光躺在磁盘里没用关键是怎么快速提取出有效信息。我平时分析nmon文件分两种情况故障发生时争分夺秒直接用命令行翻原始文本平时做复盘或写报告则会把.nmon文件转换成图表。4.1 不装任何依赖怎么快速用grep和awk定位问题先说说命令行快查。nmon文件是纯文本所以很多Linux原生的文本处理工具都能直接上。比如要看整机CPU的变化规律直接抓CPU_ALL行grep CPU_ALL /var/log/nmon/node01_250228_1500.nmon | head -20输出的每行大致是T0001,CPU_ALL,30.5,2.1,5.6,61.8这种结构前一个数字是采样序号后面分别对应User%、Sys%、Wait%、Idle%。如果你发现Wait%一直高而User%不高那基本可以断定瓶颈在I/O等待CPU还在空转等磁盘或者网络。再看内存抓MEM行grep ^MEM /var/log/nmon/node01_250228_1500.nmon | head -20内存行包含物理内存总量、可用量、缓存量、交换分区用量等。如果发现可用内存一路走低而缓存一路走高就要考虑是不是有进程在做大量文件读写占用了page cache。要算一个时间段内的平均值结合awk就行。下面这条命令计算所有CPU_ALL记录的平均User%、Sys%、Wait%grep CPU_ALL /var/log/nmon/node01_250228_1500.nmon | awk -F, {sum1$3; sum2$4; sum3$5; n} END {printf User: %.2f%%, Sys: %.2f%%, Wait: %.2f%%\n, sum1/n, sum2/n, sum3/n}注意nmon文件里各行的字段顺序可能随版本略有变化我建议你先用head看一眼实际列位置再写awk别生搬硬套。如果还想定位是哪个进程在消耗资源看TOP行grep TOP /var/log/nmon/node01_250228_1500.nmon | head -30TOP行会记录占用CPU百分比最高的那几个进程的信息。虽然粒度比perf差很多但作为第一层过滤工具绰绰有余。4.2 把.nmon变成图表用nmonchart和nmon_analyser命令行快查适合自己排查但给团队同步结论时图表直观得多。nmon生态里有两个常用的图表方案nmonchart和nmon_analyser。nmonchart是一个ksh脚本把.nmon文件转成一个自包含的HTML文件里面用JavaScript画好了CPU、内存、磁盘、网络的时间序列图。用法也不复杂先把脚本和依赖准备好# 装kshnmonchart依赖这个shell yum install -y ksh # 进入nmon目录假设脚本在 /opt/nmon/nmonchart cd /opt/nmon ksh nmonchart /var/log/nmon/node01_250228_1500.nmon /tmp/node01_250228_1500.html生成完的HTML文件直接用浏览器打开全是一张张时间序列曲线CPU四项、内存、磁盘I/O、网络吞吐按时间轴对齐非常适合做故障时间线比对。这个方案完全离线运行不依赖外网内网环境也走得很顺。另一个方案是nmon_analyser它是一个Excel宏工具运行在Windows或macOS的办公电脑上。你需要把.nmon文件从服务器拷贝到本地然后在Excel里打开nmon_analyser启用宏选择文件它会自动分析并生成一套详细报表包含平均值、最大值、TOP进程排名等格式长得比较正式适合直接剪到故障报告里。我的习惯是技术排查优先用nmonchart因为它快、全、在Linux本地就能搞定写正式报告时用nmon_analyser因为它的汇总表和统计项更整洁。两个工具各有各的场合反正都不欠依赖装好一次就能用很久。5. 这套监控方案实测中踩过的坑和取舍再顺的工具落地上也免不了踩坑。这一节算是我在分布式系统节点上用nmon大半年后实打实给后面的运维同学排雷。5.1 采样参数设置不当时文件大小轻松膨胀几十倍我最早在某一台性能测试机上跑过激进参数-s 1 -c 86400也就是每秒采一次连续采24小时。结果第二天一看.nmon文件干到了几百MB。更要命的是nmon高频采集本身会消耗一定的CPU和I/O在严苛环境里这等于给自己加了额外负载。后来我定了这么一套参数策略日常采集-s 30 -c 2880也就是30秒一次一天一归档。故障复现期临时用-s 1 -c 600跑10分钟把现场细节抓下来用完就停。这里的关键思路是区分常态化留存和临时性取证两个场景。日常采集不需要太高频因为绝大多数故障都能通过分钟级数据确定大致时间点只有已经发现异常、需要深挖根因时才值得用秒级间隔做短窗口的密集采样。这样既控制了文件体积也兼顾了采集精度。5.2 时间戳总是差8小时时区问题一定要提前统一有一次复盘我发现nmon数据里的CPU峰值时间和业务日志里的异常时间总是错开一段。后来仔细一看是节点系统的时区设置不统一有的节点是UTC有的是CST东八区而写入nmon文件名和记录的本地时间因此差出了8小时。这个问题看似不大但在分布式系统多节点对账时非常致命。你拿着三台节点的数据画同一张时间线如果它们的时区不统一所有的时间对齐都是错的。我的处理方式是两件事一起做一是把节点时区统一成业务所处时区的标准用timedatectl set-timezone Asia/Shanghai这类命令固定下来二是在systemd的ExecStart里用-F参数给输出文件命名时带上明确的时间标记比如node01_$(date %Y%m%d_%H%M).nmon确保看到文件名就能判断是哪个时段的快照。5.3 虚拟化环境里的磁盘指标会说谎我发现很多云主机上nmon采集到的磁盘I/O数据看起来非常低但业务却明显出现了存储性能问题。原因在于nmon读取的是虚拟机的虚拟磁盘统计这些数值反映的是虚机内看到的I/O而宿主机层面的磁盘竞争、缓存命中率等虚机内部往往是感知不到的。类似的CPU的Wait%在虚拟化环境下也会出现虚高。比如宿主机的邻居在疯狂做磁盘操作你的虚机CPU可能被调度延迟拉高Wait%看起来像是本机磁盘出问题了但实际上是宿主机层面共享存储的瓶颈。所以用nmon分析虚拟化节点时我给自己定了一个原则单看磁盘指标不能下结论必须同时看网络延迟、TCP重传、CPU上下文切换等指标综合判断是虚机内部问题还是宿主机层面问题。这也是分布式系统监控里常说的指标交叉验证单点数据只能提供线索交叉验证才能靠近真相。5.4 systemd托管nmon时进程模型导致的反复重启这个坑我前面稍微提过这里展开说说。最初我把nmon.service的Type写成simple结果启动后systemd认为主进程退出了因为它确实fork完就返回了然后Restarton-failure开始起作用隔几十秒就重启一次服务。这样一来同一时间段可能存在多个nmon进程写文件采集的数据互相交错极其混乱。我把unit改成Typeforking之后问题才彻底解决。如果你在抓这个问题时发现自己ps -ef | grep nmon能看到多个进程不用怀疑十有八九是Type配错了。处理方式就是我前面写的改Typeforking然后systemctl daemon-reload再systemctl restart nmon。这是个小坑但真的能卡住一大半第一次配systemd托管nmon的人。最后再分享一个我自己养成的习惯每台节点的/var/log/nmon目录我会配一个定时任务每天凌晨把前一天的.nmon文件用gzip压缩并按月归档保留90天就清理。Prometheus的存储空间有限很多时序数据过几周就查不到了但nmon的原始文件只要几十MB存上三个月几乎没压力。真遇到那种需要翻一个多月前某次性能抖动记录的场合这批原始文件就是整个监控体系里唯一的监控录像机。这套架构我用了大半年日常故障复盘的效率提升非常明显强烈建议在分布式系统里每个节点都配上。