简介:面向Linux运维、测试与性能调优人员,提供CentOS环境下stress离线部署与性能压力测试所需的配套资料,重点解决无外网服务器无法直接安装压测工具、依赖不完整的问题。压缩包共43个文件,约46.66MB,以11个rpm依赖包和stress-1.0.4源码文件为主,包含configure、C源码、m4、install-sh等构建脚本,以及readme、news、changelog、texi/info等说明文档,便于完成编译安装、依赖核对和版本确认。目录中同时保留配置检查、版本检查、安装辅助和示例说明等材料,便于在受限网络环境中复用与排错。内容涉及sar命令与Linux性能压测场景,可用于CPU、内存、IO等基础压力实验环境搭建,帮助读者理解离线安装流程、组件依赖关系及监控数据查看方式。对需要撰写压测记录、核对系统负载或整理部署文档的读者也较友好。已有2598人学习下载,适合需要离线部署stress并补齐系统监控、性能验证思路的初中级Linux使用者参考。
1. 版本选择:CentOS 上离线装 stress 1.0.4 前要先认的三件事
前阵子接手一台内网 CentOS 7 机器,最小化安装,yum 源指向本地仓库。要做上线前的容量摸底,顺手敲了yum install stress,回了一行No package stress available,然后就没有然后了。机器不通外网,也不方便临时开通道,只能老老实实走离线安装。stress 这个工具本身小得可怜,一个 C 文件加几个头文件,编出来几百 KB,坑全在环境上:目标机可能没装 gcc;装好的二进制拷到另一台机器,可能报 GLIBC 版本对不上;sudo之后还可能提示找不到命令。这篇就把我在 linux 压力测试环境里反复装 stress 的过程记下来——版本怎么选、依赖怎么补、怎么编、参数怎么配、翻车之后怎么救,以及装完怎么确认它真的在压这颗 CPU。
2. 备料清单:离线机上的 gcc、make 与编译前置核对
离线安装最怕的不是缺某一个包,而是装到一半才发现缺,回来补又得重新走一遍流程。所以我在动手之前会把目标机的情况一次性摸完,把结论记在工单里,后面每一步都对着这份信息走。
2.1 先把目标机的家底摸清楚
同一个 stress 二进制,在 x86_64 上能跑,拿到 aarch64 上就是一句cannot execute binary file;在 CentOS 7 上编好的,拷到 glibc 更老的系统上又会报version GLIBC_2.14 not found。所以离线安装的第一原则是:在哪台机器上跑,就在哪台机器上编,实在不行也要保证架构和系统大版本一致。
# 一次性把关键信息打出来,贴到工单里备查 cat /etc/redhat-release # 系统大版本:CentOS 7.x 还是 8.x uname -m # 架构:x86_64 还是 aarch64 getconf LONG_BIT # 32 位还是 64 位 nproc # 逻辑核数,后面配 --cpu 要用 free -m # 内存总量,后面配 --vm-bytes 要用 df -h /usr/local /tmp /data # 安装目录、压测临时目录的剩余空间 ulimit -a # 进程数、文件句柄、栈大小等限制nproc和free -m这两个数后面会直接进压测命令,先记下来能省不少事。ulimit -a里重点看open files和max user processes,stress 起几十个 worker 时会 fork 大量进程,限制太紧会直接fork: Resource temporarily unavailable。df -h则是为--hdd场景铺路,磁盘写满在压测里是实打实的生产事故。
2.2 gcc、make 与 configure 的核对顺序
很多人以为源码编译必须凑齐 autoconf、automake、libtool 这一整套,其实 stress 用不上。它发布出来的 tar 包里通常已经带了生成好的configure脚本,真正的编译依赖只有 gcc 和 make。
# 编译 stress 真正需要的东西,比想象中少 gcc --version make --version ld --version # 顺手确认 C 库头文件在不在,缺了它 make 会报 stdio.h not found ls /usr/include/stdio.h rpm -q glibc-develld属于 binutils,正常装 gcc 时会作为依赖一起装上;glibc-devel提供stdio.h、stdlib.h这些头文件,编译任何 C 程序都绕不开。最小化安装的 CentOS 往往这三样全无,gcc --version直接command not found。
判断要不要 autotools,解压前就能看出来:
# 看包里有没有现成的 configure,有就不用装 autotools tar -tzf stress-1.0.4.tar.gz | grep -E '(^|/)configure$|Makefile\.in$'命令输出里出现configure和Makefile.in,说明是发布包,./configure && make直接能走;如果只有configure.ac、Makefile.am,那才是开发态源码,得先autoreconf -i,这时候才需要 automake 和 autoconf。我一般优先找前者,省一层依赖。
2.3 缺依赖时用同版本安装镜像搭本地 yum 源
离线机补 gcc,最省事的做法是挂载同版本的安装镜像做本地源,而不是满世界找 RPM 包手工rpm -ivh。手工装 RPM 最难受的是依赖链,gcc依赖cpp、glibc-devel、kernel-headers,一环没对上就卡住;本地源交给 yum 自己解。
# 1) 挂载本机或同版本的安装镜像,文件名以实际为准 mkdir -p /mnt/cdrom ls -lh /opt/iso/ mount -o loop /opt/iso/CentOS-7-x86_64-DVD-xxxx.iso /mnt/cdrom # 2) 写一个只指向本地目录的 repo 文件 cat > /etc/yum.repos.d/local-iso.repo <<'EOF' [local-iso] name=Local ISO baseurl=file:///mnt/cdrom gpgcheck=0 enabled=1 EOF # 3) 清缓存重建,只装编译要用的包 yum clean all yum makecache yum install -y gcc make glibc-devel几个参数值得展开。baseurl=file:///mnt/cdrom这里的三个斜杠不能少,两个斜杠会被当成主机名解析,报Cannot retrieve repository metadata。gpgcheck=0是内网隔离环境的常见取舍,省掉导入 GPG KEY 的步骤;如果环境有合规要求,把镜像里的RPM-GPG-KEY-CentOS-*导入后改成gpgcheck=1更稳妥。yum clean all必须在改完 repo 后跑一次,否则旧的元数据缓存会让新源看起来“不存在这个包”。
装完立刻回头验证,别等到编译时才发现:
# 装完确认三件套齐了,版本号也一起记下来 gcc --version | head -1 make --version | head -1 rpm -q glibc-devel如果环境里已经有别的内网 yum 源,装的时候建议显式限定仓库,避免版本串台:yum install -y --disablerepo="*" --enablerepo="local-iso" gcc make glibc-devel。这一步我踩过一次坑——内网源里的 gcc 版本比本机 glibc 新,装完能编译但链接报错,排查了半天。
3. 编译落地:stress 源码包从上传到 make install 的完整走法
依赖备齐之后,编译本身并不复杂,真正决定成败的是几个细节:包有没有校验、configure的日志有没有留、装完的路径有没有进 PATH。这三件事每一件都能单独写一条踩坑记录。
3.1 上传、校验与解压的目录约定
源码包我一律放/usr/local/src,二进制装到/usr/local/bin,好处是卸载的时候删两个目录就干净了,不用去系统目录里翻。
# 统一放到 /usr/local/src,卸载的时候好找 mkdir -p /usr/local/src cd /usr/local/src # 上传方式按环境选:scp、rz、或者从共享目录 cp # scp stress-1.0.4.tar.gz user@target-host:/usr/local/src/ # 拿到包先看大小和校验值,别急着解压 ls -lh stress-1.0.4.tar.gz sha256sum stress-1.0.4.tar.gz校验这一步在离线环境里格外重要。文件经过 U 盘、跳板机、共享目录几次中转,损坏的概率不低,而 tar 包损坏的表现往往是解压到一半报unexpected EOF,看起来像磁盘问题,实际是包坏了。校验值跟源端对一遍,能省掉后面一大堆误判。
# 先看包内结构,确认顶层目录名 tar -tzf stress-1.0.4.tar.gz | head -20 # 解压并进入源码目录 tar -xzf stress-1.0.4.tar.gz -C /usr/local/src cd /usr/local/src/stress-1.0.4 # 确认 configure 是现成的,还是需要重新生成 ls -l configure configure.ac 2>/dev/nullhead -20那一步是为了确认顶层目录名。有些包解压出来是stress-1.0.4/,有些直接铺平到当前目录,盲写cd很容易进错地方。configure存在就直接用;只有configure.ac就先跑autoreconf -i,这一步会用到 automake,前面没装的话这里就卡住了。
3.2 configure 的参数与 make 的失败信号
configure的参数不多,最常用的是--prefix,它决定make install之后二进制落在哪。
# 标准装法:装到 /usr/local/bin ./configure --prefix=/usr/local # 保守装法:留一份完整日志,出问题直接翻 ./configure --prefix=/usr/local > /tmp/stress-configure.log 2>&1 tail -30 /tmp/stress-configure.log--prefix=/usr/local是默认值,写出来是为了明确;不想污染系统目录的话可以改成--prefix=/opt/stress,之后靠软链接暴露命令:
# 装到独立目录,再用软链接挂到 PATH 里已有的位置 ./configure --prefix=/opt/stress # 后面 make install 之后执行: ln -sf /opt/stress/bin/stress /usr/local/bin/stressconfigure失败时不要只看终端最后一行,真正的信息在/tmp/stress-configure.log或者源码目录下的config.log里,那是排错时最该先翻的记录。常见的失败信号对照如下:
| configure / make 输出 | 含义 | 处理方向 |
|---|---|---|
no acceptable C compiler found in $PATH | 没装 gcc,或 gcc 不在 PATH | 回 2.3 用本地源装 gcc |
C compiler cannot create executables | gcc 在,但链接器或 C 库头文件缺 | 装 binutils、glibc-devel,翻 config.log |
make: gcc: Command not found | configure 阶段用了别的编译器变量 | ./configure CC=/usr/bin/gcc重跑 |
fatal error: stdio.h: No such file | glibc-devel 缺失 | yum install -y glibc-devel |
Permission denied写 /usr/local | 当前用户不是 root,或目录属主不对 | 用 root 执行,或换--prefix到家目录 |
configure通过之后进make:
# 编译量很小,-j4 足够,并发数别超过 nproc make -j4 2>&1 | tee /tmp/stress-make.log # 关键看有没有真正的 error,warning 可以放过 grep -iE 'error' /tmp/stress-make.logstress 源码比较老,编译时出现几条 warning 属于正常现象,比如隐式函数声明、未使用变量,不影响功能;真正要盯的是error和Error 1。tee的作用是把输出同时留一份到文件,方便事后比对——有些环境下终端滚动缓冲区有限,报错一闪就没了,这是血泪经验。
3.3 make install 与 PATH 上的两个隐蔽坑
# 安装,把二进制放到 --prefix 指定的 bin 目录 make install # 装完立刻确认落在哪、能不能跑 ls -l /usr/local/bin/stress /usr/local/bin/stress --version which stress能直接用绝对路径跑通,说明二进制本身没问题;which stress找不到,那就是 PATH 的问题。处理方式有两种,我倾向写profile.d,比改/etc/profile好维护:
# 写进 profile.d,所有登录 shell 都能拿到 cat > /etc/profile.d/local-bin.sh <<'EOF' export PATH=/usr/local/bin:$PATH EOF chmod +x /etc/profile.d/local-bin.sh # 当前会话立即生效 source /etc/profile.d/local-bin.sh第二个坑更隐蔽:普通用户stress --version正常,sudo stress --version却提示command not found。原因是 sudo 读的是/etc/sudoers里的secure_path,跟当前 shell 的$PATH完全是两回事。
# 看看 sudo 用的是哪条 PATH sudo grep secure_path /etc/sudoers # 典型输出:Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin改法是用visudo编辑,把/usr/local/bin拼进secure_path;不想动 sudoers 的话,脚本里一律写绝对路径/usr/local/bin/stress也行。同样的问题在 cron 里会重演一遍,cron 的默认 PATH 更短,定时压测任务里写相对命令几乎是必然翻车。
4. 参数配置:stress 的 CPU、内存、IO 三类压测场景怎么调
装完只是拿到了工具,真正决定压测有没有意义的是参数。stress 的参数不多,但每个都有明确的语义边界,配错方向得到的数据还不如不测。
4.1 CPU 压测:--cpu 的数量与 --timeout 的搭配
# 所有逻辑核跑满,持续 60 秒,打印 worker 进度 stress --cpu "$(nproc)" --timeout 60 --verbose # 只压一半核,观察部分负载下的调度和温升 stress --cpu $(( $(nproc) / 2 )) --timeout 120 # 带 backoff,让 worker 错峰启动,避免瞬间抢满 CPU stress --cpu 4 --backoff 100000 --timeout 60--cpu N的语义是 fork 出 N 个 worker,每个都在做sqrt()浮点运算的死循环,属于纯用户态计算压力,不产生 IO。N 超过nproc并不能压出更高负载,多出来的 worker 只会在运行队列里排队,测到的是上下文切换开销而不是 CPU 算力,所以--cpu一般取nproc或略少。
--timeout N是软定时,到点后 stress 给 worker 发信号,worker 打完当前这一轮循环再退出,所以实际耗时可能比 N 略多几秒。这个参数在压测里是必须留的兜底——我见过有人不加--timeout直接stress --cpu 8,然后开另一个终端干别的去了,回来发现机器已经满载跑了两个小时。
--verbose打开后,启动时会打印一行dispatching hogs: 8 cpu, 0 io, 0 vm, 0 hdd,结束时打印successful run completed in 60s。这行汇总信息很值钱:worker 数跟参数对不对得上,一眼就能看出来。
4.2 内存压测:--vm-bytes 的取值边界
内存场景是三类里最容易把机器压到失联的,因为一旦触发 OOM killer,被挑中的进程不一定是 stress。
# 2 个 worker,每个 malloc 512M,持续 60 秒 stress --vm 2 --vm-bytes 512M --timeout 60 --verbose # 只分配不释放,观察常驻内存持续上涨 stress --vm 1 --vm-bytes 1G --vm-keep --timeout 60 # 分配后挂起,模拟"占着内存不干活"的场景 stress --vm 1 --vm-bytes 1G --vm-hang 30 --timeout 90--vm N是 worker 数,--vm-bytes B是每个 worker 申请的内存,总量是两者的乘积,默认每个 worker 256M。--vm-keep表示申请后不释放、循环复用同一块内存,用来观察 RSS 的真实增长;不加则是反复 malloc/free,产生的是分配器压力。--vm-hang N是申请完睡 N 秒再释放,用来模拟内存被长期占住的形态。
取值上我一般留 15% 到 20% 的余量给内核和 sshd:
# 取物理内存的 70%,两个 worker 平分 total=$(free -m | awk '/^Mem:/{print $2}') per=$(( total * 70 / 100 / 2 )) stress --vm 2 --vm-bytes ${per}M --timeout 60 --verbosefree -m输出的第二行以Mem:开头,$2是 total 列,单位 MB;awk匹配行首是为了避开Swap:那一行。算出来的per是两个 worker 各自的份额,加起来占七成。剩下的三成不是浪费,是留给系统响应你敲命令的空间。
4.3 IO 与磁盘:--io 和 --hdd 别在根分区上跑
# io worker 反复调用 sync(),制造回写压力,不落大文件 stress --io 4 --timeout 60 --verbose # hdd worker 在当前目录反复写、删临时文件,必须有足够空间 mkdir -p /data/stress-tmp && cd /data/stress-tmp df -h . stress --hdd 2 --hdd-bytes 1G --timeout 60 --verbose--io N的 worker 做的是sync()循环,把页缓存刷到磁盘,压力落在回写路径上,不产生大文件,适合观察iostat里的wkB/s和await。--hdd N才是真正写文件,每个 worker 默认 1G,写到当前工作目录,所以必须先cd到专门目录、先df -h .确认空间。在/根分区或者有业务数据的目录里跑--hdd,属于自己给自己制造故障。
跑完检查一下临时文件有没有残留:
# 正常退出会 unlink 临时文件;被 kill -9 则可能留下 ls -lh /data/stress-tmp/把三类场景编排成一个脚本,日常用起来更省事:
#!/usr/bin/env bash # run_stress.sh —— 按场景跑一轮压测,日志落到 /var/log/stress set -u LOG_DIR=/var/log/stress mkdir -p "$LOG_DIR" STAMP=$(date +%Y%m%d-%H%M%S) case "${1:-cpu}" in cpu) stress --cpu "$(nproc)" --timeout "${2:-60}" --verbose ;; mem) stress --vm 2 --vm-bytes "${2:-512M}" --timeout "${3:-60}" --verbose ;; io) cd /data/stress-tmp || exit 1 stress --hdd 2 --hdd-bytes "${2:-1G}" --timeout "${3:-60}" --verbose ;; *) echo "usage: $0 {cpu|mem|io} [size] [seconds]" exit 2 ;; esac 2>&1 | tee "$LOG_DIR/stress-$STAMP.log" echo "exit code: ${PIPESTATUS[0]}"set -u保证引用未定义变量时立即报错,避免空参数把命令拼成stress --cpu;${1:-cpu}是默认值写法,不带参数时默认跑 CPU 场景;tee让输出同时进终端和日志文件;PIPESTATUS[0]取的是管道里第一个命令的退出码,也就是 stress 的真实结果,不加这个拿到的永远是tee的退出码。脚本里用的都是stress相对命令,所以要么 PATH 已经配好,要么改成绝对路径,前面 sudo 和 cron 的坑在这里同样成立。
5. 排错避坑:离线装 stress 时最常撞上的五个现场
离线环境的好处是变量少,坏处是出问题时没有搜索引擎帮你兜底。下面这几条是我在不同机器上真实撞过的,按现场顺序记下来。
5.1 安装阶段的两个硬拦路
现象一:yum install stress回一句No package stress available。
原因:CentOS 的基础仓库里本来就没有 stress,最小化安装的离线环境更没有额外的软件源,所以查不到这个包名。解决:转源码编译,也就是前面 2、3 章的流程;如果环境里恰好有额外源且允许用,yum provides "*/stress"能先确认哪个仓库提供它,但离线机上这条路基本走不通。别在这条命令上反复试,浪费时间。
现象二:./configure报no acceptable C compiler found in $PATH。
原因:最小化安装没有 gcc,或者 gcc 装了但当前用户 PATH 里没有/usr/bin。前者居多。解决:回 2.3 用同版本镜像搭本地源装gcc make glibc-devel;装完which gcc确认一次,再重跑configure。有个细节值得注意——装完 gcc 之后如果还报同样的错,多半是之前configure的缓存没清,删掉源码目录里的config.cache和config.log再跑一遍。
5.2 运行阶段的三个失控现场
现象三:普通用户能跑,sudo stress提示command not found。
原因:sudo 走的是/etc/sudoers里secure_path定义的路径,不含/usr/local/bin。解决:visudo把/usr/local/bin加到secure_path里,或者脚本里统一写绝对路径。同一个坑在 cron 任务里会再出现一次,而且因为 cron 不打印错误,往往只表现为“任务没跑”,排查时容易想歪方向。
现象四:--vm-bytes给太大,SSH 直接断开,机器 ping 得通但连不上。
原因:内存申请总量逼近物理内存,触发 OOM killer,被挑中的进程可能是 sshd 而不是 stress。解决:按 70% 上限取值,务必带--timeout兜底;如果是重要机器,压测前确认有控制台或带外管理通道能进。这类事故没有后悔药,一旦 sshd 被干掉,只能靠控制台救。
现象五:--hdd跑完发现根分区满了,系统开始报各种写失败。
原因:--hdd写的是当前工作目录,默认每个 worker 1G,两个 worker 就是 2G,在/下跑等于拿系统盘做靶子。解决:先cd到专门的压测目录,df -h .确认剩余空间至少是worker 数 × hdd-bytes的两倍;跑完ls确认临时文件已被清理。被kill -9中断时 stress 来不及 unlink,需要手工清理残留的大文件。
5.3 一条能收敛问题的排查顺序
撞上问题先别猜,按这个顺序看:
- 看日志:
configure阶段看config.log,make阶段看make的完整输出,运行阶段看/var/log/messages里有没有 OOM 记录。 - 看进程:
ps -ef | grep stress对一下 worker 数量跟参数是否一致,数量不对说明参数解析就有问题。 - 看资源:
top、vmstat 1、iostat -x 1三条命令能覆盖 CPU、内存、IO 三类现场,先确认压的是不是你想压的那个维度。
6. 验证跑通:三条命令确认 stress 真的在压这颗 CPU
装完能跑出--version不算完,那只能证明二进制能加载。真正要确认的是:参数解释对了、压力确实落在了目标资源上、到点能干净退出。我的验证固定分三步走。
第一步,先用小参数试跑,确认 worker 数和参数对得上:
# 用 2 个 CPU worker 试 10 秒,看 dispatching 那行 stress --cpu 2 --timeout 10 --verbose正常输出里会有一行dispatching hogs: 2 cpu, 0 io, 0 vm, 0 hdd,结尾一行successful run completed in 10s。如果dispatching那行的数字跟你给的参数不一致,说明参数拼错了或者被 shell 展开成了别的值。
第二步,另开一个终端观察实际负载。这一步才是关键,输出好看不代表真在压:
# 分核看 CPU,每个核的 %us 应该同步抬高 top -1 # 或者用 sysstat 里的分核视图 mpstat -P ALL 1 5 # 看运行队列和上下文切换 vmstat 1 5对三类场景分别盯不同指标,参照下表:
| 压测场景 | 观察命令 | 正常表现 | 异常信号 |
|---|---|---|---|
| CPU | mpstat -P ALL 1 | 每个核%usr接近 100,%idle接近 0 | 只有单核满,说明--cpu参数被 shell 吃掉了 |
| 内存 | free -m、vmstat 1 | used明显上涨,si/so变化不大 | si/so大幅波动,说明已经在换页,压过头了 |
| 磁盘 | iostat -x 1 | %util上升,await变大,wkB/s有量 | %util不动,说明目录选错或磁盘是内存盘 |
| 全部 | ps -ef | grep stress | worker 数等于各参数之和 | 进程数对不上,参数没生效 |
第三步,确认退出行为干净:
# --timeout 到点后应该返回 0,没有残留进程和临时文件 stress --cpu 2 --timeout 5 echo $? ps -ef | grep stress | grep -v grepecho $?输出 0 表示正常结束;如果输出 1 或者 124 之类,通常是某个 worker 起的资源没申请到,比如--hdd空间不足、--vm内存被 cgroup 限制卡住。最后那条ps应该只剩你自己,有残留进程说明有 worker 卡在系统调用里没退出来。
从那以后我每次在离线机器上装完任何自编译工具,都会强制走一遍这个三步验证——小参数试跑、另一终端看指标、检查退出码和残留。看起来啰嗦,但它能在正式压测之前把参数理解偏差、PATH 问题和资源限制全部暴露出来,比压到一半机器失联再回头查要省事得多。希望帮到你。
本文还有配套的精品资源,点击获取