十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux服务器CPU/内存/GPU监控实战:从nvidia-smi到性能排障

Linux服务器CPU/内存/GPU监控实战:从nvidia-smi到性能排障 我头一回接手那台带RTX 3090的Linux服务器时差点被一张监控截图带沟里去。训练脚本已经起来了nvidia-smi里显存占用显示24GB左右几乎快满了看起来“卡得很有道理”但loss纹丝不动GPU风扇声音也跟没负载一样。后来认真敲了几条命令才发现GPU利用率一直是0%数据全堵在CPU预处理那一环。自那以后我就养成了一个习惯查看Linux服务器的内存、CPU、GPU使用情况不能只看哪个数字高要搞清楚这些数字到底在说什么。这篇文章就把这件事讲透。我会用真正的运维排查视角把free、top、vmstat、nvidia-smi这些常用命令重新过一遍重点不是教命令怎么敲而是解释输出里每个关键指标的含义以及它们在实际定位问题时到底该怎么配合使用。刚入门服务器运维的人能看懂跑了几年深度学习环境但偶尔被机器“卡”得没脾气的人应该也能从中找到几条有用的思路。1. 先把监控动作拆成“看数字、看趋势、找根源”三个动作很多人一上来就想知道“哪个命令最有用”我的回答一直是没有一条命令能独立解决问题。Linux服务器的资源监控本质上要做三层事。第一层是看数字也就是瞬间快照。比如你现在执行一下free -h看到内存还剩多少这是当前状态。第二层是看趋势也就是持续采样。单次的数字太容易骗人内存刚被某个进程释放完你正好看到free那一列变大了这并不代表内存一直宽裕反过来free 显示为0也不等于系统马上要死。只有连续观察几秒甚至几分钟才能判断一个指标是在持续恶化、正常波动还是偶发尖刺。第三层是找根源也就是从系统整体的数字往具体进程和线程去定位。top告诉你CPU高你得继续知道是哪个进程的哪类CPU占用高是用户态计算还是内核态在忙还是在做磁盘等待。没有这层归因前面的数字只是背景噪音。这套三层思路对应到工具选择上是有规律的。看数字用free、uptime、nvidia-smi这类一次性命令看趋势用vmstat、mpstat、dmon这些支持指定间隔持续输出的命令找根源用ps、top的线程视图、pidstat、perf等能精确到进程和线程的工具。后面讲内存、CPU、GPU三块时我都会按照这个思路来组织而不是简单罗列命令参数。2. 内存篇free -h 只是开始available 才最有参考意义2.1 free 输出的正确打开方式判断内存使用情况绝大多数人第一反应就是执行free -h。这个命令简单直接但很多新人会被它的输出误导。看下面这个典型输出$ free -h total used free shared buff/cache available Mem: 31Gi 18Gi 3.0Gi 312Mi 10Gi 11Gi Swap: 8.0Gi 2.0Gi 6.0Gi这里最坑的是第三列free和第六列available。free显示“只有3.0Gi可用”很多第一次看的人会紧张内存是不是不够了实际上系统还有11Gi左右的可用内存系统自己也判断当前内存压力不大。原因在于Linux的内存管理策略是“尽可能用内存”它会主动把空闲物理内存拿去做文件缓存buff/cache这个缓存是可以被回收再利用的。所以在实际判断中不要盯着free那一列要看available。available是内核基于剩余内存和可回收缓存估算出的“在不触发swap的前提下还能给新进程分配多少内存”这才是更贴近真实余量的数字。再强调一遍available才是决策依据free那一列只适合用来快速瞥一眼不适合作为评估标准。如果哪天你看到available持续走低哪怕free显示还有几个G也说明系统已经开始紧张了。因为这意味着内核发现空闲内存不够正在考虑回收缓存如果回收速度跟不上分配速度下一步就会动用swap再下一步可能就是OOM。2.2 用 vmstat 盯“交换”这个隐性杀手内存的瞬时快照有了还需要看趋势。我的习惯是执行vmstat 1 5意思是每隔1秒打印一次状态连续打印5次。$ vmstat 1 5 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 2 0 2097152 3176800 104856 10485760 0 0 12 35 1220 8800 10 3 86 1 0 1 0 2097152 3168900 104856 10485790 0 0 0 4 1180 8400 9 2 88 1 0 2 0 2097152 3156700 104856 10485820 0 0 0 8 1300 9100 12 3 84 1 0 1 0 2097152 3148800 104856 10485810 0 0 0 2 1250 8600 10 2 87 1 0 2 0 2097152 3141000 104856 10485830 0 0 0 4 1210 8400 11 2 86 1 0重点关注两列siswap in从交换分区读回内存和soswap out把内存页写到交换分区。这两列如果是0或者偶尔出现一个非0值说明内存状态是健康的。如果si和so长时间稳定不为0那就是典型的交换颠簸说明物理内存已经不够用内核不停地在内存和磁盘之间挪页面。因为写swap本质上是磁盘IO这个动作会拖慢整个系统而且对交互式服务的影响特别明显经常表现为“延迟突然飙升”或“服务假死”。排查交换问题的时候不能光看数字还要看谁在制造这种压力。老位置还是得回到进程层面用ps和top找出内存大户。2.3 定位内存大户ps、htop 与 cgroup 视角当系统内存紧张时我会用下面这条命令快速列出当前按物理内存占用排序的前几个进程$ ps aux --sort-%mem | head -20执行之后RES那一列就是进程当前实际占据的物理内存。这个值的大小直接决定谁是内存大头。要注意的是RES里包含进程自己的匿名页和部分文件映射页它不完全等于“程序以为自己在用的内存”但在定位问题时已经是足够准确的参考了。如果嫌ps输出不够直观可以装一个htop按F6选择按MEM%列排序交互体验好很多。再看一眼就能锁定可疑进程。这里还要多提一句容器化环境。现在很多服务跑在Docker或K8s里宿主机上直接看ps往往不够因为进程的RES跨越了容器和宿主两个视角。在宿主机上通过docker stats --no-stream可以快速看到每个容器的内存占用这个命令实际读取的是cgroup的统计更加接近“容器真实配额使用量”。另外很多国产化的麒麟、统信UOS系统也遵循同样的机制cgroup内存统计方法完全通用。2.4 关于 buffer/cache 的常见误区我经常被人问到类似问题“我服务器上16G内存全被buff/cache吃掉了是不是有程序在泄漏内存”这个问题要拆开说。buff/cache里很大一部分是内核在做文件读写时的页缓存也就是把磁盘上读过的文件内容留在内存里下次再读就快得多。这部分内存在系统有需求时会自动回收不算泄漏。所以看到free显示“只剩几百MB”但available还有一半以上时根本不用慌。真正需要留个心眼的是另一类情况cache长时间异常膨胀、无法回落。这里面有一种比较隐蔽的内核态内存增长比如目录项缓存dentry cache和inode缓存出现异常它们记录在slab里。用slabtop可以看到这部分内存的占用情况有些特殊场景下内核模块或文件系统bug会导致slab内存只升不降。遇到这种情况再去考虑调vm.drop_caches或者重启相关服务平时不要动不动就执行echo 3 /proc/sys/vm/drop_caches因为强制清理缓存会短时间拉高磁盘IO可能让正在跑的服务抖一下。3. CPU篇别只盯着百分比load average 和 wa 才是决策依据3.1 top 里的每一列都在说什么CPU这块最常用的入口是top。很多教程会直接说“看%CPU这一列”但真到诊断时我一般先看的是开头三行。top - 14:23:45 up 30 days, 2:10, 3 users, load average: 6.30, 5.20, 4.80 Tasks: 150 total, 1 running, 149 sleeping, 0 stopped, 0 zombie %Cpu(s): 12.5 us, 3.1 sy, 0.0 ni, 83.4 id, 1.0 wa, 0.0 hi, 0.0 si, 0.0 st第一行末尾的load average三个值分别是过去1分钟、5分钟、15分钟的系统平均负载。这个值的含义是在采样时间窗口内处于可运行状态和不可中断睡眠状态的进程平均数。注意它不是CPU占用率而是一个“队列长度”概念。一台物理核数为8的机器load average常年大于8就说明一直有进程在排队等CPU系统是过载的如果load average只是偶尔超过核数通常是短时任务突发可以观察一段时间再说。第三行有us、sy、ni、id、wa、hi、si、st八项。等实际排障时最重要的是三列us是用户态CPU占比sy是内核态CPU占比wa是CPU等磁盘IO的时间占比。wa高说明瓶颈可能在磁盘这时候光看CPU就解决不了问题。st是虚拟化环境特有的一列表示当前CPU时间被宿主机抢占的比例在云服务器上如果st长期很高说明宿主机资源争抢严重这种“卡”在虚拟机里怎么调都没用只能考虑换规格或换实例。3.2 用 lscpu 搞清楚物理核、逻辑核和超线程在看CPU使用率之前必须知道自己这台机器到底有几颗CPU、几颗物理核、几个逻辑核。这个信息直接决定load average多少算高、mpstat -P ALL该解读多少列数据。lscpu会一次性把这些信息全部打印出来。我常用的几个字段是Architecture、CPU(s)、Socket(s)、Core(s) per socket、Thread(s) per core。$ lscpu Architecture: x86_64 CPU(s): 32 On-line CPU(s) list: 0-31 Thread(s) per core: 2 Core(s) per socket: 8 Socket(s): 2这个例子是一台两路服务器。每个Socket有一颗物理CPU每颗CPU有8个物理核每个物理核有2个超线程所以逻辑CPU总数是2×8×232个。在top里看到的“32核”指的其实是32个逻辑处理器而不是32个物理核。对于计算密集型任务逻辑核之间共享物理核心的计算资源所以32个逻辑核的实际吞吐能力通常达不到32个物理核的水平。搞清楚这个结构的价值在于load average的判断阈值要以逻辑CPU数量为基准但同时要知道超线程对吞吐的帮助有限不能指望靠超线程把纯计算性能翻倍。3.3 按 CPU 维度拆分负载mpstat -P ALLtop看的是整个CPU汇总状态肉眼已经很难看出某些“偏核”问题。比如系统整体看起来只有20%占用但某个CPU核已经打满这种不均衡在小并发场景和中断密集场景下非常常见不拆开看是看不出来的。mpstat -P ALL 1可以每秒打印每个逻辑CPU的使用状态$ mpstat -P ALL 1 12:00:01 AM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 12:00:01 AM all 10.50 0.00 2.50 0.50 0.00 1.00 0.00 0.00 0.00 85.50 12:00:01 AM 0 65.00 0.00 10.00 0.00 0.00 2.00 0.00 0.00 0.00 23.00 12:00:01 AM 1 2.00 0.00 1.00 0.00 0.00 0.00 0.00 0.00 0.00 97.00如果看到某个CPU核的%soft明显高于其他核多半是网络收发或磁盘中断集中打到了同一个核上。早期的网卡驱动没有开启多队列时这种现象特别常见。之后可以进一步看/proc/interrupts来确认中断号分布必要时通过调整RPSReceive Packet Steering或修改中断的CPU亲和性把中断分散到多个核上。3.4 定位到线程级top -Hp 与 pidstat -t确认某个进程CPU占用高之后还剩最后一个问题是哪个线程消耗的CPU。比如Java应用、Python多线程任务、数据库线程池进程下面几十上百个线程定位到线程级别才能去对应的日志或代码里查问题。top -Hp 可以查看进程内的线程视图。配合top运行时按P键按CPU排序很快就能抓到最吃CPU的那个线程TID。抓到了之后记住这个TID把它转成十六进制$ printf %x\n 17600 44c0然后在jstack、gdb或对应语言线程转储里搜索这个十六进制线程ID就能对应到具体代码路径。如果没有交互环境还可以用pidstat -t -p 1它会持续输出这个进程下每个线程的CPU使用率适合写进脚本里做记录。这比单纯看top更适合事后复盘因为你能拿到一段时间内的趋势数据而不是只有一个瞬态快照。4. GPU篇nvidia-smi 的每一列都是一个独立信号4.1 显存占用高不等于GPU在干活GPU是本文的重头戏。现在的深度学习、大模型微调、推理服务基本上都离不开NVIDIA显卡而nvidia-smi是绕不开的监控工具。但我见过太多人被这个工具的输出骗过。最典型的误导就是开头我提到的那个场景显存占满了任务却没在跑。为什么会出现这种情况因为模型权重、优化器状态、中间激活值一旦加载进显存就会一直占着哪怕计算已经停了显存占用也不会掉。nvidia-smi里的Memory-Usage和GPU-Util本来就是两个维度的指标前者是“模型占了多大地方”后者是“计算单元现在有多忙”。显存占用高只能说明模型的显存消耗大不能说明计算正在推进。4.2 nvidia-smi 输出逐字段解读执行nvidia-smi后典型的输出大概是这样的结构$ nvidia-smi ----------------------------------------------------------------------------- | NVIDIA-SMI 515.65.01 Driver Version: 515.65.01 CUDA Version: 11.7 | |--------------------------------------------------------------------------- | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | || | 0 NVIDIA GeForce RTX 3090 Off | 00000000:01:00.0 Off | N/A | | 42% 58C P0 198W / 350W | 24192MiB / 24576MiB | 47% Default | ---------------------------------------------------------------------------拆开看几个关键字段Temp、Perf、Pwr:Usage/Cap温度和功耗曲线是判断卡是否“真正在干重活”的很好参照。一张在跑的卡功耗会明显爬升比如3090训练时能到300W以上推理时也会在几十到两百多瓦之间波动。如果显存占用很高但功耗只有二三十瓦温度也在迅速回落基本可以断定计算没有在跑。Memory-Usage显存使用量上面已经说了它只代表占用不代表算力负载。GPU-Util这列是GPU计算核心的利用率。它也不是万能的对图形渲染类任务可能不准但在CUDA计算场景下一个模型只要在稳定训练这列一般能维持在较高的水平。如果它频繁地在0%和100%之间跳说明计算是在突发的短任务更多时候是数据供给不上。Volatile Uncorr. ECC这是显存ECC纠错相关的错误统计正常的H100、A100这类企业级卡在一般情况下这列应该是0。出现非0值说明显存出现过不可纠正错误这对长时间训练来说是个隐患值得用nvidia-smi -q -d ECC查看详情。4.3 动态监控多卡watch、dmon 与 gpustatnvidia-smi本身是一次性命令但可以用watch让它持续刷新$ watch -n 1 nvidia-smi这适合人肉盯屏但看久了眼睛疼而且没法留痕。多卡环境下我更推荐nvidia-smi dmon它输出的是一行行紧凑数字非常适合持续采集$ nvidia-smi dmon -s mpucv -d 1 # gpu pwr gtemp mtemp sm mem enc dec mclk pclk # Idx W C C % % % % MHz MHz 0 198 58 - 47 98 0 0 7500 1695 1 25 42 - 0 0 0 0 650 210这里的sm是流式多处理器利用率mem是显存控制器利用率enc/dec是视频编解码单元占用。对深度学习和推理任务来说sm列是最值得关注的。如果sm偏低说明计算单元没吃满瓶颈在别处如果mem一直很高说明任务在频繁访问显存数据交换量大。gpustat是另一个特别好用的工具基于nvidia-smi封装能一眼看到所有GPU上的进程和用户名$ gpustat -i 1在一台多人共用的GPU服务器上gpustat直接能告诉你哪块卡上跑着谁的什么任务比反复执行nvidia-smi看进程友好太多。nvtop则是htop风格的全屏工具适合交互式查看装好后按逻辑键可以按显存或利用率排序。4.4 深度学习场景中GPU利用率低的原因与定位GPU利用率不高的原因我在PyTorch、TensorFlow、PaddlePaddle环境里都用不同方式排查过排在第一位的永远是CPU数据供给不足。训练框架喂数据是异步的GPU算得快CPU预处理跟不上训练循环就只能等着。最常见的问题包括Dataloader的num_workers设得太低甚至保持默认0数据增广写在CPU上做得太重磁盘是机械盘读图片或读record文件成了瓶颈。这个场景下你观察GPU-Util会看到持续的波动比如在0%、30%、80%之间来回跳同时看系统的CPU占用会发现us并不高但跟数据加载相关的进程在忙。第二个常见问题是模型太小或batch size开得太小。GPU并行度不够每个batch算得太快反而是Python侧调度和kernel启动的开销占了主导。遇到这种情况GPU-Util会很低但sm的波动周期非常短。第三个是进程级串行问题典型的是在同一张卡上跑了多个任务但每个任务本身的GPU内存已经不够导致反复执行显存分配和释放甚至触发OOM重试。这种也能从dmon输出里看到pwr、sm断断续续的毛刺。如果是刚装好驱动和深度学习框架可以先用PyTorch打通一条最简单的检查路径import torch print(torch.cuda.is_available()) print(torch.cuda.device_count()) print(torch.cuda.get_device_name(0)) x torch.randn(10000, 10000, devicecuda) y torch.matmul(x, x) torch.cuda.synchronize() print(y.device)只要这段能跑通说明驱动、CUDA、PyTorch版本之间的链路没问题。很多时候GPU利用率低其实不是硬件问题而是运行环境不对程序根本没调到GPU上。4.5 容器与非 NVIDIA 平台的补充GPU服务器上跑容器现在越来越普遍。容器里执行nvidia-smi是否能正常显示取决于Docker运行时是否支持GPU。启动容器时要带--gpus all比如$ docker run --gpus all --rm nvidia/cuda:11.7.1-base-ubuntu20.04 nvidia-smi如果没带这个参数容器里是看不到GPU设备的。排查容器内GPU不可用问题时先确认宿主机nvidia-smi正常再确认容器运行时有nvidia runtime。非NVIDIA平台比如AMD显卡或国产GPU监控命令各有不同。AMD显卡对应rocm-smiIntel显卡对应xpu-smi或intel_gpu_top。这些工具的输出字段和nvidia-smi类似主要看利用率和显存占用但生态成熟度不一样很多模型框架的适配程度也不同。除非你有明确的非N卡需求否则优先还是把NVIDIA这套环境弄明白。5. 排障实战CPU、GPU、内存都显示正常但服务器就是卡5.1 第一件事看 D 状态进程和磁盘 IO“CPU不高、内存够用、GPU也没满但整个服务器就是卡”这是排查群里出现频率最高的问题。我一般不会先去怀疑CPU而是先找D状态进程。D状态指不可中断睡眠状态通常是进程在等磁盘IO或内核资源。一个长期停留在D状态的进程不仅自己卡住还可能阻塞其他依赖它的任务。top输出里如果Tasks一行出现好几个D状态或者cpu那行wa非0马上接着看iostat$ iostat -x 1 5重点关注%util和await。%util接近100%说明磁盘基本满负荷await很高说明每个IO请求都在排队。这种场景常见于机械盘做日志存储、数据库频繁刷盘、或者有人在后台跑大量小文件的打包解压。解决方案比较简单粗暴把高IO负载迁到SSD或者错峰执行。5.2 上下文切换与中断风暴排除了磁盘问题以后下一个嫌疑是上下文切换异常。内核在进程和线程之间频繁切换时CPU会花大量时间在调度上而不是干活上整体表现就是“机器很忙但什么实际进展都没有”。vmstat里的cscontext switch列可以看到每秒上下文切换次数。如果cs长期维持在几十万级别就需要看看是不是线程开得太多或者在跑大量小任务。再配合mpstat -P ALL看某个核的%soft特别高那基本是中断了。可以用cat /proc/interrupts看各中断号在不同CPU上的分布也可以用perf top快速采样内核热点看是哪个驱动占用了大量CPU时间。我遇到过一种很隐蔽的情况某个网络驱动不支持多队列所有网卡收包中断都集中在一个核上这个核被打满syscal读取大量CPU时间但整体CPU利用率只有十几。这种如果只看top的汇总数据是定位不到的必须mpstat拆开看。5.3 swap 颠簸与 NUMA 跨节点访问内存这块我也把它放进“卡顿追凶”清单里不是因为内存占用高而是因为内存分配不合理。先看swap。free -h看到swap used持续增长时说明有一些不活跃的进程内存被换到磁盘上了。如果某个进程需要访问这些已经被换出的页面就得从磁盘读回来这个IO延迟比内存访问慢好几个数量级。这个场景在负载平稳时看不出来一旦有流量或计算突发性能损耗立刻放大。临时缓解可以调低swappiness$ sysctl vm.swappiness10但要强调这只是缓解治本是加内存或排查内存泄漏。再一个容易忽略的是NUMA架构。多路服务器上每颗CPU有自己直连的内存访问远端CPU上的内存比访问本地内存慢。如果进程被调度到CPU0但内存却分配在CPU1的本地内存上性能损耗可达20%甚至更高。用numactl --hardware可以看节点分布用numastat可以看当前的访问统计。如果跨节点访问不平衡可以通过numactl --cpunodebind0 --membind0绑定进程到指定CPU和内存节点。5.4 一次完整排查的复盘拿一个真实的场景复盘一下。去年有一台8卡A100的服务器跑分布式训练时卡得厉害。nvidia-smi看每张卡都是满显存GPU-Util却不停抖动system load也不高内存available充足。第一步我先排除了显存不够的问题因为GPU-Util不是持续为0说明计算在断断续续推进。第二步看IO趋势用iostat发现共享存储盘的%util高得离谱。这台机器挂载的是网络存储模型权重保存和日志写入频繁触发网络IO。第三步用pidstat -d确认正是训练进程在向网络盘写入checkpoint时整个数据加载链路被卡住GPU在等数据回传。最后把checkpoint路径从网络盘改到本地SSD再定期同步到网络盘做备份问题就消失了。整个过程跳过了单条命令的局限从卡顿现象出发通过D状态进程、IO等待、进程行为三条证据链锁定根因。6. 把常用命令串成一个“一眼诊断”组合6.1 综合命令推荐单兵作战时效率很重要我不会一个命令一个命令地敲。以下几组命令值得记下来基本覆盖80%的服务器资源排查场景。第一组系统总览一条命令看全内存、负载、CPU汇总状态$ free -h uptime top -bn1 | head -15第二组按CPU和内存排序看进程$ ps aux --sort-%cpu | head -10 $ ps aux --sort-%mem | head -10第三组看趋势持续采样CPU、内存、上下文切换$ vmstat 1 5 $ mpstat -P ALL 1 5第四组看磁盘IO和具体进程的IO$ iostat -x 1 3 $ pidstat -d 1第五组看GPU一次性输出利用率、显存、功耗和进程信息$ nvidia-smi $ nvidia-smi dmon -s mpucv -d 1 $ gpustat这些命令组合起来一个完整的资源轮廓基本就出来了。再深挖时再上perf、strace、numastat这些重型工具。6.2 一个亲测有效的诊断顺序如果你现在接手一台“怎么这么卡”的服务器而且不想东一榔头西一棒子可以按下面这个顺序走。先跑free -h和uptime确认内存余量和load average判断是不是最常见的内存或负载过载。再看top的前三行特别是wa和st如果wa高直接查磁盘st高直接在云平台侧看宿主机健康状况。CPU百分比正常但整体卡接着mpstat拆核找是否存在单核打满。进程级再用ps排序和top -Hp锁定可疑对象。如果是GPU服务器不要只看nvidia-smi的显存一定把GPU-Util、功耗、温度三个字段一起看再连着执行几秒dmon看计算是否稳定。最后实在找不到头绪用iostat和pidstat -d检查IO层。这个顺序不会漏掉最常见的瓶颈也不会在前期浪费过多时间。熟练之后整套动作几分钟就能完成。排查服务器资源这件事我一直觉得更像破案而不是看仪表盘。命令输出只是散落的脚印真正有价值的是你根据脚印还原出“系统内部到底发生了什么”的判断过程。把free、top、vmstat、nvidia-smi这几个基础工具吃透比装一百个花哨的监控面板都管用。尤其是在GPU服务器上跑深度学习任务的人学会区分显存占用和计算利用率很多看起来玄学的卡顿其实就是一层窗户纸的事。
返回列表