wwqdrh

背景

容器与宿主机共享磁盘空间,如果不做容量限制,会存在写满宿主机的可能

# 下面的就是在容器中查看到的,实际的空间量就是宿主机上的
$ 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部分重新安装那代价有点大,下次有空再试😂