Cgroup V2 IO 控制器基准测试:io.max、io.weight 与 iocost 实测分析

本文记录在生产级 Kubernetes 节点上对 Cgroup V2 IO 控制器的系统性基准测试,覆盖 io.max(带宽/IOPS 硬限)、io.weight(权重调度)、io.cost.qos(延迟模型 QoS)三类控制机制,以及 Direct IO vs Buffer IO、raw disk vs LVM、ext4 vs xfs 等不同组合下的实测数据。

测试环境

  • OS:Ubuntu 22.04 LTS(kernel 5.15)
  • Kubernetes:v1.23
  • 存储:3 块 1.7T SSD(sda/sdb/sdc),LVM VG 横跨三块盘
  • 测试工具:fio + 自定义 IO 统计脚本

磁盘布局:

1
2
3
4
5
6
7
8
sda  1.7T
└─sda4 1.3T → LVM PV
sdb 1.7T
└─sdb1 1.7T → LVM PV
sdc 1.7T
└─sdc1 1.7T → LVM PV

VG: vg10000(由 sda4 + sdb1 + sdc1 组成)

FIO 基础命令:

1
2
3
4
5
fio -filename=/mnt/test/xfs/testfile \
-direct=0 -iodepth 64 -thread -rw=write \
-ioengine=libaio -bs=4k -size=20G \
-numjobs=1 -group_reporting --runtime=100 \
-name=mytest

裸盘基准性能

4K 顺序读写 读 BW 读 IOPS 写 BW 写 IOPS
Ext4 Direct 359 MB/s 89,887 391 MB/s 97,825
Ext4 Buffer 336 MB/s 84,110 728 MB/s 182,086
XFS Direct 360 MB/s 89,997 400 MB/s 100,005
XFS Buffer 300 MB/s 74,910 780 MB/s 194,982

Buffer IO 的写带宽因 page cache 聚合写入而显著高于 Direct IO。


io.max 限速测试(Case 1)

单 volume,单线程,对单设备设置 io.max

Case IO 类型 文件系统 层级 配置 实测写 BW 实测写 IOPS 磁盘利用率
1-1 Direct ext4 raw wbps=100MB/s 100 MB/s 21,113 31%
1-2 Direct xfs raw wbps=100MB/s 100 MB/s 13,382 28%
1-3 Buffer ext4 raw wbps=100MB/s 100 MB/s 90 25%
1-4 Buffer xfs raw wbps=100MB/s 100 MB/s 90 25%
1-5 Direct xfs raw wiops=1000 4 MB/s 1,000 100%
1-6 Buffer xfs raw wiops=10 12.6 MB/s 14.5 3%
1-7 Direct xfs LVM wiops=1000 0.4 MB/s 100 4%
1-8 Buffer xfs LVM wiops=100 475 MB/s 2 1.6%
1-9 Direct xfs LVM wbps=100MB/s 100 MB/s 25,570 28%
1-10 Buffer xfs LVM wbps=100MB/s 100 MB/s 2 20%

关键发现:Buffer IO 的 IOPS 限速不准确

Case 1-8(Buffer IO,LVM,wiops=100):实测 IOPS 仅为 2,但 BPS 达到 475 MB/s,远超预期。Case 1-6 类似(wiops=10,实测 14.5 IOPS)。

原因:Buffer IO 的 dirty page writeback 使用的实际 block size 并非 fio 设置的 4K,而是由内核的 writeback 聚合策略决定的(通常更大)。内存越少,dirty page 刷盘频率越高,block size 越小;内存充足时,多个小写入被合并为大块写入,导致实际 IOPS 远低于配置值,BPS 却可能超出限制。

结论io.max 的 BPS 限速(wbps)在 Buffer IO 下准确,IOPS 限速(wiops)在 Buffer IO 下不可靠。


跨设备与多 Volume 测试(Case 2)

Case 配置 Device 1 实测 Device 2 实测 LVM 总计
2-1 dev1=100MB/s, dev2=无限 104 MB/s, 83 IOPS 489 MB/s, 446 IOPS IOPS 在 [0,100] 波动
2-2 两设备各 100MB/s 100 MB/s, 100 IOPS 100 MB/s, 100 IOPS 100 MB/s, [5,70] IOPS

跨设备 LVM volume 的限速以底层设备为粒度,LVM 层级的 IOPS 由于条带化导致数值波动较大。


io.weight 权重调度测试(Case 3)

两个 LVM volume,两个 fio 线程,测试 io.weight 比例调度效果。

Case 场景 Volume 1 Volume 2 磁盘总计
3-1.1 Buffer,无限制 250 MB/s 250 MB/s 500 MB/s, 100% util
3-1.2 Direct,无限制 220 MB/s 220 MB/s 440 MB/s, 100% util
3-2 Direct,weight 100:300,user qos 206 MB/s 209 MB/s 420 MB/s
3-4 Buffer,weight 100:300,user qos 129 MB/s 368 MB/s 500 MB/s
3-6 Direct,weight 100:900,user qos 153 MB/s 155 MB/s 304 MB/s
3-8 Buffer,weight 100:900,user qos 57 MB/s 442 MB/s 500 MB/s

关键发现:Direct IO 的 io.weight 在默认 iocost.qos 下无效

Case 3-2 和 3-6:权重设置为 100:300 和 100:900,但两个 volume 的实际带宽几乎相等(206 vs ~209,153 vs ~155)。

原因:默认的 io.cost.qos 会根据延迟自动调整权重(ctrl=auto),当 Direct IO 让磁盘达到 100% 利用率时,内核为了满足延迟目标会忽略用户设置的权重比例,重新分配。

Buffer IO 下权重调度效果正常(3-4: 129 vs 368 ≈ 1:2.8;3-8: 57 vs 442 ≈ 1:7.8),接近预期的 1:3 和 1:9。

结论io.weight 在 Buffer IO 下效果良好;在 Direct IO 且磁盘满负载时,需要禁用 iocost.qos 的自动调整(设置 min=max=100)才能生效。


iocost QoS 精确控制测试(Case 6)

禁用 iocost 自动调整(min=max=100),两个 volume 权重 100:900。

Case 附加配置 Volume 1 Volume 2 磁盘总计
6-1 无 io.max 22 MB/s, 83% util 309 MB/s, 85% util 322 MB/s
6-2 无 io.max,权重 100:300 127 MB/s 291 MB/s 405 MB/s
6-3 io.max 各 150MB/s 18 MB/s 145 MB/s 168 MB/s
6-4 io.max 各 100MB/s 67 MB/s 102 MB/s 164 MB/s
6-5 v2 限制 10MB/s 48 MB/s 10 MB/s 58 MB/s

Case 6-1 权重 100:900 的实际比例约为 1:14(22 vs 309),超过预期 1:9。这是因为磁盘利用率约 85%,iocost 轻微调整了分配比例。禁用自动调整后(min=max=100),权重比例更接近配置值。


三 Volume 权重测试(Case 7)

三个 LVM volume,权重配置不同,io.cost.qos 设置 min=max=100(禁用自动调整)。

Case 权重配置 Vol 1 Vol 2 Vol 3 总计
7-1 100:900:900 38 MB/s 6 MB/s 191 MB/s 235 MB/s
7-3 100:900:900,v2 限 10MB/s 10 MB/s 9.6 MB/s 153 MB/s 172 MB/s
7-4 100:900:900,ctrl=auto 20 MB/s 112 MB/s 122 MB/s 254 MB/s
7-5 100:300:900 9.7 MB/s 30 MB/s 155 MB/s 194 MB/s
7-6 100:300:600 14 MB/s 43 MB/s 118 MB/s 175 MB/s
7-9 无权重配置(基准) 121 MB/s 119 MB/s 119 MB/s 359 MB/s

发现

  1. 7-4 使用 ctrl=auto 时,权重 900 的 vol3 实测 122 MB/s,仅略高于 vol2(112 MB/s),比例远不是 1:9,自动调整干扰了权重效果
  2. 设置 io.weight 后总带宽下降(235 vs 359 MB/s),io.cost.qos 可以缓解但无法完全消除
  3. 7-2(ctrl=auto,无 min=max)导致节点出现 CPU 软锁,测试中断

大规模 Pod 场景测试(Case 5 & 9)

100 个 Pod,每 Pod 10 个 fio 线程,测试 IO 调度器和 iocost 对节点整体的影响。

Case 5:不同 IO 调度器对比

场景 Load Avg CPU D-state 进程 节点 IO PSI 总 IO 带宽
无限制(mq-deadline) 900 100% 30 910ms 300 MB/s
io.max 限速(mq-deadline) 548 68.4% 0 450ms 200 MB/s
io.max 限速(bfq) 700 93.4% 0 791ms 150 MB/s

Case 9:容器数量对 IO 吞吐的影响

不同 IO 调度器和控制策略下,随容器数量增加的写入吞吐量:

场景 1容器 2容器 6容器 18容器 50容器
SSD xfs,bfq 500 MB/s 500 MB/s 220 MB/s 220 MB/s 300 MB/s
SSD xfs,mq-deadline 500 MB/s 500 MB/s 500 MB/s 500 MB/s 500 MB/s
SSD xfs,bfq+iocost 250 MB/s 300 MB/s 340 MB/s 16~490 MB/s 35~491 MB/s
SSD xfs,bfq+ioweight 40 MB/s 42 MB/s 65 MB/s 500 MB/s 500 MB/s
NVMe xfs,bfq 830 MB/s 900 MB/s 810 MB/s 850 MB/s 750 MB/s

关键发现

  1. BFQ 调度器在多容器下吞吐量下降显著:SSD 环境下从 500 MB/s 降至 220 MB/s(6容器),而 mq-deadline 全程保持 500 MB/s 稳定
  2. iocost 导致 IO 不稳定:启用 iocost 后,IO 吞吐在 16~490 MB/s 之间剧烈波动,某些情况下长时间低 IO 才能恢复(见 iocost 测试图示)
  3. NVMe 上 BFQ 相对稳定:NVMe 的高 IOPS 特性让 BFQ 的调度开销相对较小,多容器下吞吐下降幅度可接受

总结与使用建议

功能 场景 建议
io.max BPS 限速 Direct IO / Buffer IO 可靠,直接使用
io.max IOPS 限速 Direct IO 可靠
io.max IOPS 限速 Buffer IO 不可靠,慎用
io.weight Buffer IO 效果良好
io.weight Direct IO,磁盘满载 需配合 min=max=100 关闭 iocost 自动调整
io.cost.qos ctrl=auto 任何场景 高并发下可能导致 CPU 软锁,生产谨慎使用
IO 调度器 多容器高并发 mq-deadline 比 bfq 更稳定,bfq 适合需要权重调度的场景

核心结论:Cgroup V2 的 IO 控制器功能强大,但各机制之间存在相互影响(iocost 会覆盖 io.weight,BFQ 在高并发下有性能衰减)。生产环境建议从 io.max BPS 限速入手,待充分测试后再逐步引入权重和 iocost 功能。