换盘了、调了配置、升了版本,存储"到底快没快"这个问题,靠感觉和dd答不出来。对象存储走 S3 接口,要按 HTTP 请求算吞吐、延迟和分位数,还得把每轮的原始数据留下来,下次才能比。这篇用 warp(MinIO 社区的开源 S3 压测工具)对着 RustFS 把流程走完整:装上、连上被测端点、跑第一轮、把输出读明白、把结果存成可对比的档案。文中所有命令、参数和示例输出都出自 warp 官方 README,没有编造的数字;README 里的示例数字是在它自己的测试环境跑出来的,仅供看懂输出结构,换成 RustFS 的数字得自己跑。
为什么挑 warp,而不是自己写个脚本压
自写循环压测通常栽在两件事上:一是只算平均数,p99 抖动全被平均掉;二是跑完不留原始数据,两周后想复算只能重跑。warp 把这两件事都替你做了:它是 Go 写的单二进制工具,release 页直接下各平台的可执行文件;每次基准跑完,会把每一个请求的明细存成一份 zstd 压缩的 CSV,默认文件名形如warp-operation-2026-10-12[143000]-xxxx.csv.zst。事后用warp analyze随时重新分析,换个分析参数就能从另一个角度看同一份数据,不用重跑。
安装两条路。省事的直接从 warp Releases 下载对应平台的二进制;有 Go 环境的从源码编,README 要求 Go 1.21 以上:
gitclone https://github.com/minio/warp.gitcdwarp&&go build另外有个独立的warp-rdma包,支持把 PUT/GET 的负载走 RDMA 直送内存或显存,标准二进制不支持--rdma旗标,需要时从 release 单独装。日常压 S3 接口用不到它。
子命令按操作类型分:get、put、delete、list、stat各测一种,mixed按比例混跑多种操作,multipart和multipart-put测分片上传下载,还有versioned、snowball、atomic这类专项。没有叫warp s3的总命令,别照着老教程找。
把 RustFS 端点和密钥备好,注意那口会被清空的桶
被测端用 RustFS:S3 API 默认监听 9000 端口,所以 endpoint 写http://<rustfs-host>:9000。注意 9001 是控制台端口,给人看 Web 界面用的,压测别打到这个口。RustFS 是 Apache 2.0 协议、兼容 S3 的分布式对象存储,部署与密钥管理见官方文档。
warp 的连接参数是--host、--access-key、--secret-key,TLS 加--tls,自定义 region 加--region。同样的东西也能走环境变量:WARP_HOST、WARP_ACCESS_KEY、WARP_SECRET_KEY、WARP_REGION、WARP_TLS,写进脚本比每条命令重复敲干净。README 对密钥权限有明确要求:必须能创建、删除、列出桶,还要能上传对象,只读账号跑不了。
最大的一个坑在这里:warp 默认操作一个叫warp-benchmark-bucket的桶,每轮基准开始前和结束后都会把它整个清空。README 用警告框原话强调了桶里不能放任何数据。所以要么用--bucket指一个专用的空桶,要么确认目标桶里没有你舍不得的东西。把生产桶指给 warp,等于亲手执行了一次全删。
第一跑:一条命令先测读
最小可用命令就一行:
warp get--host=http://<rustfs-host>:9000\--access-key=<ACCESS_KEY>--secret-key=<SECRET_KEY>\--duration=1mwarp get会先往桶里上传--objects个--obj.size大小的对象(这批上传的耗时也会作为 PUT 统计展示),然后开始随机下载,跑满--duration为止。跑完输出长这样(原样引自 README 的 GET 章节,注意这是 README 测试环境的示例数字):
Operation: GET * Average: 94.10 MiB/s, 9866.97 obj/s Throughput, split into 299 x 1s: * Fastest: 99.8MiB/s, 10468.54 obj/s * 50% Median: 94.4MiB/s, 9893.37 obj/s * Slowest: 69.4MiB/s, 7279.03 obj/s三段结构:第一行是整轮平均,后面是把全程切成 299 个 1 秒段后,最快段、中位段、最慢段各是多少。这两组数差距大不大,比平均值本身更能说明稳不稳。读性能还有 TTFB(首字节时间)的统计,加--analyze.v会展开成完整的分位数表,下一节细说。
三个变体按需用。想拿桶里已有的对象当测试集、跳过预上传,加--list-existing,warp 会先列出至多--objects个对象再随机下载它们;想测部分读(Range 请求),加--range,每次从随机偏移读随机长度,S3 网关类应用和断点续传场景对口的正是这条路;桶开了版本控制想顺带测版本化读,用--versions=n指定每个对象放几个版本。默认情况下 warp 上传的都是随机数据,这个设计是有意的:避免存储端对可压缩内容做优化,测出来的才是裸吞吐。
duration、concurrent、obj.size:三个旋钮决定你在测什么
同一套存储,这三个参数不同,数字能差出一个数量级,所以报数字必须连参数一起报。
--duration控制压多久。太短会撞上预热期,TCP 爬坡、缓存未热都拉低数字;太长浪费时间。一分钟起步,正式对比再加长。
--concurrent控制并发操作数,默认 20。README 专门提醒:调并发对性能有影响,测延迟的时候尤其明显。想知道吞吐上限,把并发往上调到吞吐不再涨为止;想知道单请求延迟,就压低并发。
--obj.size控制对象大小,默认值按子命令各不相同。固定大小适合对照测试;加--obj.randsize变成随机大小(上限还是--obj.size),分布按 log2 切,README 给了换算:平均大小约为--obj.size乘 0.179151,想要目标平均大小就乘 5.582 得出该填的最大值。它还接受分桶格式,4096:10740,8192:1685,16384:1623表示 0 到 4096 字节这段占权重 10740,以此类推,用来模拟真实业务的大小分布。
还有个--autoterm让 warp 自己判断稳定性:它持续把当前数据降采样成 25 个点,最后 7 个点落在阈值内就提前收工,阈值默认 7.5%(--autoterm.pct可改)。好处是不用手掐表,代价是每轮实际时长不同,对比时要用 50% Median,不能用 Average,README 明确提醒这种场景下不该比均值,中位数好得多。
单一操作之外,mixed子命令按比例混跑四种操作,配--get-distrib、--stat-distrib、--put-distrib、--delete-distrib四个旗标调比例(设 0 关掉某类),最终占比按各值的分数决定。它有个约束:put-distrib必须大于等于--delete-distrib,不然对象池迟早被删空。想贴近真实业务的读写比,用 mixed 比单跑get或put更有说服力,代价是结果里 GET、PUT 各算各的,吞吐不能简单相加对外报。
输出怎么读:看 Median、看 TTFB,别忘了预热被裁掉了
先说一个容易懵的点:你跑了 60 秒,输出里的分析时长可能只有 59.9 秒甚至更少。别慌,这是 warp 故意裁的。README 写明了分析窗口的起止:从所有线程都完成第一个请求开始,到最后一个线程发起请求为止,两头裁掉,为的是排除预热和收尾的抖动。看懂这个,就不会把"时间对不上"当成 bug。
逐行拆一遍输出:
Average:整轮平均吞吐和对象速率。大对象看 MiB/s,小对象和元数据类操作看 obj/s,stat这种操作只报 obj/s。50% Median:中位 1 秒段的值。两轮结果对比看这个数,别看 Average,理由上一节说过。Fastest/Slowest:最快和最慢的 1 秒段。Slowest 和 Median 差距悬殊,通常意味着有卡顿源值得去查。TTFB:README 的定义分两个方向,下载是从请求发出到收到响应第一个字节的时间,上传是从请求体最后一个字节发出到收到响应的时间。读场景的响应快慢基本就看它。
默认输出只有均值级别的信息,加--analyze.v才展开每请求的分位数(Avg、50%、90%、99%、Fastest、Slowest,TTFB 同样一套),还能按主机拆开看,多节点集群排查单节点拖后腿就靠这个。--analyze.op=GET只看某一类操作,--analyze.host只看某一台,分析窗口想再裁就加--analyze.skip=10s跳过开头 10 秒。分位数里还藏了两个容易被略过的指标:First Access 和 Last Access,分别是每个对象的首次和末次访问耗时,预上传阶段的 PUT 慢、正式阶段的 GET 快,从这个切面能看出来。想把切段后的时序数据导出去画图,--analyze.out=文件.csv会把每段的吞吐、对象数、错误数写成标准 CSV,字段表在 README 里。
两次跑的结果要能摆在一起比,流程得先固定
基准数字只在同条件下才有意义。想做到调配置前后的两组数能直接对上,流程比工具重要:
- 固定版本和参数:记下 warp 版本、RustFS 版本、完整命令行。并发、对象大小、时长任何一个变了,数字就不该放一起比。
- 归档原始数据:每轮的
csv.zst文件按默认命名存好,这是你的复算凭证;想自己命名用--benchdata指定。 - 用
warp cmp出对比报告:warp cmp 旧文件.csv.zst 新文件.csv.zst,输出直接给百分比差值,Average、Median、Slowest 各一行,参数差异也会列出来。 - 客户端瓶颈要排掉:单台客户端打不满服务端时,测出来的是客户端的上限。warp 支持多机分布式压测:各机器跑
warp client(默认监听 7761 端口),主控端加--warp-client=client-{1...10}:7761协调发压;参数按每个客户端各自生效,--concurrent=8意味着每台都是 8。散跑的几轮也能事后用warp merge合并,但只有时间上真正重叠的数据会被算进去,所以各客户端要时钟同步(README 建议对齐到 1 秒内),最好用--syncstart对齐起跑。
还有个对复现特别有用的机制:warp 支持把整套参数写进 YAML 配置文件,README 在 yml-samples 目录放了每种基准类型的样例,用warp run <file.yml>运行,命令行里还能用-var 变量名=值往模板里注入变量。把每次基准的 YAML 和产出文件放同一个目录归档,半年后别人拿着这份 YAML 就能原样复跑,比翻聊天记录找命令行可靠得多。
被测端是多节点时,--host支持逗号分隔多地址和{1...8}区间展开,warp 默认把请求派给在途请求最少的主机,想改成轮询用--host-select=roundrobin。
至于上线前该测哪几组读场景,之前写过一篇《S3对象存储上生产:务必做的3组读压测》,讲的是"测什么";这篇讲的是"怎么测",两篇配合着用。
踩坑清单,都写在 README 里了
- 桶会被清空:再强调一次,
warp-benchmark-bucket或--bucket指定的桶,每轮前后都会被 wipe,README 警告框原文是"should not contain any data"。 - 端口别打错:RustFS 的 S3 API 在 9000,控制台在 9001,压测目标永远是前者。
- 预热和抖动:分析窗口自动裁掉了预热,但如果你判断预热期更长,用
--analyze.skip再裁;重跑几轮取一致的区间,比单轮的 Fastest 可信。 - 数字上不去先怀疑客户端:单机带宽、CPU、连接数都可能先于存储饱和。分布式模式和
warp merge都是为消掉客户端瓶颈准备的。 - 并发是每客户端的:分布式压测时总并发是
--concurrent乘以客户端数,README 原话"parameters apply to each client"。 csv.zst会随规模变大:明细是按请求逐条记录的,时长和并发加上去,文件就跟着变大,几轮长压测的产出归档前留意磁盘占用,别让它把归档盘塞满。- warp 测的是 S3 协议层:它评估的是经 HTTP 的 S3 接口表现,底层磁盘的块设备性能要用 fio 这类工具单独测,两者不能互相替代,报数字时别把 S3 压测结果说成磁盘性能。
下次动配置前,先跑这一轮
动作收拢成四步:装 warp(release 二进制,或 Go 1.21+ 自编);--bucket指个专用空桶;一条warp get --duration=1m跑基线;改配置后同参数再跑一轮,warp cmp出差值。原始csv.zst和完整命令行一起归档,半年后还能复算。
相关入口:warp 仓库与 Releases 页(各平台二进制,README 覆盖全部旗标说明),RustFS 文档覆盖部署与密钥配置。