观测与压测工具箱
把 iostat/vmstat/perf/bpftrace 和 elbencho 串成一套可复用的手法。
学完这节你能做到
- 用 elbencho 设计出能回答具体问题的测试用例,而不是跑个分
- 用 elbencho service 模式对共享文件系统做多客户端压测
- 读懂 first done / last done 两列,从差距里看出不均衡
- 用 bpftrace 抓出单次慢 I/O 的调用栈
把前面几节串成一套手法
到这里,L0 的知识点已经齐了。这一节不引入新概念,而是把工具组织成可复用的动作:遇到什么问题,按什么顺序,敲哪些命令。
工具分两类,代价完全不同:
| 类型 | 代表 | 开销 | 什么时候用 |
|---|---|---|---|
| 静态计数器 | vmstat、iostat、sar、ss | 几乎为零 | 常态巡检、第一轮定位 |
| 动态追踪 | perf、bpftrace、biosnoop | 有开销,需谨慎 | 定位到范围后,抓具体证据 |
顺序永远是:先用零开销的工具收敛范围,再用追踪工具挖根因。反过来做,你会在生产机器上跑一个高开销的 trace,然后被业务方投诉。
压测:elbencho 的四件套
压测不是"跑个分",是回答一个具体问题。先想清楚问题,再选参数。
本课统一用 elbencho —— 它由 BeeGFS 创始人开发,一个工具覆盖了 fio、mdtest、ior 的功能,块设备、文件系统、S3 都能压,而且原生支持多客户端协同。学一套参数就够用。
# 1. 顺序写 —— 回答"这块盘的写带宽上限是多少"
elbencho -w --direct -b 1M -t 4 --iodepth 32 \
-s 100g --timelimit 60 --lat /dev/nvme0n1
# 2. 顺序读 —— 回答"读带宽上限"
elbencho -r --direct -b 1M -t 4 --iodepth 32 -s 100g --timelimit 60 /dev/nvme0n1
# 3. 随机写 —— 回答"数据库类负载能跑多少 IOPS"
elbencho -w --direct -b 4k -t 4 --iodepth 64 --rand --randamount 50g \
-s 100g --timelimit 60 --lat --latpercent /dev/nvme0n1
# 4. 随机读 —— 回答"元数据类负载能跑多少 IOPS"
elbencho -r --direct -b 4k -t 4 --iodepth 64 --rand --randamount 50g \
-s 100g --timelimit 60 --lat --latpercent /dev/nvme0n1
必须理解的几个参数:
| 参数 | 含义 | 常见错误 |
|---|---|---|
--direct | 绕过 page cache | 忘了加,测出的是内存速度 |
-b | 块大小 | 不写块大小的 IOPS 数字没有意义 |
--iodepth | 单线程异步队列深度(默认 1 = 同步 I/O) | 用默认值会低估 NVMe 的能力 |
-t | 并发线程数 | 单线程压不满 NVMe |
--rand --randamount | 随机 offset,以及每线程的随机 I/O 总量 | 不加 --rand 测的其实是顺序 |
--timelimit | 按时间跑而非按数据量 | 不加就是写完 -s 停,可能只测到 SSD 的缓存区间 |
--lat --latpercent | 输出延迟均值与百分位 | 只报 IOPS 不报延迟,等于没测 |
- 忘了
--direct—— HDD 跑出 3GB/s,测的是 page cache - 测试时长太短 —— SSD 有 SLC 缓存,前 30 秒很快,之后掉到稳态。至少
--timelimit 300才能看到真实的稳态性能 - 在生产盘上跑写测试 —— 路径写成
/dev/nvme0n1时-w会直接覆盖裸设备上的数据。动手前先确认这块盘是空的
压测:多客户端压共享文件系统
单机压出来的数字只是上限参考。共享文件系统(CephFS、GPFS)需要多客户端并发才能压出真实能力——同一个 elbencho,加上 service 模式即可:
# 每台客户端上先起一个 service(后台常驻)
elbencho --service
# 协调节点统一发起:4 台客户端一起压同一个文件系统
elbencho --hosts client1,client2,client3,client4 \
-w -b 4M -t 16 --direct -s 100g /mnt/cephfs/bench/file
# 多文件(元数据)性能:这才是共享文件系统真正的考验
elbencho -w -d -n 100 -N 10000 -s 4k -t 16 /mnt/cephfs/bench
# 块设备:一次压多块盘,验证整机能力
elbencho -w -b 4M -t 48 --direct -s 100g /dev/nvme{0..11}n1
压测裸盘时同步开一个 iostat -xm 1 观察。经验值:NVMe 单盘写入约 5 GB/s,如果实测差得远,先怀疑 PCIe 链路降速:
lspci -vvv | grep 'Non-Volatile' # 拿到 PCIe 地址,如 5a:00.0
lspci -s 5a:00.0 -vvv | grep 'Speed'
# LnkCap: Speed 16GT/s, Width x4
# LnkSta: Speed 16GT/s (ok), Width x1 (downgraded) ← 掉到 x1 了出现 downgraded 说明链路宽度或速率没跑满,通常是插槽、转接卡或 BIOS 设置的问题。这类硬件问题必须在集群部署前发现,否则上线后会表现为某几个 OSD 莫名其妙地慢。
结果怎么读:first done 与 last done
elbencho 的结果表有两列数字,很多人只看右边一列就走了,其实这两列之间的差距本身就是结论:
OPERATION RESULT TYPE FIRST DONE LAST DONE
========= ================== ========== =========
WRITE Elapsed ms : 41230 68940
IOPS : 12470 7460
Throughput MiB/s : 4870 2910
Total MiB : 200704 200704
- first done = 第一个工作线程(多客户端时是第一个 host)干完自己那份活时的统计。此刻所有线程都还在跑,机器处于满载、竞争均衡的状态,所以这一列代表并发拉满时的峰值能力。
- last done = 最后一个线程干完时的统计,也就是整个作业的真实完成时间。这一列包含了后半段"快的已经收工、慢的还在磨"的拖尾,所以它是你真正能对外承诺的聚合数字。
上面这组数据里 last done 的带宽只有 first done 的六成,说明最快和最慢的线程差了 27 秒——这不是一次可信的压测,是一次暴露了不均衡的压测。
一句话记法:想知道"这堆盘、这张网到底能跑多快",看 first done;想知道"这批活干完要多久",看 last done。
- 判断硬件上限用 first done,它更接近设备/网络的理论峰值
- 描述业务感知("全部完成要多久")用 last done
- 两列的差距用来判断这次测试有没有资格被当成基线:经验阈值是 elapsed 差 10% 以内正常,超过 20% 先查不均衡,别急着记数字
硬件验收要回答的是"这批盘和这张网有没有跑到它该有的水平",这时只有 first done 有意义——它是所有线程都还在跑、机器满载时的数字,最接近设备与链路的物理峰值。last done 已经被拖尾稀释过,拿它对比理论值会系统性地低估硬件。
举个例子:12 块 NVMe 单盘写 5 GB/s,理论上限约 60 GB/s;节点是 2 × 25GbE,网络上限约 6 GB/s。
- first done 4.9 GB/s:达到网络理论值的 80% 以上 → 硬件没问题,瓶颈在网络,符合预期,可以验收
- 如果拿 last done 的 2.9 GB/s 去比,只有网络上限的一半 → 会得出"硬件不达标"的错误结论,然后开始排查根本不存在的问题
所以顺序是:先用 first done 确认硬件跑到了该有的水平,再用两列的差距确认这次运行是否均衡,最后才用 last done 描述业务感知。
差距过大的常见原因
| 类别 | 具体原因 | 怎么确认 |
|---|---|---|
| 测试数据量太小 | 每线程只跑几秒,预热、文件创建/打开、缓存刷写这些固定开销占了大头;线程间几秒的正常抖动被放大成几十个百分点 | 把 -s / --randamount 加大到每线程至少跑 60 秒以上,看差距是否收敛 |
| 任务切分不均 | 文件大小不一、-N 除不尽线程数、目录里文件数量差异大 | 换成等大文件、线程数取整重跑一遍看差距是否消失 |
| 有一块慢盘 | 单盘 PCIe 降速、坏道重试、温度降频、SSD 寿命末期 | iostat -xm 1 看哪块盘 await 高;ceph osd perf 找慢 OSD;lspci 查 downgraded |
| 数据分布不均 | Ceph 的 PG / CRUSH 权重不均、OSD 接近 nearfull、GPFS NSD 容量不一致 | ceph osd df tree 看 PG 数与使用率的离散度 |
| 元数据侧串行 | 所有线程写同一个目录,撞在同一个 MDS / 同一个元数据锁上 | 用 -d -n 让每线程写自己的目录,对比差距 |
| 客户端不对等 | 某台客户端 CPU 少、挂载参数不同、跨了交换机、bond 哈希不均、MTU 不一致 | 逐台单独跑一遍取单机基线,横向比对;ethtool -S 看丢包重传 |
| SSD 缓存耗尽 / GC | 前 30 秒吃 SLC 缓存很快,之后掉稳态,先跑完的线程占了便宜 | 拉长 --timelimit 或加大数据量,看差距是否收敛 |
| 缓存命中不对等 | 部分线程的数据还在 page cache 里 | 加 --direct,或每轮前 --dropcache |
| 后台任务抢资源 | 测试期间集群在 scrub / recovery / backfill / 快照回收 | 压测前 ceph -s 确认 HEALTH_OK 且无 recovery |
| 启动不同步 | service 模式下各 host 起跑时间差、节点间时钟偏移 | 用协调节点统一发起(--hosts),别手工分别敲命令 |
last done 只是这一次运行的完成时间,它把所有拖尾原封不动地算进去了。如果差距来自数据量太小、某块慢盘或后台 recovery,那这个数字描述的是那次故障/那个测试缺陷,不是系统能力——直接抄进报告,等于把偶然当结论,而且换台机器就复现不出来。
出数字之前按顺序做四件事:
- 先看差距。超过 20% 不出数字,先把原因解决掉或解释清楚,再重跑一轮
- 确认量够。写清每线程跑了多久、总数据量多少;跑了几秒的结果没有资格进报告
- 两列都写,附上差距百分比和一句原因说明。只给一个数字,一定会被追问"这是峰值还是平均"
- 重复 3 次取中位数。三次 last done 波动超过 10%,说明还没进稳态,任何单次数字都不是基线
给结论时用区间比用一个点更诚实:稳态聚合带宽 2.9 ~ 4.9 GB/s(last / first done),差距源于 osd.23 慢盘,已换盘后复测。
--timelimit 是按时间跑,所有线程同时被叫停,first done 和 last done 自然几乎相等——差距消失不等于不均衡消失,它只是换了个形式:表现为总吞吐比预期低。所以定位不均衡时要跑固定数据量(只给 -s / --randamount,不加 --timelimit)让快慢差异显形;打稳态基线时再用 --timelimit。
想留证据就加 --csvfile /tmp/bench.csv,把每一轮两列数字都记下来,事后对比参数改动的效果。
动态追踪:bpftrace 一行流
当静态工具告诉你"盘的平均延迟是 15ms",但你需要知道是谁在发这些慢 I/O,就该上 bpftrace 了。
# 1. I/O 延迟分布直方图 —— 看清长尾
biolatency-bpfcc 10 1
# 2. 逐条打印 I/O,含进程名、扇区、延迟
biosnoop-bpfcc | head -40
# 3. 按进程统计 I/O 大小
bpftrace -e 'tracepoint:block:block_rq_issue { @[comm] = hist(args->bytes); }'
# 4. 抓超过 100ms 的慢 I/O 及其调用栈
bpftrace -e 'kprobe:blk_account_io_done /nsecs/ { ... }'
biolatency 的输出最能说明问题:
usecs : count distribution
0 -> 127 : 84213 |████████████████████████████████████████|
128 -> 255 : 12043 |█████ |
256 -> 511 : 3211 |█ |
...
16384 -> 32767 : 89 | | ← 长尾在这
平均延迟可能只有 0.2ms,但这 89 个 16ms 以上的请求,就是业务侧感知到的卡顿来源。
- 先在测试机上验证命令,不要在生产上现敲现试
- 限定运行时长(
timeout 30 bpftrace ...),不要开着就走 - 高频事件(如每次内存分配)不要 trace,开销可能压垮机器
- 记录你做了什么、什么时候做的,方便事后关联业务抖动
压测报告怎么写才有说服力
跑完数字只是原料。一份能用于决策的报告要包含:
## 测试环境
- 硬件:8 × Dell R750,每节点 12 × 7.68TB NVMe,2 × 25GbE
- 软件:Ceph 20.2.1,3 副本,pg_num=256
- 客户端:4 台,与存储节点独立
## 测试方法
- 工具:elbencho 3.1-7(4 台客户端 service 模式)
- 参数:见附录完整命令行
- 数据量:每线程 100 GiB(远超节点内存,避免只测到缓存与启动开销)
- 每组跑 300 秒,取稳态区间;每组重复 3 次取中位数
## 结果
| 场景 | 块大小 | 队列深度 | IOPS | 带宽(last done) | 平均延迟 | P99 延迟 | first/last 差距 |
| ... |
## 结论与瓶颈判断
- 顺序写在 11.2 GB/s 见顶,与 cluster 网理论上限吻合 → 瓶颈在网络
- 4K 随机写 IOPS 未达单盘能力之和的 50% → 需进一步排查
## 未覆盖的场景
- 未测试恢复期间的性能衰减
- 未测试多租户混合负载
"未覆盖的场景"这一段决定了报告的可信度。明确说出没测什么,比把测了的部分吹得天花乱坠更专业——因为看报告的人需要知道风险在哪。
用 elbencho 测一块企业级 NVMe 的顺序写,跑了 30 秒得到 6.8 GB/s,远超厂商标称的 5 GB/s。最可能的原因是?
iostat 显示某块盘平均延迟只有 0.3ms,但业务方反馈偶发卡顿。下一步最该做什么?
elbencho 跑 12 块盘的顺序写,固定数据量、不加 --timelimit,结果 first done 4870 MiB/s、last done 2910 MiB/s。下一步最该做什么?
这节课的落点
- 先用零开销的静态工具收敛范围,再用 bpftrace 类工具挖根因
- elbencho 四件套:顺序读写测带宽、随机读写测 IOPS;
--direct必加,--timelimit时长要够 - 同一个 elbencho 加
--service/--hosts就能多客户端压共享文件系统,尤其是多文件元数据场景 - 结果表看两列:first done 是满载峰值,last done 是这次运行的聚合完成情况;差距超过 20% 先查不均衡(数据量太小、慢盘、切分不均、分布不均、后台任务),别急着记数字
- 判断硬件性能上限、做验收对比理论值只用 first done;拿被拖尾稀释过的 last done 去比理论值,会误判成"硬件不达标"
- 报告不能直接抄 last done:两列都写、注明数据量与时长、重复 3 次取中位数,结论给区间并解释差距来源
- 裸盘压测顺便用
lspci查 PCIe 是否 downgraded,这类硬件问题要在上线前发现 - 报告必须写清方法、环境和未覆盖的场景
L0 到此结束。你已经具备了在任何一台 Linux 机器上定位性能问题的基本能力——接下来把这套能力用到分布式存储上。
延伸资料
- Systems Performance, 2nd Edition — Brendan Gregg
- k8s-in-action
storage/elbencho/