xfs_quota

xfs_quotaxfsprogs 提供,安装命令:yum install xfsprogs -y

  • 类型:磁盘容量、文件数(Inode)
  • 方式:软限制、硬限制。硬限大于等于软限制,否者软限制失效
    1
    xfs_quota -x -c 'limit [-ug] bsoft=N bhard=N isoft=N ihard=N <name>' <mount-point>
    说明:
  • -x 专家模式,允许对配额系统进行修改
  • -c 子命令选项,依赖专家模式
  • 子命令:
    • df -b (block) -i (inode) -h
    • timer 宽限时间
    • limit
      • -u:对用户限制
      • -g:对组限制
      • bsoft:磁盘容量软限制
      • bhard:磁盘容量硬限制
      • isoft:文件数量软限制
      • ihard:文件数量硬限制
    • report 列出quota项目
      • -u:对用户查看
      • -g:对组查看
      • -a:查看所有可用分区的配额使用报告
      • -b:查看磁盘容量
      • -i:查看文件数
    • print 当前系统参数
    • state 列出当前系统信息和相关的启动项
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      xfs_quota -x -c "timer [-ug] [-bir] Ndays"
      xfs_quota -x -c 'df'
      xfs_quota -x -c 'df -b'
      xfs_quota -x -c 'df -b -i'
      xfs_quota -x -c 'df -b -i -h'
      xfs_quota -x -c 'print'
      xfs_quota -x -c 'report'
      xfs_quota -x -c 'report -ugr' <mount-point> # user/group/project
      xfs_quota -x -c 'report -abi' <mount-point>
      xfs_quota -x -c 'state'

os设置配额

  1. 创建用户组及用户
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # cat myquota.sh
    #!/bin/bash
    groupadd myquotagrp
    for username in myquota1 myquota2 myquota3 myquota4 myquota5
    do
    useradd -g myquotagrp $username
    echo "$username:password" | chpasswd
    done

    mkdir /home/ruike/quota_test_dir
    chgrp myquotagrp /home/ruike/quota_test_dir
    chmod 2770 /home/ruike/quota_test_dir

    查看
    drwxrws--- 2 root myquotagrp 6 Dec 27 16:12 quota_test_dir/

    所属myquotagrp组,否则切换到不同用户时,没有权限写入
  2. 查看挂载目录状况
    注意: xfs, usrquota,grpquota
    1
    2
    # mount | grep quota_test_dir
    /dev/mapper/ubuntu--vg-docker--lv--ext4 on /home/ruike/quota_test_dir type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,usrquota,grpquota)

quota限制 for user and group

要求: 5个用户共属一组, 每个用户250/300M的容量限制, 组总容量950M/1G容量, grace time 14天

  1. 对用户进行限制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    xfs_quota  -xc 'limit -u bsoft=250M bhard=300M myquota1' /home/ruike/quota_test_dir/
    xfs_quota -xc 'limit -u bsoft=250M bhard=300M myquota2' /home/ruike/quota_test_dir/
    xfs_quota -xc 'limit -u bsoft=250M bhard=300M myquota3' /home/ruike/quota_test_dir/
    xfs_quota -xc 'limit -u bsoft=250M bhard=300M myquota4' /home/ruike/quota_test_dir/
    xfs_quota -xc 'limit -u bsoft=250M bhard=300M myquota5' /home/ruike/quota_test_dir/

    # xfs_quota -xc 'report -ubih '
    User quota on /home/ruike/quota_test_dir (/dev/mapper/ubuntu--vg-docker--lv--ext4)
    Blocks Inodes
    User ID Used Soft Hard Warn/Grace Used Soft Hard Warn/Grace
    ---------- --------------------------------- ---------------------------------
    root 0 0 0 00 [------] 3 0 0 00 [------]
    myquota1 0 250M 300M 00 [------] 0 0 0 00 [------]
    myquota2 0 250M 300M 00 [------] 0 0 0 00 [------]
    myquota3 0 250M 300M 00 [------] 0 0 0 00 [------]
    myquota4 0 250M 300M 00 [------] 0 0 0 00 [------]
    myquota5 0 250M 300M 00 [------] 0 0 0 00 [------]
  2. 对组进行限制

1
2
3
4
5
6
7
8
9
# xfs_quota -xc 'limit -g bsoft=950M bhard=1G myquotagrp' /home/ruike/quota_test_dir/

# xfs_quota -xc 'report -gibh'
Group quota on /home/ruike/quota_test_dir (/dev/mapper/ubuntu--vg-docker--lv--ext4)
Blocks Inodes
Group ID Used Soft Hard Warn/Grace Used Soft Hard Warn/Grace
---------- --------------------------------- ---------------------------------
root 0 0 0 00 [------] 3 0 0 00 [------]
myquotagrp 0 950M 1G 00 [------] 0 0 0 00 [------]
  1. 设置grace time
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # xfs_quota -xc 'timer -b -u 14days' /home/ruike/quota_test_dir/
    # xfs_quota -xc 'timer -b -g 14days' /home/ruike/quota_test_dir/

    # xfs_quota -xc state
    User quota state on /home/ruike/quota_test_dir (/dev/mapper/ubuntu--vg-docker--lv--ext4)
    Accounting: ON
    Enforcement: ON
    Inode: #131 (2 blocks, 2 extents)
    Group quota state on /home/ruike/quota_test_dir (/dev/mapper/ubuntu--vg-docker--lv--ext4)
    Accounting: ON
    Enforcement: ON
    Inode: #132 (2 blocks, 2 extents)
    Project quota state on /home/ruike/quota_test_dir (/dev/mapper/ubuntu--vg-docker--lv--ext4)
    Accounting: OFF
    Enforcement: OFF
    Inode: N/A
    Blocks grace time: [14 days]
    Inodes grace time: [7 days]
    Realtime Blocks grace time: [7 days]
  2. 测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    root@ubu2004:/home/ruike/quota_test_dir#  su myquota1
    # dd if=/dev/zero of=quota1.img bs=1M count=260
    切换到root 用户

    root@ubu2004:/home/ruike/quota_test_dir# su myquota2
    # dd if=/dev/zero of=quota2.img bs=1M count=500

    切换到root 用户
    root@ubu2004:/home/ruike/quota_test_dir# ll
    -rw-r--r-- 1 myquota1 myquotagrp 272629760 Jan 5 12:09 quota1.img
    -rw-r--r-- 1 myquota2 myquotagrp 314572800 Jan 5 12:10 quota2.img

    root@ubu2004:/home/ruike/quota_test_dir# xfs_quota -xc 'report -bihu'
    User quota on /home/ruike/quota_test_dir (/dev/mapper/ubuntu--vg-docker--lv--ext4)
    Blocks Inodes
    User ID Used Soft Hard Warn/Grace Used Soft Hard Warn/Grace
    ---------- --------------------------------- ---------------------------------
    root 0 0 0 00 [0 days] 3 0 0 00 [------]
    myquota1 260M 250M 300M 00 [13 days] 1 0 0 00 [------]
    myquota2 300M 250M 300M 00 [13 days] 1 0 0 00 [------]
    myquota3 0 250M 300M 00 [------] 0 0 0 00 [------]
    myquota4 0 250M 300M 00 [------] 0 0 0 00 [------]
    myquota5 0 250M 300M 00 [------] 0 0 0 00 [------]

可以看到,
quota1使用了260M, warn/grace是13天, 13天后, 其hard会变为260m
quota2即使dd了500M, 这里也只用了300M 写超过的部分并不会hang住

针对目录设置quota

更改项目, prjquotausr/grpquota不能同时设置

1
2
3
4
5
6
7
# cat /etc/fstab
/dev/mapper/ubuntu--vg-docker--lv--ext4 /home/ruike/quota_test_dir/ xfs defaults,prjquota 0 0
# umount /home/ruike/quota_test_dir/
# mount -a

# mount | grep prjquota
/dev/mapper/ubuntu--vg-docker--lv--ext4 on /home/ruike/quota_test_dir type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,prjquota)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# echo '11:/home/ruike/quota_test_dir' >> /etc/projects  //  指定专案识别码与目录的对应11是识别码, 可以随便取, 一定要放到etc下的这个文件里
# echo 'myquotaproject:11' >> /etc/projid # 规范专案名称与识别码的对应, 文件也是固定的

# xfs_quota -xc 'project -s myquotaproject' # 初始化专案名称
Setting up project myquotaproject (path /home/ruike/quota_test_dir)...
Processed 1 (/etc/projects and cmdline) paths for project myquotaproject with recursion depth infinite (-1).

# xfs_quota -xc print
Filesystem Pathname
/home/ruike/quota_test_dir /dev/mapper/ubuntu--vg-docker--lv--ext4 (pquota)
/home/ruike/quota_test_dir /dev/mapper/ubuntu--vg-docker--lv--ext4 (project 11, myquotaproject)

#设置限制
# xfs_quota -xc 'limit -p bsoft=450m bhard=500m myquotaproject' /home/ruike/quota_test_dir/

查看
# xfs_quota -xc 'report -bih'
Project quota on /home/ruike/quota_test_dir (/dev/mapper/ubuntu--vg-docker--lv--ext4)
Blocks Inodes
Project ID Used Soft Hard Warn/Grace Used Soft Hard Warn/Grace
---------- --------------------------------- ---------------------------------
#0 0 0 0 00 [------] 2 0 0 00 [------]
myquotaproject 0 450M 500M 00 [------] 1 0 0 00 [------]

测试

1
2
3
4
5
6
7
8
9
10
11
root@ubu2004:~# fallocate -l 500M /home/ruike/quota_test_dir/500Mfile
fallocate: fallocate failed: No space left on device

root@ubu2004:~# fallocate -l 400M /home/ruike/quota_test_dir/400Mfile
root@ubu2004:~# xfs_quota -xc 'report -bih'
Project quota on /home/ruike/quota_test_dir (/dev/mapper/ubuntu--vg-docker--lv--ext4)
Blocks Inodes
Project ID Used Soft Hard Warn/Grace Used Soft Hard Warn/Grace
---------- --------------------------------- ---------------------------------
#0 0 0 0 00 [------] 2 0 0 00 [------]
myquotaproject 400M 450M 500M 00 [------] 3 0 0 00 [------]

额外指令

1
2
3
4
5
6
// enable/disable
xfs_quota -x -c "disable -up" /home/ruike/quota_test_dir
// off后要重新挂载才能进行quota操作
xfs_quota -x -c "off -up" /home/ruike/quota_test_dir
// 移除所有project
xfs_quota -x -c "remove -p" /home/ruike/quota_test_dir

docker 全局配额设置

开启容器的配额限制

1
2
3
4
5
"storage-driver": "overlay2", // 使用哪个存储驱动
"storage-opts": [ // 驱动的其他option设置
"overlay2.size=50M",
"overlay2.override_kernel_check=true"
]

查看两个容器的都被限制住

1
2
3
4
5
6
7
8
root@ubu2004:~# docker exec -it test1 bash
root@415bba651ed0:/# df -Th
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 50M 20K 50M 1% /‘
root@ubu2004:~# docker exec -it test2 bash
root@415bba651ed0:/# df -Th
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 50M 46M 5.0M 91% /

临时禁用配额限制

1
# xfs_quota -x -c "disable -up" /home/ruike/quota_test_dir

此时进入两个容器, 可以看到限制被禁用了

1
2
3
4
# docker exec -it test2 bash
root@e6fabc995b51:/# df -Th
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 31G 533M 31G 2% /

所以project的含义是对不同的目录

删除Quota限制

1
2
3
4
5
root@ubu2004:~# xfs_quota -x -c "off -up" /home/ruike/quota_test_dir
root@ubu2004:~# xfs_quota -x -c "remove -p" /home/ruike/quota_test_dir
// 查看不到配额了
root@ubu2004:~# xfs_quota -x -c "state"

off后要重新挂载才能进行quota操作

容器粒度配额限制

1
2
3
4
5
6
7
8
9
10
11
# docker run -itd --storage-opt size=30M --name ruike2 nginx
root@ubu2004:~# docker exec -it ruike2 bash
root@3b96569f34c1:/# df -Th
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 30M 16K 30M 1% /

# docker run -itd --storage-opt size=20M --name ruike1 nginx
# docker exec -it ruike1 bash
root@764b4db8ed48:/# df -Th
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 20M 16K 20M 1% /
1
2
3
4
5
6
7
8
9
10
root@ubu2004:~# xfs_quota -xc 'report -bih'
Project quota on /home/ruike/quota_test_dir (/dev/mapper/ubuntu--vg-docker--lv--ext4)
Blocks Inodes
Project ID Used Soft Hard Warn/Grace Used Soft Hard Warn/Grace
#17 8K 50M 50M 00 [------] 17 0 0 00 [------]
#18 20K 50M 50M 00 [------] 23 0 0 00 [------]
#19 8K 20M 20M 00 [------] 17 0 0 00 [------]
#20 20K 20M 20M 00 [------] 23 0 0 00 [------]
#21 8K 30M 30M 00 [------] 17 0 0 00 [------]
#22 16K 30M 30M 00 [------] 21 0 0 00 [------]

每个容器是两条project限制, projectID是怎么关联到哪个容器的?

docker 配额实现

docker创建容器可设置--storage-driver=xxx --storage-opt size=yyy, 此配置优先级高于全局的配置

daemon/create.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr error) {
// 调用imageService 创建layer
rwLayer, err := daemon.imageService.CreateLayer(ctr, setupInitLayer(daemon.idMapping))
}

func (i *ImageService) CreateLayer(container *container.Container, initFunc layer.MountInit) (layer.RWLayer, error) {
var layerID layer.ChainID
// 将容器使用的镜像根文件系统的chainID作为layerID
if container.ImageID != "" {
img, err := i.imageStore.Get(container.ImageID)
if err != nil {
return nil, err
}
layerID = img.RootFS.ChainID()
}
}

func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWLayerOpts) (_ RWLayer, err error) {
// init 层
pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc, storageOpt)

// 可读可写层
if err = ls.driver.CreateReadWrite(m.mountID, pid, createOpts); err != nil {
return
}

}

github.com/docker/docker/layer/layer_store.go

1
2
3
4
5
6
7
8
func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc MountInit, storageOpt map[string]string) (string, error) {
// <data-root>/overlay/grapthID-init
initID := fmt.Sprintf("%s-init", graphID)
// 这一层也是可读可写层,只不过是特定的文件
if err := ls.driver.CreateReadWrite(initID, parent, createOpts); err != nil {
return "", err
}
}

在创建容器可读可写层时,分成了两步

  1. 创建init层,作为/etc/hosts, resolv.conf等的挂载层, initiD: <data-root>/overlay/grapthID-init
  2. 容器的可读可写层
    最后调用的都是ls.driver.CreateReadWrite()

docker的storage driver 支持 overlay2,aufs,devicemapper,vfs,zfs等(moby/daemon/graphdriver/)下,这里选用创建的overlay2
daemon/graphdriver/overlay2/overlay.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
...
if _, ok := opts.StorageOpt["size"]; ok && !projectQuotaSupported {
return fmt.Errorf("--storage-opt is supported only for overlay over xfs with 'pquota' mount option")
}
return d.create(id, parent, opts)

}

func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) {
// <data root>/overlay2/<RWlayerID>
dir := d.dir(id)

if opts != nil && len(opts.StorageOpt) > 0 {
driver := &Driver{}
if err := d.parseStorageOpt(opts.StorageOpt, driver); err != nil {
return err
}

if driver.options.quota.Size > 0 {
// Set container disk quota limit
if err := d.quotaCtl.SetQuota(dir, driver.options.quota); err != nil {
return err
}
}
}
}

github.com/docker/docker/quota/projectquota.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//	echo 999:/var/lib/docker/overlay2 >> /etc/projects
// echo docker:999 >> /etc/projid
// xfs_quota -x -c 'project -s docker' /<xfs mount point>

func (q *Control) SetQuota(targetPath string, quota Quota) error {
//
log.G(context.TODO()).Debugf("SetQuota(%s, %d): projectID=%d", targetPath, quota.Size, projectID)
// 这里的backingFsBlockDev, 就是/var/lib/docker/overlay2 目录对应的后端块设备
return setProjectQuota(q.backingFsBlockDev, projectID, quota)
}

// setProjectQuota - set the quota for project id on xfs block device
func setProjectQuota(backingFsBlockDev string, projectID uint32, quota Quota) error {
// 调用C包实现设置
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

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
}

我们可以上面的log查看到对应的打印 journal -u docker | grep projectID | grep <grapthID>, 以下是查看到的日志

1
2
Jan 05 13:19:35 ubu2004 dockerd[176096]: time="2024-01-05T13:19:35.859782918Z" level=debug msg="SetQuota(/home/ruike/quota_test_dir/overlay2/8568b8ee62483dee1f9aa1bb8165e5d5ba3c7b89a78f886a5d93ee9bc2f1c13f-init, 31457280): projectID=21"
Jan 05 13:19:35 ubu2004 dockerd[176096]: time="2024-01-05T13:19:35.871014600Z" level=debug msg="SetQuota(/home/ruike/quota_test_dir/overlay2/8568b8ee62483dee1f9aa1bb8165e5d5ba3c7b89a78f886a5d93ee9bc2f1c13f, 31457280): projectID=22"

每创建一个容器,docker会在container的可读可写层和init层两个目录设置project quota
<data-root>/overlay2/<grapthID><data-root>/overlay2/<graphID>-init

k8s 本地临时存储及弱配额实现

  1. 概况
    ephemeral-storage 分成csi管理的临时卷 和 kubelet管理的local 临时卷
    前者可以通过csi机制来设置容量,后者

local ephemeral-storage 可以由本地存储设备 或者 ram内存设备来提供。ram emptydir 虽然可以做临时存储(生命周期角度),但是不会统计到临时存储的磁盘(占用的内存),file emptyDir会占用磁盘空间

统计临时存储的使用量,有两种本地临时性存储的配置

  • 单一文件系统:所有临时存储都存放在同一个文件系统中
  • 双文件系统: 容器镜像和rootfs单独一个文件系统,前两种另一个文件系统

分文件系统是因为利用文件系统统计临时存储的空间使用量

kubelet 将仅统计临时存储的”根文件系统”。docker data root(/var/lib/docker)必须和kubelet(/var/lib/kubelet/) 位于同一个分区才能统计临时存储。
临时存储容量会影响到pod调度,kubelet会统计当前节点的kubelet分区的可分配的磁盘资源,在创建Pod时会根据存储需求调度到满足存储的节点,当pod实际使用超过申请的临时存储会进行驱逐操作,避免耗尽节点上的存储空间> kubectl describe node 可以看到节点临时存储的总容量

  1. 设置本地临时存储
    1
    2
    3
    4
    5
    resources:
    requests:
    ephemeral-storage: "200Mi"
    limits:
    ephemeral-storage: "400Mi"

哪些数据会占用本地临时存储? 一种3种

  • 容器日志(/var/log),写入容器 stdout, stderr 流,
  • emptyDir volume数据(存放在/var/lib/kubelet/…/volumes/kubernetes.io~emptyDir/…), tmpfs类型因为使用内存,不统计到本地临时存储(emptyDir:medium: "Memory", sizeLimit: 500Mi
  • 镜像层和容器可写层(/var/lib/docker)
  1. 如何度量 Pod 的存储用量?
  • 周期扫描上面三个路径
  • 项目配额(Project Quota,更优,更快)
    • 目录下所创建的所有文件都属于该项目,内核只需要跟踪该项目中的文件所使用的存储块个数

k8s使用的project quota IDs 会注册在 /etc/projects/etc/projid 文件中,

3.1 如何开启项目配额上报使用容量

1
2
1. kubelet --feature-gates LocalStorageCapacityIsolationFSQuotaMonitoring=true, 默认false
2. 临时存储文件系统是 xfs 且 with prjquota

项目配额可以帮你监视存储用量,但无法强制执行限制。

pkg/volume/util/fsquota/quota_linux.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func AssignQuota(m mount.Interface, path string, poduid types.UID, bytes *resource.Quantity) error {

id, err := createProjectID(path, oid)
if err == nil {
// 当启用强制配额时,也会以禁用配额
if ibytes > 0 {
ibytes = -1
}
if err = setQuotaOnDir(path, id, ibytes); err == nil {
quotaPodMap[id] = poduid
quotaSizeMap[id] = ibytes
podQuotaMap[poduid] = id
dirQuotaMap[path] = id
dirPodMap[path] = poduid
podDirCountMap[poduid]++
klog.V(4).Infof("Assigning quota ID %d (%d) to %s", id, ibytes, path)
return nil
}
}

}

所以emptyDir是弱限制配额,不会强制限制写入,但是利用prjQuota 统计临时存储使用量,当超过阈值时,触发pod的驱逐,从而导致临时存储的数据全部丢失,驱逐后的pod不会超过临时了。

1
2
3
4
5
guarantee-gpu0-6c-6c5644c9cd-5n88n              1/1     Running            0          7s
guarantee-gpu0-6c-6c5644c9cd-kbn25 0/1 Evicted 0 103m
# kubectl describe pod guarantee-gpu0-6c-6c5644c9cd-kbn25
Warning Evicted 52s kubelet Pod ephemeral local storage usage exceeds the total limit of containers 100Mi.
Normal Killing 52s kubelet Stopping container nginx-gpu0-6c

亲测,kubelet和docker data-root 必须同一个分区,否则不会触发eviction

总结

  1. 磁盘配额以及volume的大小或者k8s csi挂载的volume的大小,都是通过文件系统的quota功能实现的,这个大小是写在host fs中的,必须是操作系统做限制,否则上层岂不是一直写block了。

  2. project quota特性可以统计磁盘使用量,当超过阈值时写入数据hang住或者直接返回No space left on device

  3. project quota实际限制的目录是<data-root>/overlay2/<grapthID><data-root>/overlay2/<graphID>-init, 只有这两个目录是可写的,该目录下的lowerDir可能有几个G且是只读的, 不在quota配额范围),quota限制的是增量的数据,分成全局配额或者容器粒度的配额。

  4. 每一个容器对应一条project记录,开启限额, 每个容器/project 互不影响

  5. k8s empty 卷不支持强配额,而只是利用project quota 统计目录卷的存储容量(mount option 带了projQuota, 但是没有setQuota的数值,所以有一个间隙可以写入更多的数据,没在fs侧设置而是在k8s侧进行了数量控制,当超过则驱逐pod)

  6. 在容器中df -Th会读取/sys/fs/(共享host侧的)查看container rootfs的大小, 如果grapthID的目录设置了quota, 则显示的就是该quota, 如果没有配额, 显示的是data root所在分区,其实就是parent dir 限制了quota时, 其子dir也限制了大小,则优先使用子dir的quota, quota就是普通volume的size概念

  7. # xfs_quota -xc printxfs_quota -xc 'report -bh' 限制节点所有挂在点的配额即volume size

  8. /etc/projects/etc/projid文件记录 project 信息

REF

  1. https://github.com/kubernetes/kubernetes/pull/66928
  2. https://hackmd.io/@2F6DeP2PT42wM_XKgXFfvw/HySYorxui#%E4%BD%BF%E7%94%A8-xfs-project-quota