背景
容器与宿主机共享磁盘空间,如果不做容量限制,会存在写满宿主机的可能
# 下面的就是在容器中查看到的,实际的空间量就是宿主机上的
$ df -h
Filesystem Size Used Avail Use% Mounted on
overlay 49G 22G 25G 47% /
overlay文件系统中并没有直接限制磁盘quota的功能
当使用docker时限制的操作,也就是用 XFS Quota 来限制容器的 OverlayFS 大小,限制upperdir的目录大小
swarm模式下不支持限制容器的磁盘quota(或者暂时不知道🤔
# docker模式下
$docker run -it --rm --storage-opt size=10M ubuntu:22.10 bash
docker: Error response from daemon: --storage-opt is supported only for overlay over xfs with 'pquota' mount option.
查看docker源码中会发现使用了xfs的project机制,下面是核心代码
// moby/quota/projectquota.go
// setProjectQuota - set the quota for project id on xfs block device
func setProjectQuota(backingFsBlockDev string, projectID uint32, quota Quota) error {
var d C.fs_disk_quota_t
d.d_version = C.FS_DQUOT_VERSION
d.d_id = C.__u32(projectID)
d.d_flags = C.XFS_PROJ_QUOTA
d.d_fieldmask = C.FS_DQ_BHARD | C.FS_DQ_BSOFT
d.d_blk_hardlimit = C.__u64(quota.Size / 512)
d.d_blk_softlimit = d.d_blk_hardlimit
var cs = C.CString(backingFsBlockDev)
defer C.free(unsafe.Pointer(cs))
_, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, C.Q_XSETPQLIM,
uintptr(unsafe.Pointer(cs)), uintptr(d.d_id),
uintptr(unsafe.Pointer(&d)), 0, 0)
if errno != 0 {
return errors.Wrapf(errno, "failed to set quota limit for projid %d on %s",
projectID, backingFsBlockDev)
}
return nil
}
docker环境下实验
前面看到不做特殊处理的docker是无法使用磁盘quota限制的
$ docker info
...
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
userxattr: false
...
目前后端的文件系统是extfs,需要切换至xfs
$ docker info | grep "Docker Root Dir"
Docker Root Dir: /var/lib/docker
$ df -T -h /var/lib/docker
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda3 ext4 49G 22G 25G 47% /
蚌埠住了, docker直接安装在的根目录,文件系统是ext4,也就是必须重新安装docker在xfs文件系统的挂载点上
在xfs文件系统上实验
实验一下手动限制
要使用 XFS Quota 特性,必须在文件系统挂载的时候加上对应的 Quota 选项,比如需要配置 Project Quota,那么这个挂载参数就是"pquota"。
对于根目录来说,这个参数必须作为一个内核启动的参数"rootflags=pquota",这样设置就可以保证根目录在启动挂载的时候,带上 XFS Quota 的特性并且支持 Project 模式。
由于我的根目录是ext4文件格式,所以新创建一个分区,格式化为xfs系统,并且配置为project quota
This disk is currently in use - repartitioning is probably a bad idea. It's recommended to umount all file systems, and swapoff all swap partitions on this disk. 为正在使用的磁盘建立分区不是一个好主意,要么新建一个磁盘,我这里由于使用的是虚拟机,就用虚拟机添加一块硬盘
$ sudo apt-get install xfsprogs # xfs文件系统相关工具集
$ fdisk -l
$ fdisk /dev/sdb
Command (m for help): n <- 新建分区
Command action <- 选择要创建的分区类型
e extended <- 扩展分区
p primary partition (1-4) <- 主分区
p <- 输入建立主分区
Partition number (1-4):1 <- 分区编号
First sector (2048-20971519, default 2048): <- 直接回车默认
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-20971519, default 20971519): +1G <-分区大小
Command (m for help): w <- 保存分区表, 完毕会退出fdisk命令
The partition table has been altered!
Calling ioctl() to re-read partition table.
Syncing disks.
$ fdisk /dev/sdb # 查看分区是否创建成功
Command (m for help): p <- 打印当前磁盘的分区信息
...
Device Boot Start End Sectors Size Id Type
/dev/sdb1 2048 2099199 2097152 1G 83 Linux
...
$ mkfs.xfs /dev/sdb1 # 为分区格式化文件系统, 如果出现`The device apparently does not exist; did you specify it correctly?`,需要重启系统然后格式化
挂载分区,要使用Project Quota 功能,需要在挂载时指定-o prjquota
参数,并且这个参数还不能和usrquota、grpquota一起使用,也就是说,如果开启Project Quota就无法使用针对用户和用户组的配额限制。
$ rm -rf /xfstest && mkdir /xfstest && mount -o prjquota /dev/sdb1 /xfstest
$ rm -rf /xfstest && mkdir /xfstest && mount -o pquota /dev/sdb1 /xfstest
$ df -lhT
Filesystem Type Size Used Avail Use% Mounted on
...
/dev/sdb1 xfs 1014M 40M 975M 4% /xfstest
$ cat /proc/mounts | grep prjquota # 检验是否生效
/dev/sdb1 /xfstest xfs rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,prjquota 0 0
给一个指定的目录打上一个 Project ID,对打好project id的做上资源限制
$ mkdir -p /xfstest/xfs_prjquota
$ xfs_quota -x -c 'project -s -p /xfstest/xfs_prjquota 1' # 为文件夹分配ProjectID 1
$ xfs_quota -x -c 'limit -p bhard=10m 1' /xfstest # 后面的/xfstest就是这个xfs文件系统的挂载点
实验一下能否完成资源限制
$ dd if=/dev/zero of=/xfstest/xfs_prjquota/test.file bs=1024 count=20000 # 20000个1024大小的区块,20m
dd: error writing '/xfstest/xfs_prjquota/test.file': No space left on device
10241+0 records in
10240+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0.0514849 s, 204 MB/s
$ du -h /xfstest
10M /xfstest/xfs_prjquota
10M /xfstest
实验成功
docker部分重新安装那代价有点大,下次有空再试😂