如何正确进行 VPS 基准测试:工具选择与结果分析
VPS 基准测试不仅是运行 yabs.sh,更需手动执行 fio、sysbench 和 iperf3。本文详解如何通过监控 steal time 指标识别超卖现象,并教你解读 CPU 配额限制与虚拟化类型对性能的影响,助你获取真实的硬件表现数据。
VPS 基准测试的含义
VPS 基准测试需要测量 4 项指标:单个 CPU 核心的运行速度、机器的内存带宽、存储每秒可处理的小型随机磁盘操作数,以及网络链路提供的吞吐量。运行一次 yabs.sh,大约 10 分钟即可得到 CPU、磁盘和网络结果;内存带宽请使用下面单独的内存测试。解读结果更困难,因为 VPS 会与其他租户共享物理硬件。因此,同一台机器可能在 03:00 报告一个数值,在 20:00 报告完全不同的数值。
这里的计划是先运行 yabs.sh 获取总体情况,然后手动运行其中使用的工具。自行运行这些工具,才能修改一个标志,观察数值如何变化,并了解该数值实际测量的内容。请在机器完成配置后再执行,不要提前执行。新 VPS 的前 10 分钟中的步骤应先完成,因为仍在首次安装更新的服务器,其基准测试结果可能很差,而原因与硬件无关。
测量机器前,先了解机器
每个糟糕的基准测试中,有一半的问题来自作者不了解测试机器。
nproc
lscpu | grep -E 'Model name|Hypervisor|Thread'
free -h
df -hT /
uname -r
systemd-detect-virtHypervisor vendor: KVM 表示完全虚拟化,因此您运行的是自己的内核。systemd-detect-virt 输出 lxc 或 openvz 则表示容器虚拟化:您共享主机内核,CPU 和内存限制由 cgroup(控制组)设置,而不是由虚拟硬件提供。在 cgroup v2 系统上,您可以从容器的 cgroup 中读取 CPU 限制;当该 cgroup 挂载在 /sys/fs/cgroup 时,适用以下路径。
cat /sys/fs/cgroup/cpu.maxmax 100000 表示此 cgroup 没有自己的配额,但祖先 cgroup 仍可能施加配额。200000 100000 表示您可以在每个 100000 微秒的周期内使用 200000 微秒的 CPU 时间,相当于 2 个核心的配额。标称 4 vCPU、但配额只有 2 个核心的方案,基准测试结果永远不会达到 4 个核心的水平。基准测试工具可以识别这一限制:VoxelBench 会报告 CPU 配额;如果主机暴露的 CPU 数量远多于服务器可使用的数量,它还会标记 CPU 可能处于共享或限流状态。
df -hT / 的重要性在于另一个原因:Type 列。如果其中显示 overlay,说明您位于容器内,磁盘测试结果描述的是该覆盖层下方的存储设备。现在记下这一点。
持续监控 steal time
steal time 是虚拟 CPU 在 hypervisor 未为其调度 CPU 时间时,被迫等待所占的时间比例。它可以反映虚拟机无法获得 CPU 时间,但无法区分这是邻近虚拟机繁忙,还是服务商正在执行 CPU 配额限制。
vmstat 1 10查看右侧的 st 列。持续出现 steal time 表示来宾系统无法获得 CPU 时间,但超过 5% 并不能证明宿主机存在超售。服务商可能会将 CPU 配额限制显示为 steal time。top 在 CPU 行中显示与 %st 相同的数值。进行基准测试时,在第二个 SSH 会话中持续运行 vmstat 1,并将 steal 数值记录在每个结果旁边。
从 yabs.sh 开始
yabs.sh(Yet Another Bench Script)是一个 Shell 脚本,会运行 fio、iperf3 和 Geekbench,并输出一份汇总结果。如果系统中已安装 fio 和 iperf3,它会直接使用;如果未安装,则会下载静态二进制文件。yabs 是 VPS 基准测试讨论中常用的工具,因此,向他人提供 yabs 输出是最快的比较方式。
该项目提供的单行命令如下。
curl -sL yabs.sh | bash这会将 URL 当前提供的内容直接通过管道交给 Shell。请先下载并阅读脚本,再运行。
curl -sLo yabs.sh https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/yabs.sh
less yabs.sh
bash yabs.sh使用管道时,将选项放在 -s -- 之后;运行本地副本时,直接将选项放在文件名之后。常用选项包括:-f 跳过磁盘测试,-i 跳过网络测试,-g 跳过 Geekbench,-r 将 iperf3 测试位置减少到 3 个,-j 以 JSON 格式输出结果,-w results.json 将 JSON 写入文件。
bash yabs.sh -r -w yabs-run1.json首次运行前需要了解两点。Geekbench 会上传测试结果,并输出一个公开的 browser.geekbench.com URL。任何获得该链接的人都可以查看你的 CPU 型号和得分。-g 会完全跳过该测试。其次,iperf3 阶段会向多个区域的服务器传输真实网络流量,这会消耗每月带宽额度。在 1 Gbit/s 链路上,完整的网络测试可能传输几十 GB,因此带宽额度较小时应使用 -r,按流量计费的链路应使用 -i。
yabs 输出各部分的含义
磁盘部分使用 fio 运行 50/50 的读写混合测试,块大小分别为 4k、64k、512k 和 1m。它会报告每种块大小的 IOPS(每秒输入/输出操作数)和带宽。对于数据库、邮件服务器或任何执行大量小规模写入的任务,应重点关注 4k 行,因为大多数服务器 IO 都是小规模且分散的。1m 行适用于备份和视频处理,这些场景涉及长字节流的传输。
网络部分使用 iperf3 对多个地区的公共服务器进行双向并行流测试。此处数值较低时,应将其视为一个疑问而非定论,因为公共 iperf3 服务器是共享的且经常处于饱和状态,测试结果不佳可能是由远端造成的。
Geekbench 部分提供单核分数和多核分数。单核分数预测单个请求、编译或查询的完成速度。多核分数主要反映服务器实际拥有的核心数量。
磁盘:自行运行 fio
fio (flexible IO tester) 是 yabs 磁盘测试部分底层的工具。直接使用该工具时,各个标志位才具有实际意义。
sudo apt update && sudo apt install -y fio sysbench iperf3在您实际使用的文件系统上,执行队列深度为 32 的 4k 随机读取测试:
fio --name=randread4k --filename=./fio-testfile --size=2G --bs=4k \
--rw=randread --ioengine=libaio --iodepth=32 --direct=1 \
--runtime=60 --time_based --group_reporting输出结果中的摘要行如下所示。
read: IOPS=184k, BW=719MiB/s (754MB/s)(42.1GiB/60001msec)fio 会在下方打印一个 clat percentiles 块。99.00th percentile(第 99 百分位)是值得参考的数据,因为它代表了百分之一最慢请求的等待时间。平均延迟会掩盖用户实际感知的卡顿。
--direct=1以O_DIRECT模式打开文件,使读取操作绕过内核页缓存。如果不使用此选项,在拥有 8G 内存的机器上对 2G 文件进行第二次扫描时,数据将直接从内存读取,导致 fio 报告的 IOPS 达到数百万。该数值是真实的,但它反映的是内存性能。--ioengine=libaio提交异步请求,这使得--iodepth=32能够保持 32 个请求同时处于处理中。如果使用psync等同步引擎,大于 1 的 iodepth 将无效,测试将变为一次处理一个请求。--time_based --runtime=60运行固定的 60 秒而非处理固定工作量,确保快速磁盘和慢速磁盘在相同的挂钟时间内进行测试,从而保证对比的公平性。--size=2G设置测试文件大小。请确保该值大于路径中任何缓存的大小,并提前检查是否有足够的剩余空间。
随机写入测试使用带有 --rw=randwrite 的相同命令。请单独运行该测试,并在结束后删除文件。
fio --name=randwrite4k --filename=./fio-testfile --size=2G --bs=4k \
--rw=randwrite --ioengine=libaio --iodepth=32 --direct=1 \
--runtime=60 --time_based --group_reporting
rm -f ./fio-testfile若要模拟更接近真实流量的混合负载,请使用 --rw=randrw --rwmixread=70。您所使用的存储类型对结果的影响远超任何标志位,相关差异已在 VPS 上 NVMe 与 SATA SSD 存储的区别 中说明。
fio 因 Unknown error -1 停止时
并非所有文件系统都支持 direct IO,因此当 fio 在设置 --direct=1 后第一次请求就失败时,应首先排除 direct IO。错误可能如下所示:
fio: io_u error on file ./fio-testfile: Unknown error -1: read offset=0, buflen=4096
fio: pid=1234, err=-1/file:ioengines.c:321, func=get_events, error=Unknown error -1先运行 df -hT .。作为 Type 的 overlay 是 Docker 默认提供给容器的文件系统;它本身不是原因。overlayfs 会将 O_DIRECT 传递到底层文件系统,只有底层文件系统不支持 direct IO 时才会拒绝,因此在基于 ext4 或 xfs 的 overlay 上,--direct=1 --ioengine=libaio 可以正常工作。如果测试文件所在的底层文件系统确实不支持 direct IO,fio 通常会在打开文件时明确提示:fio: looks like your file system does not support direct=1/buffered=0。此时,应将 --filename 指向真实本地存储上的路径,例如 bind mount 的卷;或者改为在宿主机上运行 fio。如果无法访问真实存储,至少可以运行一次带缓冲的同步测试,以确认命令本身正确。
fio --name=randread4k-buffered --filename=./fio-testfile --size=256M --bs=4k \
--rw=randread --ioengine=psync --direct=0 --numjobs=1 \
--runtime=15 --time_based --group_reporting
rm -f ./fio-testfile必须明确该测试的含义。第一次运行后,256M 文件会留在页缓存中,因此 IOPS 数值反映的是 RAM 的性能。可以用它确认 fio 已安装且参数解析正确。不要将其作为磁盘性能结果引用。
dd 不是磁盘基准测试
dd 经常出现在许多 VPS 讨论中,但它只回答一个范围很窄的问题。
dd if=/dev/zero of=./ddtest bs=1M count=1024 oflag=direct conv=fdatasync
rm -f ./ddtest它测量的是单线程、单个在途请求下的顺序写入吞吐量。这是合理的快速检查,但无法说明随机 IO 的性能,也无法说明同时到达 32 个请求时会发生什么。移除 oflag=direct 后,写入会经过页缓存;但 conv=fdatasync 仍会在命令结束前强制将输出数据写入存储设备。连这个选项也移除后,结果主要测量的可能只是内核将写入数据接受到内存中的速度。因此,论坛帖子中经常引用的 dd 数值通常非常荒谬。
CPU:sysbench cpu
sysbench cpu --cpu-max-prime=20000 --threads=1 run
sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run应保留的指标是 events per second。先运行单线程测试。该数值决定单个 PHP 请求完成或单个编译任务结束的速度,而且在价格相同的主机之间差异通常最大。然后使用全部线程运行测试,以观察吞吐量如何随分配的 vCPU 数量扩展。
需要明确该测试的测量内容:sysbench cpu 通过试除法反复查找质数。它会为每个候选数计算双精度平方根,然后使用 64 位整数取模测试除数,并将每个除数与这个浮点数上限比较。因此,该测试同时包含整数运算和浮点运算。它不会以接近真实工作负载的方式测试内存带宽、向量单元或缓存。因此,它适合比较两台主机的性能,但不适合预测应用程序的实际运行表现。
Ubuntu 24.04 提供 sysbench 1.0.20,此版本将测试名称放在前面。从旧文章中复制带有 --test=cpu 的命令后,会得到 WARNING: the --test option is deprecated。sysbench 0.4 和 sysbench 1.0 的分数完全不可比较。因此,不要将自己的结果与未注明版本的公开数值进行比较。
内存:sysbench memory
sysbench memory --memory-block-size=1M --memory-total-size=20G --memory-oper=write --threads=1 run
sysbench memory --memory-block-size=1M --memory-total-size=20G --memory-oper=read --threads=1 run结果单位为 MiB/sec。将 --memory-block-size 保持为 1M,并确保所有参与比较的主机使用完全相同的值。在 1K 下,该数值会大幅下降,因为每次操作的开销出现频率提高了 1000 倍,最终测量的是循环开销,而不是内存带宽。这是已发布内存性能分数中最常见的不一致参数。
网络:iperf3
测试吞吐量的可靠方法是针对您控制的第二台机器进行测试,因为这样您可以了解两端的状态。
在远端:
iperf3 -s该命令会在 TCP 5201 端口进行监听。请仅针对您进行测试的地址开放该端口,并在测试完成后将其关闭。VPS 的基本 ufw 防火墙规则 涵盖了相关语法。
在待测 VPS 上:
iperf3 -c 203.0.113.10 -t 30
iperf3 -c 203.0.113.10 -t 30 -R
iperf3 -c 203.0.113.10 -t 30 -P 8第一个命令测量从待测机器发出的上传速度。-R 会反转方向,从而测量下载速度。-P 8 会开启 8 个并行流。
请同时运行单流和并行版本,因为它们回答的问题不同。单个 TCP 连接所能承载的未确认数据量受限于其窗口大小,因此其上限大致为窗口大小除以往返时间。在 80 ms 延迟和 4 MB 窗口的情况下,无论底层链路速度有多快,其上限约为 400 Mbit/s。单流数据反映了单个下载任务的实际速度。并行数据则反映了链路的总容量。
执行此操作时,请留意您的带宽配额。以 1 Gbit/s 的速度运行 30 秒会消耗约 3.75 GB 流量,且您需要在每个方向上多次运行测试。
参考数据,以及如何解读您的结果
此图表背后的数据
[
{
"device": "Local NVMe",
"iops_4k_read": "180,000"
},
{
"device": "Local SATA SSD",
"iops_4k_read": "90,000"
},
{
"device": "Network block",
"iops_4k_read": "12,000"
},
{
"device": "Spinning disk",
"iops_4k_read": "180"
}
]公开测试结果中,本地 NVMe 卷的 4k 随机读取 IOPS 通常接近 180,000。本地 SATA SSD 通常约为 90,000。网络附加块存储的每个请求都必须经过网络才能到达磁盘,因此通常接近 12,000;机械硬盘大约为 180,因为每个随机请求都需要移动物理磁头。
这些是各类存储的典型公开数据,不是从某一台主机测得的结果。请仅将它们用于确认您自己的结果是否处于正确的数量级。如果标称为 NVMe 的方案测得的 4k IOPS 只有几千,请先确认 --direct=1 已启用。如果已启用,请同时检查该方案的 IOPS 限制和突发额度,以及存储资源争用情况和所宣传的硬件配置。
为什么一次运行不能作为基准
单次结果只是共享机器上某一分钟的快照。应将其视为一个样本。
- 每项测试至少运行 5 次,分布在不同时间段,并至少覆盖 2 天。保留中位数和离散范围。发布结果时不提供离散范围,就是营销数字。
- 在每次运行旁记录 steal time。保留
st较高的运行,并调查这反映的是资源争用,还是计划的 CPU 配额;丢弃这些结果可能掩盖你实际获得的性能。 - 以两种时长运行磁盘测试。许多计划提供可随时间恢复的突发 IOPS 配额,但无论是 60 秒的 fio 运行,还是
--runtime=600,都不能保证测得的是性能下限。AWS gp2 在余额充满时可以将 3,000 IOPS 持续至少 30 分钟,因此两次运行都可能测到突发性能。在将结果称为持续性能前,先检查提供商的额度模型,并监控额度余额。 - 确认没有其他任务正在运行。在 CPU 测试中途启动 apt 事务,
unattended-upgrades会实际影响测试分数;每次运行前执行ps -e -o comm= | grep -E 'apt|dpkg'只需 1 秒。 - 一次只更改一个变量。不同的工具版本、块大小或线程数会产生无法比较的数字,无论它们看起来多么相近。
比较两个提供商时,应在同一天的同一时间运行测试。否则,测到的是一天中的时间段。
最后对您的工作负载进行基准测试
合成工具仅用于对机器进行排名。只有您自己的工作负载才能判断机器是否足够。请为您实际执行的任务计时。
time tar -czf /tmp/bench.tgz /usr/share
rm -f /tmp/bench.tgz该操作会压缩几百兆字节的数据,因此它会同时测试 CPU 和磁盘,并且在两者中任何一个发生变化时都会受到影响。出现 Removing leading / from member names 警告是正常的。更好的做法是,为您自己的构建过程、最慢的查询或页面渲染进行计时。如果一个构建在某台主机上耗时 4 分钟,而在另一台主机上耗时 7 分钟,那么无论 Geekbench 的结果如何,结论都已经确定。这也是衡量何时增加机器配置不再具有性价比的指标,在您阅读 VPS 的实际月度成本 或将工作负载迁移到 独立服务器 之前,了解这一点非常重要。
FAQ
为什么我每次运行基准测试时结果都不同?
VPS 与其他租户共享物理 CPU、存储和网络,因此测试结果取决于它们当时正在执行的任务。测试期间运行 vmstat 1,查看 st 列:持续的 steal time 表示不可用的 CPU 时间,但这既可能是服务商限制了您购买的 CPU 配额,也可能是宿主机资源争用造成的。超过 5% 不能确定具体原因。解决方法是改进测试方法,而不是调整参数。在不同时间段分别运行每项测试至少 5 次,然后同时报告中位数和结果分布。
为什么 fio 报告数百万 IOPS?
几乎总是因为缺少 --direct=1。没有该选项时,fio 会通过内核页缓存读取,因此第一次运行后,2G 测试文件会从 RAM 提供,实际测量的是内存带宽。添加 --direct=1,并确保测试文件大于路径中的所有缓存。如果 --direct=1 随后失败,请运行 df -hT .,检查测试文件所在的存储。overlay 类型本身不是问题,因为 overlayfs 会将 O_DIRECT 传递到底层存储。只有底层文件系统拒绝直接 IO 时,才应将测试文件移到真正的本地存储上。
yabs.sh 单独使用是否足够?
用于初步了解时足够。它会使用 4 种块大小运行 fio,双向运行 iperf3,并运行 Geekbench,然后输出一份其他人也能读取的摘要。当您需要了解某个数字为何如此时,它就不够了,因为您无法为每项测试单独调整参数。yabs 结果异常时,请直接使用 fio 或 sysbench 重现测试,并且每次只修改一个参数。
哪一个单独的数字最能预测我的应用使用体验?
对于大多数 Web 和数据库工作负载,依次是单核 CPU 速度和 4k 随机读取延迟。吞吐量数据看起来很高,但通常很少决定实际体验,因为典型请求的数据量很小。应从 fio 的 clat percentiles 块中报告 99th percentile,而不是平均值,因为每 100 个请求中最慢的那个请求才是用户会感知到的请求。
基准测试前需要安装什么吗?
fio、sysbench 和 iperf3 都包含在 Ubuntu 和 Debian 软件仓库中:sudo apt install -y fio sysbench iperf3。yabs.sh 在 Bash 中运行,需要 curl 或 wget 下载文件,还需要 tar 解压 Geekbench;没有 tar 时,它会跳过 Geekbench。系统已安装 fio 和 iperf3 时,脚本会使用它们;缺少时,则会下载静态二进制文件。完成后删除所有测试文件,因为留在 20G 磁盘上的 2G fio 文件,几周后可能会触发磁盘已满告警。