文件系统隔离之 – 初识 prjquota,原理、实践

prjquota 的前身其实是 subtreequota,最早由 openvz 提出,2010年之后好像就没有消息了,没有进入内核主干,有点遗憾。后来我们移植过一次,但是由于设计过于复杂,功能不稳定,并且缺少社区的技术支持,最终选择了放弃。

prjquota 是 xfs 文件系统的一个原生特性,其设计简单,功能健壮。并且有人尝试把他移植到了 ext4 文件系统上。4系内核已经进入主干

prjquota 功能和 subtreequota 一样,能够限制一组具有相同 prjid 属性的文件的总大小。这些具有相同属性的 prjid,可能散落在不同的目录下,但属于同一个项目的文件拥有一个想同的project id标示,正如同一个用户的文件,或者同一个用户组的文件有相同的UserID,或者GroupID

具体实现,可以参考内核 patch:https://lwn.net/Articles/671627/

1)使能 prjquota 特性

磁盘project quota初始化,如下任意一种方法都可以:

  • 重新格式化一个磁盘来支持project quota: /root/ext4/e2fsprogs/misc/mke2fs /dev/hdb -O quota,project
  • 或者在已有的磁盘上使能project quota:/root/ext4/e2fsprogs/misc/tune2fs /dev/hdb  -O quota,project

mount设备支持project quota:

  • mount -t ext4 -o prjquota /dev/hdb xxxx/   或者:
  • mount -t ext4 /dev/hdb xxxx/; /root/ext4/quota-tools/quotaon  xxxx/,但是这个方法,需要在磁盘上没有任何文件被打开的时候才能执行

创建 project id和quota限制管理

  1. 设置一个目录属于一个project id:/root//ext4/e2fsprogs/misc/chattr -p 1001 xxxx/test1
  2. 使得这个目录下的文件默认继承这个project id:/root//ext4/e2fsprogs/misc/chattr  +P xxxx/test1
  3. 设置project的配额:/root/ext4/quota-tools/setquota -P 1001 100 100 400 500 xxxx,可以重复设置,例如更新quota,立即生效

2)使用测试

project id的继承:

sh-3.00# touch xxxx/test1/a1
sh-3.00# touch xxxx/test1/a2
sh-3.00# lsattr 
sh-3.00# /root//ext4/e2fsprogs/misc/lsattr -p xxxx/test1/
1001 ------------------P xxxx/test1/a1
1001 ------------------P xxxx/test1/a2
sh-3.00#

限额测试

sh-3.00# dd if=/dev/zero of=xxxx/test1/xxx bs=1024 count=101 
hdb: write failed, project block limit reached.
dd: writing `xxxx/test1/xxx': Disk quota exceeded
97+0 records in
96+0 records out
sh-3.00#

观测限额使用情况

sh-3.00# /root/ext4/quota-tools/quota -P 1001
Disk quotas for project #1001 (pid 1001): 
Filesystem blocks quota limit grace files quota limit grace
/dev/hdb 100* 100 100 4 400 500 
sh-3.00#

3)性能

prjquota 的原理是在文件写入的时候,检查文件所属的 prjquota 是否已经超限,这个检查是极其轻量的,几乎不会发生性能耗损(除非存在大量文件并发读写试,内核锁竞争的问题)

所以理论上来说,prjquota 性能可以保持跟原生文件系统一致。这个在我们的实际测试结果中,也是比较符合预期的

IO类型\设备类型 Block size ext4 & no quota ext4 & prj quota
随机读 4k 147 148
随机读 16k 145 146
随机读 32k 137 138
随机读 64k 126 125
随机读 128k 108 106
随机读 256k 82 83
顺序读 4k 6083 6267
顺序读 16k 2429 2484
顺序读 32k 1295 1271
顺序读 64k 599 652
顺序读 128k 376 373
顺序读 256k 188 185
随机写 4k 141 148
随机写 16k 138 143
随机写 32k 130 135
随机写 64k 121 168
随机写 128k 104 110
随机写 256k 87 122
顺序写 4k 10731 11518
顺序写 16k 2480 2549
顺序写 32k 1287 1329
顺序写 64k 682 649
顺序写 128k 328 355
顺序写 256k 169 175

4)一些约定、一些坑

  1. project id有继承性,如果一个目录没有使能project id的继承,在这个路径下,新建的文件的project id为0,否者将继承目录的project id
  2. 对于系统调用rename(), 如果oldpath和newpath不属于同一个project id,rename将失败。但是对于使用这个系统调用的程序,比如mv是不会失败的,因为mv调用rename失败后,将通过创建文件,读老的文件内容,写内容到新的文件的方式达到move的效果。在实际落地过程中,我们修改了内核实现,允许了跨 prjquota mv 文件
    1. 如果目标没有projectid,则rename是可以的
  3. 如果创建一个文件的硬连接到一个目录下,但这个目录和文件的project id不同,创建将失败
  4. project id是文件系统域的,不同的文件系统可以有相同的projectid,之间没有关系
  5. 文件系统相关的工具链需要升级:例如fsck,否则老版本的fsck会破坏project quota的元数据
  6. 如果不是通过格式化,而是tune2fs方式打开磁盘的prjquota功能,对于磁盘上的一些老的文件系统数据,可能不支持prjquota属性扩展,需要对所有老文件全部 touch 一遍才行,touch一遍的时候,内核会修改文件的扩展属性,增加 prjquota 扩展字段
发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注