做运维这些年,最烦的事就是接手一台新服务器时,脑子里全是问号:CPU能扛多少压力,内存读写有没有虚标,磁盘IOPS能不能撑起数据库,到公网的带宽和延迟到底靠不靠谱。每次拿到一台新机器,我的第一道工序就是把Linux Bench整套跑一遍。这套综合性Linux服务器性能测试与网络质量检测脚本,能一次性把CPU、内存、磁盘、网络这些核心参数摸清楚,让我心里有底,再决定这台机器适合放什么业务。
对运维工程师、独立服务器管理员、还有帮公司做技术选型的人来说,这脚本是最省事的入门体检工具。先用它建立基线,后续调优和故障排查才有参照。老手不用再一条条敲sysbench、dd、ping的原始命令;新手也能通过生成的报告快速理解机器的真实水平。
今天把这套脚本的设计思路、核心模块、跑通流程和踩坑记录完整梳理一遍,有需要的直接照着抄。
1. 为什么需要一套“全能体检”脚本
1.1 一条条命令敲出来的认知碎片
很多刚接触服务器的人习惯这样做:想看CPU就敲lscpu,想看磁盘就敲df -h,觉得机器慢就敲top。这些命令能拿信息,但拿不到“到底行不行”的结论。我见过太多人把机器从云平台买回来,看到配置是8核16G、系统盘80G,就默认这台机器一定飞快。结果真把业务放上去,一个数量不大的数据库查询就能把磁盘拖死。
原因很简单:规格参数说的是“有什么”,性能测试回答的是“能用什么程度”。同样的型号、同样的核心数,在低配虚拟机上和高配物理机上跑出来的性能相差可以非常大。这种差异只能靠统一方法的压测体现出来。我之所以要把测试沉淀成脚本,就是希望每次做体检时用的工具、参数、时长完全一致,这样测出来的数字才能在不同机器之间横向对比。
单点命令还有个问题:输出格式五花八门。lscpu的表格、free的KB/MB混用、df的百分比,一眼看过去抓不到重点。脚本把这些输出统一整理成“模块化报告”,先打系统标识,再分项压测,最后给结论性数字,任何一个人拿起来都能看懂。
1.2 性能与网络一起测的好处
单独跑性能和单独测网速都有现成的工具,但运维场景里这两件事从来都是关联的。一台服务器CPU、磁盘都很强,但网络出口带宽只有5Mbps,那它就不适合当下载站或视频分发节点。反过来,网络带宽很高但磁盘随机读写很弱,跑高并发数据库就是灾难。只有把硬件性能和网络质量放到同一次测试里,才能还原一台机器真实的服务能力。
还有一个现实原因:测试窗口稀缺。云厂商经常有按小时计费的临时机器,或者测试机位需要预约。与其分别在两个时间段折腾,不如用一套脚本在窗口期内把该测的全测完,报告也集中在一份日志中。时间成本是运维里最容易被忽视但最贵的成本。
脚本设计初期,我特意把“一次执行、全部出结果”作为硬指标。整个流程下来大约耗时5到10分钟,正好是一个人泡杯茶、回几条消息的间隙,不用蹲在屏幕前反复操作。
2. 核心功能拆解:脚本到底在测什么
2.1 CPU与内存:压力测试的参数是这么来的
脚本的CPU部分我用的是sysbench。测试分为单线程和多线程两组。
function cpu_bench() { echo "== CPU Benchmark ==" # 单线程 sysbench cpu --cpu-max-prime=20000 --threads=1 run # 多线程 sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run }参数里的--cpu-max-prime=20000不是随手写的。它表示CPU要连续计算出20000以内有多少个素数,这个计算过程没有IO等待,是纯算力压测。数字越大,耗时越长。我试过10000时单线程跑太快,才几秒就结束,结果分辨率不够;30000又太久,测试一批机器时时间成本太高。20000在大部分机型上能跑30秒到2分钟,既有区分度又不会让人不耐烦。
内存测试同样是sysbench:
sysbench memory --memory-block-size=1M --memory-total-size=10G --threads=$(nproc) run--memory-total-size=10G表示申请并读写总量为10G的内存块。很多人以为这是在“占用10G内存”,其实它是分批操作的,实际最大并发占用取决于线程数和块大小,用测试总量控制脚本总时长。这组参数跑出来的events/s和吞吐量,对不同内存质量非常敏感。
一份合理的内存测试结果应该在几次运行间保持很小的偏差。如果某台机器内存测试数字忽高忽低,首先怀疑其他业务抢占资源,其次是内存条的可靠性。我遇到过一台机器,平时内存测试正常,一到深夜跑批任务时就掉速,后来查出来是内存ECC纠错频繁触发,属于硬件隐性故障。
2.2 磁盘:写速度、读速度、随机IOPS
磁盘测试我分成两层。第一层是快速摸底,用dd测顺序写速度:
dd if=/dev/zero of=/tmp/bench_test_file bs=1M count=2048 conv=fdatasync rm -f /tmp/bench_test_filebs=1M、count=2048表示写2GB的临时文件,conv=fdatasync保证数据真正落盘后再统计速度,避免被缓存虚报。这一层30秒内能基本判断磁盘格局。
第二层用fio做精细压测,重点看4KB随机读写的IOPS:
fio --name=randread --filename=/tmp/bench_fio --size=4G --rw=randread \ --bs=4k --iodepth=32 --ioengine=libaio --direct=1 --numjobs=1 fio --name=randwrite --filename=/tmp/bench_fio --size=4G --rw=randwrite \ --bs=4k --iodepth=1 --ioengine=libaio --direct=1 --numjobs=1这里有两个参数新手最容易踩坑:--direct=1会绕过操作系统缓存,直接读写设备,得到的是真实存储介质的能力;如果不加,测试结果会被page cache抬得很高,数据库场景下的参考价值就大打折扣。--iodepth=32对普通SATA盘是合适的队列深度,对NVMe盘可以适当调高到64或128。注意,fio测试文件会占用磁盘空间,要确保目标目录剩余空间足够大,否则测试中途可能写入失败。
我一般会在跑完fio后立刻删除测试文件,避免残留垃圾数据影响后续容量统计。磁盘性能在快照回滚、重装系统后可能出现明显变化,所以基准测试的日志一定要带时间戳保存。
2.3 网络:带宽、延迟与路由探测
网络质量检测大概是这套脚本里最容易被误解的部分。很多人以为测网速就是开个网页看快不快,但服务器场景要更细:延迟、丢包、带宽上限、路由路径分别代表不同问题。
延迟和丢包我用ping来测,默认目标选一台公共DNS服务器:
ping -c 10 -i 0.2 223.5.5.5这里的10个包、间隔0.2秒,已经足够算出稳定延迟和丢包率,数据量也不会太大。如果Ping延迟高或者丢包明显,下一步我会用traceroute追踪路由:
traceroute -n -m 15 223.5.5.5-m 15让探测最多经过15跳,可以快速定位是哪个环节延迟飙升。traceroute的每一跳结果可能存在星号,这是某些路由节点不回ICMP,不代表链路一定中断。这条经验我花了不少冤枉时间才彻底明白。
公网带宽用speedtest-cli之类的工具测:
speedtest-cli --secure它会自动挑选最近的测速节点,输出本机到测速服务器的上传下载速度和延迟。如果你是国内机器,测出的带宽代表到国内测速节点的水平;如果你面向海外用户,最好再指定一台海外测速节点。测速结果受测试时间高峰期影响明显,所以别单凭一次测速就下结论,至少测三次取中间值。
有的环境里speedtest-cli依赖Python环境,脚本会先做依赖检查,能装就装,不能装就改用curl直连下载测速文件的方式提取带宽数据。
3. 脚本设计与实现思路
3.1 用Shell写全套脚本的理由
选Shell而不是Go、Python,可能让部分人意外。我的核心理由是:部署链路短。性能测试脚本的使用场景是拿到一台还没搭好环境的新机器,这种机器往往连Python 3都没有,但一定带bash。用Shell写核心逻辑,就能做到下载脚本、赋权、执行三步走,不依赖任何语言运行时。再加上shell脚本本身就是明文,每一行在干什么都摆在那里,审查也比二进制工具方便得多。
Shell的短板也有,比如数值处理能力弱、并发控制麻烦。但性能测试脚本的特点是“串行跑命令”,天然吃满这些短板,正好扬长避短。我在设计时给每个模块都做成独立函数,不依赖全局状态,这样以后想用Python重写某个模块,也可以直接抽出来替换,不会让整个脚本崩掉。
3.2 依赖工具选型
单体脚本再全面,也不可能自己实现CPU压测和网络测速,必须借助成熟的命令行工具。我的选型标准很简单:优先选各Linux发行版官方源里都在维护的工具,少用个人项目。最终的依赖表长这样:
| 功能 | 工具 | Debian/Ubuntu安装 | RHEL/CentOS安装 |
|---|---|---|---|
| CPU/内存基准 | sysbench | apt install sysbench | yum install sysbench |
| 磁盘IOPS | fio | apt install fio | yum install fio |
| 路由追踪 | traceroute | apt install traceroute | yum install traceroute |
| 网速测试 | speedtest-cli / iperf3 | apt install speedtest-cli | yum install speedtest-cli |
| 系统信息 | lscpu / free / df | 自带 | 自带 |
脚本启动后先做依赖体检,缺什么列什么,再退出,不要自动安装。为什么不自动安装?安装软件这个动作必须由管理员确认。自动安装一旦走到奇怪的源或执行了不该执行的包,损失比测试本身还大。何况有些生产环境还要走变更审批流程,脚本越安静越安全。
3.3 输出组织与结果可读性
测试内容一多,输出乱就成了新问题。我的做法是统一用分隔线分大区,再在开头打印一段元信息:
#!/bin/bash # Linux Bench - Server Performance & Network Quality Test RUN_ID=$(date +%Y%m%d_%H%M%S) echo "==========================================" echo " Linux Bench Report $RUN_ID" echo " Hostname: $(hostname)" echo " Kernel: $(uname -r)" echo " CPU: $(lscpu | grep 'Model name' | sed 's/Model name://' | xargs)" echo " Memory: $(free -h | awk '/Mem:/{print $2}')" echo "=========================================="这个头部信息极其关键。很多机器是别人转交过来的,光看测试数字根本不知道是哪台机器、什么系统、什么时间跑的。我把这些环境指纹强制打印出来,后续整理报告时省了大量沟通成本。
主流程我用函数组织,每个模块一个function,最后main函数按顺序调用:
main() { system_info cpu_bench memory_bench disk_bench network_quality summary } main "$@"每个函数执行完,输出到终端的最后一帧都会带上该模块的结论性数字。脚本本身不把所有结果累加成一个总分,因为性能问题不是一道加减法,不同业务对CPU、内存、磁盘、网络的权重完全不同。
4. 实操实录:从拿到机器到跑出报告
4.1 部署之前的硬性前提
先把脚本放到机器的 /usr/local/bin/ 下,并给执行权限:
vim /usr/local/bin/bench.sh chmod +x /usr/local/bin/bench.sh我习惯用root执行整个测试,因为个别工具在低权限用户下会拒绝操作,比如fio访问块设备、某些系统信息读取需要特权。但要注意,root身份跑一次全量测试约占用5到10分钟,期间CPU和磁盘会有明显负载,务必确认这台机器没有正在对外服务的核心业务,否则线上会抖给你看。
磁盘测试尽量放在数据盘或系统盘的独立目录,不要和正在写入日志的目录混在一起。测试前用df -h确认剩余空间,至少留出5GB给fio,否则4G的测试文件加上dd的2G文件很容易撑爆小容量机器。
4.2 完整运行过程与输出解读
执行:
/usr/local/bin/bench.sh | tee /root/bench_report_$(date +%Y%m%d_%H%M%S).logtee是灵魂,因为屏幕输出滚动太快,不落到文件里后续根本没法回看。脚本先打印系统信息,然后进入CPU测试。我在一台8核16G的云主机上跑出来单线程事件数大约是多少,多线程大约是单线程的7倍左右,说明多核扩展性正常。
如果出现多线程结果接近单线程、甚至更低,要优先怀疑CPU被限制在单核频率上。这种问题常见于某些低配虚拟机,以及没有正确安装CPU频率调度器的系统。
内存测试接着跑,sysbench会在结束时给出Total operations和Transfer speed。这里有个容易误读的地方:不同内核版本和内存频率下,数值差异本来就存在,更重要的是看同机型多次测试的稳定性,而不是跨型号比绝对值。
磁盘部分,dd的写速度对大文件顺序读写有代表性,fio的随机4K IOPS对数据库类业务更有参考性。这里给个粗略的参照:普通SATA SSD的4K随机写IOPS常在几千到两万之间,NVMe SSD可以到几十万级别,机械盘往往只有一两百。看到数字落到哪个档位,就能快速判断存储层是不是瓶颈。
网络部分会依次输出延迟、丢包和带宽。这里我把测试目标分成“默认公共DNS”和“用户指定IP”两类。脚本读取一个可配置变量,比如TEST_TARGET_IP,用户可以在跑之前先设定。如果你特别关心到某条专线的质量,就把专线对端的IP填进去,脚本会自动切换到自定义目标。
4.3 保存日志与三次对比法
跑一次只能叫抽样,不能叫基准。我自己的标准流程是:新机器到货后连续跑三次全量,每次间隔5分钟,把三次结果都存档。如果三次的关键指标波动超过10%,基本可以判断机器存在性能抖动,这种抖动要么来自虚拟化邻居争抢资源,要么来自物理机硬件不稳定。
日志文件的命名坚持加日期时间戳,正好可以避免覆盖历史记录。一个月后出现业务变慢时,把这些存档翻出来,跟新测的一对比,往往能快速锁定是磁盘老化、带宽被限,还是内存不足导致swap频繁。这种用数据说话的排查方式,比我之前凭感觉猜问题高效得多。
5. 常见问题与排查技巧实录
5.1 我踩过的坑和解决方式
| 问题 | 现象 | 处理方式 |
|---|---|---|
| sysbench未安装 | bash: sysbench: command not found | 按依赖表用官方源安装 |
| fio权限不足 | fio: failed to open file: Permission denied | 检查目录权限或改用root执行 |
| dd速度虚高 | 写速度几GB/s,但真实文件写入明显更慢 | 确认有没有加conv=fdatasync,没有就加上 |
| speedtest-cli缺失 | command not found | 用pip安装speedtest-cli,或改用iperf3 |
| traceroute全星号 | 路由全显示* * * | 优先看是否到最后一跳才正常,中间星号不代表中断 |
| 磁盘空间不足 | dd或fio中途失败 | 换目录或先清空临时文件 |
这些坑没有一个是脚本能自动绕过的,都需要管理员在测试前检查环境。所以我在脚本的依赖检查阶段会把“磁盘剩余空间是否大于5GB”也列为一条警告项,低于阈值就提醒但不阻断执行。
5.2 测试结果波动大怎么破
我遇到最典型的场景:同一台机器,白天测和晚上测的带宽结果差出一倍。这个锅不能让脚本背,公网测速本身依赖沿途网络状态和测速节点负载。正确做法是固定同一测速节点、同一时间段、连续测三次取中位数,再把多个时间点的结果画成趋势。比如连续一周每天记录一次,能看出限速策略或者高峰期拥塞规律。
CPU结果波动则首先要看是不是有cron任务、监控Agent在抢资源。我用top -bn1看瞬时负载,如果load average接近核心数甚至超过,测试数据直接作废,等负载下来再跑。
还有一类波动来自散热。物理机在高温下会降频,跑多线程CPU测试时数字越来越低是符合物理规律的。我试过把一台机器从机柜角落挪到通风位后,同样的sysbench成绩涨了13%,这是真实发生过的。所以做性能对比前,务必确认测试时机器的散热条件一致。
5.3 让脚本适配更多场景的小改动
这套脚本我用了很久,每隔一段时间就会根据新场景改一改。如果你接手使用,我建议优先改三个地方:
第一,把TEST_TARGET_IP定义成变量,而不是在脚本里写死。不同业务关心的网络目标完全不同,写死会导致每次都要改脚本,太蠢。
第二,给每条测试加超时保护。sysbench和fio默认会一直跑,万一参数写错或机器太慢,整个脚本会挂住。我用 timeout 300 sysbench ... 给命令套一层超时,超过5分钟自动终止,脚本流程不至于卡死。
第三,接入通知。脚本跑完准备一个摘要变量,通过你在用的IM机器人或者邮件接口把关键结果发出来。远程操作服务器时,人不可能一直盯着终端,有通知就能在跑完第一时间拿到结论。
这三处改动都不大,但能把脚本从“人盯着跑的工具”升级成“自动产出报告的小助手”。
最后说点实实在在的。拼装一个性能测试脚本不难,难的是测试方法论能不能坚持。现在很多人手里一堆脚本,但都是要用时才想起来跑一下,跑完随手丢,根本没有积累。我个人这些年靠这套“建档-复测-对比”三个动作,已经提前发现了好几次磁盘老化和带宽突降的隐患,省掉的麻烦远不止调试那点时间。希望你也别把它当成一次性的玩具,而是当成服务器长期健康管理的一部分。