ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Ubuntu stress 压力测试:CPU、内存与磁盘IO稳定性实战

Ubuntu stress 压力测试:CPU、内存与磁盘IO稳定性实战 1. 为什么在 Ubuntu 上要留一个 stress 在手边装完 Ubuntu 之后多数人第一件事是配源、装输入法、调字体、连 SSH把开发环境捯饬得舒舒服服。但真到机器开始不对劲的时候——风扇狂转、编译卡死、SSH 连不上、docker 起不来——你会发现手里缺一个能把问题复现出来的东西。stress就是这样一个工具它是一个命令行下的系统压力测试工具能按你的要求往 CPU、内存、磁盘 IO 上施加可控的负载用几十行参数就把一台 Ubuntu 推到它的极限边界。它不画图、不友好、参数还带点脾气但在排查硬件稳定性、验证散热方案、测试虚拟化资源配额、给新到的服务器做老化验证这些场景里它是我见过最省事的一把锤子。这篇文章我想聊的是怎么把stress真正用起来而不是照着手册念参数。包括它背后的工作原理、参数该怎么算、什么时候不能用、压完之后看什么指标、以及在 VMware、VirtualBox、WSL 这些套娃环境里跑会有什么不一样。开头面向的是会用 Linux 命令、但没系统做过压测的开发者后面几节会给到更细的实操细节和踩坑记录做运维或者搞硬件选型的朋友也能直接抄作业。先说清楚一件事压力测试stress test的价值不在于把机器跑满而在于让问题暴露在有监控的环境里。你手动打开一个yes /dev/null也能把单核跑满但你不知道它跑了几个核、跑了多久、用了多少内存、有没有触发 OOM。stress把这些变量变成参数能重复、能记录、能对比这才是它比人肉烤机值得用的地方。注意stress只能制造负载不能给你答案。跑之前一定要想清楚我要验证什么否则跑完只有一堆烫手的 CPU 和风扇噪音。2. 内容整体设计与思路拆解2.1 压力测试到底在验证什么很多人对压测有个误解觉得就是把机器搞到最热最卡。实际上一次有意义的压测至少要回答三类问题之一。第一类是稳定性问题这台机器在持续满负载下会不会死机、重启、报 MCE 硬件错误、SSH 掉线。第二类是性能问题满负载时 CPU 频率掉不掉、有没有降频、内存带宽够不够、磁盘 IO 会不会成为瓶颈。第三类是配置问题cgroup 配额够不够、swap 策略合不合理、OOM Killer 会不会误杀你的关键进程。这三类问题对应三种完全不同的压测策略。验证稳定性你要的是长时间、多轮次、反复压然后看日志里有没有异常。验证性能你要的是固定负载下的指标采样最好跑两轮取平均还要跟空闲基线对比。验证配置你要的是故意把内存压超看系统在极限状态下怎么反应会不会杀错人。stress 的定位很清楚它负责制造前两类负载第三类的 OOM 场景它也能压出来但你要自己去看dmesg。它不像fio那样能测磁盘的 IOPS 和延迟分布也不像sysbench那样有完整 benchmark 模型。它的优点恰恰是简单到可以临时起意就用缺点也在这里——它给出的数字不能当性能报告用。2.2 stress 与 stress-ng该选哪个Ubuntu 官方源里有stress也有stress-ng。我第一次用的时候也纠结过后来发现这两个其实是上一代和下一代的关系。对比项stressstress-ng负载种类CPU、内存、IO、磁盘 四类300 种含上下文切换、信号、socket、fork、mmap 等负载精度粗基本是跑满支持百分比、按核分配、方法论选择指标输出无--metrics输出 bogo ops、每秒操作数依赖极轻静态编译都行依赖略多功能多适用场景快速验证、临时烤机、脚本巡检精细基准、多维度压测我个人的习惯是临时验证用stress因为它几乎零学习成本敲一行就跑要做成体系化的测试或者写进 CI用stress-ng因为它的--metrics和--timeout配合起来能直接产出可对比的数据。Ubuntu 上两个都装着也不占什么空间加起来不到几兆。2.3 用之前先把基线数据摸清这一步很多人跳过然后就得到一堆没法解释的数字。压测前你要先记录四个基线值空闲时 CPU 各核温度、空闲时 CPU 频率、可用内存和 swap 用量、磁盘顺序读写的大致水平。Ubuntu 下采集这些很快温度sensors需要lm-sensors包或者直接读/sys/class/thermal/thermal_zone0/temp单位是千分之一摄氏度。频率cat /proc/cpuinfo | grep MHz或者cpupower frequency-info。内存free -h注意看available而不是free。磁盘这个不用太精确df -h看剩余空间hdparm -tT /dev/sda粗测一下就够了。没有基线压测后的数字就是孤立的。比如你压完发现温度到了 78 度这算高还是正常如果你的空闲温度就是 55 度、散热就是普通风冷那 78 度在长期高负载下其实很健康但如果空闲才 35 度压一下就冲到 90 度那说明散热或者硅脂有问题。基线决定了你后面所有判断的参照系。3. 核心细节解析与实操要点3.1 安装三种方式与选择理由Ubuntu 上装stress最省事的方式是 aptsudo apt update sudo apt install -y stress如果你用的是 Ubuntu Server 24.04 LTS 或者 22.04这条命令装到的通常是 1.0.7 版本左右功能已经够用。为什么优先推荐 apt 而不是源码因为压测工具要长时间高频运行二进制稳定性和依赖一致性比版本新鲜更重要。源码编译虽然能拿到更新的版本但configure阶段缺依赖、make出来的二进制在特定 glibc 上出问题的情况我都遇到过排查成本远超收益。第二种方式是源码编译适用于两种情况目标机器不能联网或者你需要一个能拷来拷去的静态二进制。stress的源码很小./configure make两步就能出二进制-static链接的话可以直接扔到别的同架构机器上跑。第三种方式是先确认你的 Ubuntu 跑在哪。如果是 WSL Ubuntu、VMware 虚拟机里的 Ubuntu、或者开发板挂载的 Ubuntu 环境安装步骤一样但压测结果的含义完全不同这个我在第 5 节展开。提示stress的运行进程数受ulimit -u限制。默认一般够用但如果你要把-c开到 32 以上先ulimit -u看一眼否则会 fork 失败报 Resource temporarily unavailable。3.2 四类负载进程的参数全表stress的参数设计很直白-c-i-m-d分别对应 CPU、IO、内存、磁盘四类 worker后面的数字是起几个进程。把这四类吃透这个工具就基本会用了。参数全称作用关键细节-c N--cpu NN 个进程反复做sqrt()运算纯计算不碰内存和磁盘-i N--io NN 个进程反复调用sync()触发脏页回写压的是文件系统层-m N--vm NN 个进程反复 malloc/free 内存配合--vm-bytes控制每次分配量-d N--hdd NN 个进程反复 write/unlink 文件配合--hdd-bytes控制文件大小-t N--timeout NN 秒后自动退出强烈建议永远带上--vm-bytes B每个 vm 进程每次分配的字节数默认 256M可写 4G、80% 等--vm-keep分配后不释放一直占着用来制造持续内存压力--vm-hang N分配后 sleep N 秒再释放控制内存驻留时间--hdd-bytes B每个 hdd 进程写的文件大小默认 1G写满磁盘的元凶--backoff N每个 worker 启动前等 N 微秒避免瞬间爆发让负载爬坡这里有几个设计意图值得说。CPU worker 用的是sqrt()而不是死循环空转早期版本因为编译器优化把计算结果丢掉、导致负载虚高后来通过内联汇编和随机数参与运算来保证真实占用。-i用的是sync()这一点很关键——它不是让你手动dd去写文件而是触发内核把 Page Cache 里的脏页刷到磁盘所以它压出来的是文件系统回写路径这个环节跟我们平时说的磁盘顺序读写不是一回事。-d则是真刀真枪地创建文件、写入、删除压的是目录项和 inode 的操作能力小文件多的时候这个负载特别能暴露问题。3.3 参数计算进程数与字节数怎么定这是最容易拍脑袋的地方。我给出我自己的算法你可以直接用。CPU 进程数先看核数nproc或者lscpu | grep ^CPU(s)。压满就用核数压 50% 就用核数的一半。注意stress的 CPU worker 是整核占用没有占 30%这种细粒度控制。如果你想压 50% 又不想让进程数取整可以用--backoff让一部分 worker 延迟启动或者干脆用stress-ng --cpu 4 --cpu-load 60。内存字节数先看free -h的available然后乘以 0.7 作为安全线。为什么是 0.7因为 Ubuntu 桌面版有一堆常驻服务你不可能把全部内存交给压测。假设available是 8G那么单个 vm worker 的--vm-bytes建议不超过8G / 进程数 * 0.7。如果你就是故意要压 OOM那就把--vm-bytes设成available的 1.2 倍同时另开一个终端盯dmesg。磁盘字节数--hdd-bytes默认 1G-d 4的话同时会占用约 4G 临时空间。压之前df -h看一眼/tmp或者当前目录所在分区留出至少两倍余量。我踩过一次坑在剩余 3G 的虚拟机上跑-d 4直接把根分区写满系统起不来最后进 recovery 删文件才救回来。注意--vm-bytes支持百分比写法但百分比是相对物理内存总量而不是可用内存比如--vm-bytes 80%在 8G 机器上是 6.4G跟available没关系别按错参照系。理解了参数背后的这些细节你就能明白为什么照着别人命令抄经常出问题——每个人的机器内存、核数、磁盘余量都不一样参数必须按自己的环境算。4. 实操过程与核心环节实现4.1 CPU 压测单核烤机到全核满载先做最简单的单核压 60 秒同时开一个终端观察# 终端 A施加负载 stress -c 1 -t 60 # 终端 B观察频率和温度 watch -n 1 grep MHz /proc/cpuinfo | head -1; sensors | grep Core-t 60让 stress 自己在 60 秒后退出这样你不需要记着去 kill。观察时重点看三样频率有没有掉、温度爬升曲线是否平缓、有没有核心温度明显高于其他核心。如果某个核心温度比别人高 10 度以上多半是散热器接触不均或者导热硅脂涂得不匀这在二手服务器上很常见。全核压测就是把-c改成nproc# 压满所有核心持续 5 分钟 stress -c $(nproc) -t 300 --backoff 20000--backoff 20000是让 worker 之间间隔 20 毫秒启动避免同一瞬间所有核心同时冲上去造成电流尖峰。这个细节对笔记本和低端主板有意义——瞬间的电流冲击可能触发电源保护表现为无故重启你还会以为是系统 bug。加了 backoff 之后负载是爬上去的稳定性问题更容易复现成持续高温死机而不是启动即重启。全核压测时我一般另开一个终端跑mpstat -P ALL 2看各核的%idle是不是都接近 0以及%iowait有没有异常。如果压 CPU 的时候 iowait 很高说明有别的进程在抢磁盘这时候温度和频率的数据就不可靠了。4.2 内存压测为什么你的机器会突然卡死内存压测是最容易出事故的一项。核心命令# 1 个 worker每个周期分配 2G分配后驻留 10 秒再释放 stress -m 1 --vm-bytes 2G --vm-hang 10 -t 120--vm-hang 10的作用是让内存被分配后停留 10 秒再释放而不是瞬间 malloc 再立刻 free。为什么要这样因为如果不 hang内存被快速申请又释放Page Cache 和内存分配器能很快回收实际对物理内存的压力是脉冲式的测不出持续占用下的表现。加了 hang内存才真正占住swap 交换和 OOM 才有机会触发。如果你要验证系统在内存极限下的 OOM 行为可以这样# 故意压超 30%观察 OOM Killer 杀谁 stress -m 2 --vm-bytes 130% --vm-keep -t 180--vm-keep让内存分配后一直保留这样压力是持续累加的很快就能把available吃光。这个场景下你要盯的是dmesg -T | grep -i killed process。我曾经用这招发现一台机器的 OOM Killer 优先杀掉了 docker 的 containerd 而不是压测进程原因是 containerd 的 oom_score_adj 更优先被牺牲这个问题直接导致容器服务在内存紧张时莫名退出。内存压测期间用vmstat 2看两个值siswap in和soswap out。如果 swap 分区在机械硬盘上so一高整个系统会卡到鼠标都动不了这不是 Bug是设计如此。所以内存压测建议在 swap 关闭或者 swap 在 SSD 的机器上做否则体验极差还会拖长测试时间。4.3 磁盘 IO 与元数据压力-i和-d这两个参数名字容易混我一开始也分不清。记住一点-i压的是回写-d压的是文件操作。# 压文件系统回写4 个 worker 反复 sync stress -i 4 -t 120 # 压文件元数据2 个 worker 各反复写 512M 文件并删除 stress -d 2 --hdd-bytes 512M -t 120-i单独用的时候如果系统里没多少脏页负载其实不大通常要配合-d一起用先制造脏页再触发回写压力才真实。观察指标用iostat -x 2重点看%util和await。%util接近 100 表示设备饱和await突然飙升说明排队严重。机械盘在-d负载下await到几十毫秒很正常NVMe 如果也这样那就有问题了。我用-d压测最喜欢看的是小文件场景。默认--hdd-bytes 1G写的是大文件目录项操作少。如果你想模拟真实的应用场景把字节数调小、进程数调大比如-d 8 --hdd-bytes 4M这时候 inode 创建删除的密度就上来了ext4 的日志压力会明显增大。这一步能帮你判断是不是该换文件系统或者加noatime挂载选项。提示-d产生的临时文件默认写在当前工作目录。如果你在/根目录执行它会往根分区写。养成习惯压测前cd /tmp或者把工作目录切到一块专门的临时盘上。4.4 组合负载、超时控制与后台托管真实业务不会只有一种负载所以最后一定要练混合场景。比如模拟一台同时跑 Web 服务和后台任务的机器# 4 核 CPU 2G 内存 4 个 IO worker持续 10 分钟 stress -c 4 -m 1 --vm-bytes 2G -i 4 -t 600但混合负载有个陷阱CPU、内存、磁盘会互相干扰。CPU worker 本身不碰内存但内存 worker 的 malloc/free 会占用内存带宽反过来影响 CPU 的运算吞吐。所以混合压测更适合做能不能扛住的定性判断不适合做到底快了多少的定量分析。超时控制上-t是最基本的。但压测有时需要跑几小时甚至一整天这时候要用nohup加后台运行同时记录日志nohup stress -c $(nproc) -t 86400 -v /var/log/stress-cpu.log 21 echo $! /tmp/stress.pid-v打开 verbose会把每个 worker 的创建和退出过程写进日志出问题时能知道是哪个 worker 挂了。记下 PID 是为了方便中途需要时精确停止kill $(cat /tmp/stress.pid)。注意stress会 fork 子进程直接 kill 主进程有时会留下僵死的 worker稳妥做法是pkill -f stress。后台跑一整天之前我建议先做一次 1 分钟的短压确认参数没问题、日志路径可写、磁盘不会被写满。这个习惯救过我一次——在容器里跑长时压测忘了容器有 2G 内存限制结果-m参数一上去就被 cgroup 限制干掉了白等一小时。5. 常见问题与排查技巧实录5.1 报错与异常速查现象可能原因处理方式Resource temporarily unavailable达到ulimit -u进程数上限ulimit -u 65535或减少 worker 数mmap failed/Out of memory--vm-bytes超过可用地址空间或 cgroup 限制调小字节数检查free -h和 cgroup 配额系统卡死、鼠标无响应内存压测触发 swap 大量换入换出关闭 swap 或调小--vm-bytes加--vm-hang压测中 SSH 断连网络栈被 IO 回写或内存压力挤占用nice -n -5提权关键进程减少并发 worker磁盘写满系统起不来-d的--hdd-bytes估算不足进 recovery 删文件之后压测前先df -hCPU 频率上不去功耗墙、温度墙或电源策略限制检查cpupower frequency-info和散热温度异常高但频率很低散热故障或硅脂老化清灰、换硅脂别硬压这张表里的每一条我都实际遇到过。最有意思的是SSH 断连这条。当时在一台 4 核小机器上跑-i 8IO worker 数量远超核数导致kswapd和回写线程把 CPU 全占了sshd 拿不到调度时间片连接就超时了。当时第一反应是网络故障实际是压测把网络相关进程饿死了。解决办法很简单-i的数量别超过核数或者用nice把 sshd 优先级提上去。5.2 结果怎么读判断瓶颈在哪压测跑完不是看个温度就完事要会读指标。我一般按这个顺序看第一步看vmstat 2的r列运行队列长度。如果-c $(nproc)时r明显大于核数说明有进程在排队等 CPU可能是别的服务在抢也可能是 CPU 真的不够。第二步看si/so只要不为 0说明内存已经紧张到开始换页性能会被严重拖累。第三步看iostat的%util和await判断磁盘是不是瓶颈。第四步回到top看%waiowait在总 CPU 时间里的占比。一个典型的CPU 压测但性能不达标的现场是这样%idle接近 0 看着是压满了但%wa有 20% 以上iostat显示磁盘%util90%。这说明 CPU 有五分之一的算力实际是在等磁盘你看到的满载是假的。这种情况下真正的性能瓶颈是磁盘压 CPU 只是在放大这个瓶颈。另外要养成看dmesg的习惯。压测前后各dmesg -T dmesg.before和dmesg.after然后 diff 一下。任何硬件层面的错误——内存 ECC 报错、PCIe 链路降速、thermal throttling 记录——都会在这里留下痕迹。这比看温度曲线更权威。我用这个方法在一台服务器上发现了间歇性的内存纠错错误平时用完全无感只有压测时才偶发出现最后换了内存条才解决。5.3 独家避坑几条没人告诉你但很致命的细节说几条看起来不起眼、但实际会毁掉整个测试的细节。第一条压测前关掉自动更新和后台索引。Ubuntu 的apt-daily、unattended-upgrades、tracker 索引这些东西会在你不注意的时候抢 CPU 和磁盘导致你的压测数据出现莫名其妙的波动。跑长时压测前systemctl stop apt-daily.timer这类操作值得做一遍。第二条笔记本压测请插电源。用电池时很多机器会主动限制功耗你压出来的频率和温度数据完全不能代表真实性能。同样地把电源策略切到 performancesudo cpupower frequency-set -g performance避免按需调频把负载曲线的形状改掉。第三条容器和 WSL 里压测你的指标是虚拟的。WSL2 本质是跑在 Hyper-V 虚拟机里CPU 是宿主机的份额内存上限受.wslconfig里memory参数限制磁盘 IO 走的是虚拟化通道。在 WSL 里跑-c 8可能把宿主机 Windows 也拖卡但你在 WSL 里看到的温度和频率都是读不到的。同理VMware 或 VirtualBox 里的 UbuntuCPU 是宿主的 vCPU 时间片/proc/cpuinfo里的 MHz 是虚拟值不代表物理频率。这些环境下压测只能验证配额够不够验证不了硬件稳定性。第四条压测完记得等系统凉下来再测第二轮。连续两轮压测之间至少间隔 5 分钟让温度和负载回到基线。我见过有人连压三轮第三轮数据全崩最后发现是热积累导致降频跟机器性能没关系。第五条长期老化测试要用脚本轮转不要一条命令跑到黑。写个循环每轮 10 分钟压、2 分钟歇记录每轮的最高温度、频率和是否有报错跑 50 轮。这种脉冲式负载比持续满载更容易暴露接触不良、电源不稳这类间歇性问题。6. 把 stress 用进日常脚本化与工具搭配6.1 一个能直接用的巡检脚本临时敲命令只能解决眼前问题真正省事的是把stress封装成脚本。下面这个脚本我用了很久功能是自动按核数和内存算出安全参数、跑到指定时长、期间采样温度和内存、结束后输出摘要。#!/bin/bash # stress-check.sh —— 单次稳定性巡检 DURATION${1:-300} CORES$(nproc) AVAIL_MB$(free -m | awk /^Mem:/{print $7}) SAFE_MB$(( AVAIL_MB * 70 / 100 )) echo [*] 核数: $CORES, 可用内存: ${AVAIL_MB}MB, 压测时长: ${DURATION}s echo [*] 将施加 CPU 满载 ${SAFE_MB}MB 内存压力 # 后台采样温度 ( for i in $(seq 1 $DURATION); do T$(cat /sys/class/thermal/thermal_zone0/temp 2/dev/null) [ -n $T ] echo $i $((T/1000)) /tmp/stress-temp.log sleep 1 done ) SAMPLER$! stress -c $CORES -m 1 --vm-bytes ${SAFE_MB}M --vm-hang 5 -t $DURATION wait $SAMPLER MAX_T$(awk BEGIN{m0} {if($2m)m$2} END{print m} /tmp/stress-temp.log) echo [] 压测结束最高温度: ${MAX_T}C dmesg -T | tail -50 | grep -iE error|fail|killed || echo [] dmesg 无异常这个脚本的关键设计在于用available而不是free算内存、留 30% 余量、温度采样独立于压测进程、最后自动过滤dmesg。参数用${1:-300}给了默认值直接bash stress-check.sh 600就能跑十分钟版本不用每次改脚本。6.2 和监控工具怎么搭stress本身不采集数据它的价值全靠配合的监控工具放大。Ubuntu 上装一套轻量的组合就够用sysstat提供sar、mpstat、iostat、pidstat能出历史曲线压测前后对比特别方便。htop看实时进程和负载最直观比top好用颜色区分也清楚。lm-sensors管温度sensors -j还能输出 JSON 方便脚本解析。dstat如果装得到能在一屏里同时显示 CPU、磁盘、网络、内存做整体观察很好用。我的习惯是压测前先sar -o /tmp/before.sa 2 30采 60 秒基线压测后再采一段然后用sar -f读出来对比。sysstat默认会持续记录到/var/log/sysstat/但采样间隔通常是 10 分钟对压测来说太粗所以一定要手动指定 2 秒间隔采。如果机器上已经有 Prometheus 和 node_exporter那更省事压测时面板上的曲线会直接告诉你发生了什么。特别是node_cpu_seconds_total和node_pressure_*PSI 指标后者在 Ubuntu 较新内核上能直接给出有多少进程在等 CPU/IO/内存的量化数据比vmstat的r列精确得多。6.3 不同 Ubuntu 环境下的差异化处理最后把环境差异说清楚因为同样的命令在不同 Ubuntu 形态下结果差别很大。物理机上的 Ubuntu 是唯一能做硬件验证的场景。温度、频率、ECC 报错、PCIe 状态这些都有意义。如果你要验证散热、选内存条、判断电源是否够用只在物理机上压。VMware/VirtualBox 虚拟机里的 Ubuntu压测只能验证分配给它的资源够不够。因为 vCPU 是宿主的调度产物内存是宿主的分配IO 是虚拟磁盘。这种环境下适合做容量规划——比如你给一个 Web 服务分 2 核 4G想知道并发上来够不够用在虚拟机里压一下是有参考价值的。但别指望能测出物理机稳定性。WSL Ubuntu 里的压测意义又不同。WSL2 共享宿主内存.wslconfig可以限制内存和 CPU 核数。这里的压测主要用来验证你的开发环境资源配额。要注意 WSL 里nproc返回的是分配给 WSL 的核数不是物理核数-c $(nproc)会把 WSL 的资源占满宿主 Windows 可能会卡但不会崩。开发板上挂载的 Ubuntu 或者嵌入式环境压测要格外小心散热和供电。很多开发板没有主动散热-c 4跑两分钟就降频甚至重启了这属于正常的保护行为。我个人在实际操作中的体会是stress这类工具的价值一半在参数一半在跑之前想清楚要验证什么。同样一条命令想清楚目的的人能拿到结论没想清楚的人只能收获一个烫手的机箱。真正拉开差距的不是你会不会敲-c $(nproc)而是压完以后你会不会去看dmesg、会不会对比基线、会不会把两轮之间的散热间隔算进去。这些小动作看起来麻烦但每一条都对应着一次实际踩过的坑。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进