Cgroup blkio简介和测试(使用fio测试)
因需要对docker镜像内的进程对磁盘读写的速度进行限制,研究了下Cgroup blkio,并使用fio对其iops/bps限速进行测试。
Cgroup blkio简介
Linux Cgroup(Control Groups)是Linux内核提供的用于限制、记录、隔离进程组可以使用的资源(cpu、memory、IO等)的一种机制。
在/boot下面的对应config文件里查看Cgroup内核选项:
CONFIG_BLK_CGROUP=y
CONFIG_BLK_DEV_THROTTLING=y
blkio子系统
Cgroup里每个子系统(SubSystem)对应一种资源,Cgroup blkio子系统用于限制块设备I/O速率。
挂载Cgroup root目录以及blkio子系统如下所示:
mount -t tmpfs cgroup_root /sys/fs/cgroup
mkdir /sys/fs/cgroup/blkio
mount -t cgroup -o blkio none /sys/fs/cgroup/blkio
在对应的子系统目录下通过mkdir创建资源组,rmdir可将对应资源组删除。通过将进程pid加入到资源组目录下的tasks文件的方式把某个进程加入到对应资源组。如下所示:
mkdir /sys/fs/cgroup/blkio/test
echo 9527 > /sys/fs/cgroup/blkio/test/tasks
blkio资源限制策略
blkio支持两种IO资源限制策略:IO调度权重和iops/bps限制
- IO调度权重
通过设置资源组IO的权重比例实现IO限制,该策略基于CFQ调度算法(Linux内核磁盘IO电梯算法)通过分配其IO处理的时间片来实现,因此需要确认磁盘对应的电梯算法为CFQ(Linux默认值)。
单个资源组权重取值范围100-1000。设置方法如下:
echo 500 > blkio.weight
echo "8:0 500" > blkio.weight_device
其中8:0为磁盘设备号(major:minor)。具体设备的weight_device权重的优先级高于weight默认权重。
IO调度权重使用CFQ调度算法,显得比较公平,其保障资源组最低的IO比例(对应iops有最低保证)。在设备空闲的时候,还能超限使用。因此不适用于需要严格限制资源组IO资源上限的场景。
- iops和bps限制
支持设置资源组读和写的iops、bps上限。这样在该资源组内的进程读写IO的iops和bps不会超出设置值。设置方法如下:
echo "8:0 102400" > blkio.throttle.read_bps_device
echo "8:0 10" > blkio.throttle.read_iops_device
echo "8:0 204800" > blkio.throttle.write_bps_device
echo "8:0 20" > blkio.throttle.write_iops_device
其中读bps限定在100KB,读iops限定在10。可以只设置读或者写的iops,或者bps,也可以都设置。
相比IO调度权重,iops和bps限制更加直接和量化,更适合用于限制docker容器磁盘IO上限。
使用fio测试blkio
当前3.3版本的fio支持配置cgroup进行测试,但只支持配置cgroup的weight值。
为了测试blkio iops/bps限制,在该版本fio基础上,添加了支持配置iops/bps的代码。可从github上直接下载编译修改后的fio。
blkio iops/bps功能测试
使用fio启动两个进程分属两个Cgroup进行blkio功能测试,fio配置文件如下:
[root@A03-R14-I156-1 jimbo]# cat test.fio
[global]
bs=4K
ioengine=libaio
iodepth=32
direct=1
rw=randrw
rwmixread=50
time_based
runtime=60
cgroup_nodelete=0
[test1]
filename=/export/kubelet/pods/802b34b1-eac7-11e7-bc92-246e9665d1b0/volumes/kubernetes.io~lvm/export/fio/test1.img
size=512M
cgroup_read_iops=8:16 100
cgroup_write_iops=8:16 100
cgroup_read_bps=8:16 1024000
cgroup_write_bps=8:16 1024000
cgroup=test1
[test2]
filename=/export/kubelet/pods/802b34b1-eac7-11e7-bc92-246e9665d1b0/volumes/kubernetes.io~lvm/export/fio/test2.img
size=512M
cgroup_read_iops=8:16 1000
cgroup_write_iops=8:16 1000
cgroup_read_bps=8:16 102400
cgroup_write_bps=8:16 102400
cgroup=test2
其中Cgroup设备号需要设置为物理磁盘(如sdb)或者lvm卷(如dm-5)的设备号,设置为磁盘分区(如sdb1)设备号或者loop设备号时是无效的。
对于docker镜像使用lvm磁盘时,测试结果显示设置设备号为lvm盘和物理磁盘的限速效果是一样的。
如果将bs设置过大,会导致buffered read/write时大量的IO在缓存命中,并没有实际提交给磁盘,此时fio的统计的iops/bps是不准的。
所以本次测试设置bs=4K,尽量避免缓存命中的情况发生。
启动fio测试:
[root@A03-R14-I156-1 jimbo]# ./fio ./test.fio
可直接查看fio输出读写IOPS和BW统计,需要注意有些场景(如buffered write)fio的输出并不准确,还需要结合iostat等磁盘统计工具进一步确认磁盘iops/bps的真实情况。
fio的输出如下:
test1: (groupid=0, jobs=1): err= 0: pid=619842: Fri Dec 29 16:28:41 2017
read: IOPS=98, BW=392KiB/s (402kB/s)(23.0MiB/60109msec)
...
write: IOPS=99, BW=396KiB/s (406kB/s)(23.3MiB/60109msec)
...
test2: (groupid=0, jobs=1): err= 0: pid=619843: Fri Dec 29 16:28:41 2017
read: IOPS=23, BW=94.7KiB/s (96.0kB/s)(5768KiB/60917msec)
...
write: IOPS=25, BW=100KiB/s (103kB/s)(6100KiB/60917msec)
...
可以看到test1被IOPS限制在了100,test2被bps限制在了100KiB/s。
使用该方法测试了randread、randwrite、randrw结果如下:
- DIO read、DIO write、buffered read都被成功限制住了iops/bps;
- buffered write却并没有被限制住;
Buffered write没有被限制住,是因为本次测试基于CentOS 7.2内核版本号为3.10.0,该版本内核blkio有如下描述:
Currently only sync IO queues are support. All the buffered writes are still system wide and not per group. Hence we will not see service differentiation between buffered writes between groups.
从YY哥分析的Buffer IO的throttle问题可以看到buffered写IO在内核异步线程提交时无法获取到用户进程信息,因此blkio无法支持buffered write的统计。该问题在4.2版本的Cgroup V2上得到了解决,该版本Cgroup相比V1做了很大改进并支持了writeback。详情可参看博客Cgroup V2 and writeback support。
blkio压力测试
通过如下脚本测试在同一个资源组上启动N个fio进程进行blkio压力测试:
#!/bin/bash
for ((N=0; N<100; N++))
do
./fio -filename=/export/kubelet/pods/802b34b1-eac7-11e7-bc92-246e9665d1b0/volumes/kubernetes.io~lvm/export/fio/test$N.img -size=256M -rw=randrw -ioengine=libaio -iodepth=32 -bs=4K -direct=1 -rwmixread=50 -time_based -runtime=60 -cgroup_nodelete=0 -cgroup=test -cgroup_read_iops="8:16 10000" -cgroup_write_iops="8:16 10000" -cgroup_read_bps="8:16 10240000" -cgroup_write_bps="8:16 10240000" -name=test$N -output=./output$N &
done
sleep 60
测试结果显示,N=100个进程测试时,所有IO总和都被限定在了test资源组设置的iops和bps下。
修改上述脚本中-cgroup=test
为-cgroup=test$N
,即可进行在N个资源组上启动N个fio进程进行blkio压力测试。
测试结果显示,当IO超出物理磁盘的能力时,每个资源组上的进程都无法达到iops和bps的限制,基本上比较平均的占用磁盘带宽。