本文讨论如何使用cgroup限制Linux用户各种资源使用,主要演示了CPU和内存的限制方法。

注意:因Linux发行版本的差异,本方法并不适用于所有Linux发行版。以下方法在Redhat 7/CentOS 7上测试通过。

一、目的

限制Linux特定用户(的所有进程)资源使用上限,例如:限制用户yz的CPU使用上限为10%。

二、准备

2.1 重要说明

2.2 安装libcgroup

snippet.bash
[root@bogon ~]# yum install libcgroup
[root@bogon ~]# yum install libcgroup-tools

2.3 cgroup子系统

子系统(sub system)是cgroup的内核子模块,被看作资源控制器,为不同cgroup 配不同级别的系统资源。这里仅列出我们用到的子系统(其它子系统可参阅文献1):

三、配置

一句话概括就是,先创建资源组(描述各种资源的限制值,也可称为cgroup),然后建立用户与资源组之间的关联规则(rule),即哪个用户用哪个资源组限制什么资源。

3.1 配置方法

有两个相关的系统配置文件:

3.2 例子

【例子】:假设要限制Linux用户seabox的资源:

首先,编写cgconfig配置文件/etc/cgconfig.d/seaboxdb.conf(注:seaboxdb.conf是我们自己取的名字,扩展名必须是.conf。)

group seabox_limit {
  cpu {
    cpu.cfs_quota_us = 80000;
    cpu.cfs_period_us = 100000;
  }
  memory {
    memory.limit_in_bytes = 10G;
    memory.memsw.limit_in_bytes = 15G;
  }
}

然后,在/etc/cgrules.conf中添加配置,将seabox用户关联到seabox_limit资源组。

seabox        cpu,memory        seabox_limit/

这行配置意思是:seabox用户的CPU和内存使用seabox_limit资源组限制。

最后,需要重启cgroup相关服务:

snippet.bash
[root@bogon ~]# systemctl enable cgconfig    # 启用cgconfig
[root@bogon ~]# systemctl restart cgconfig   # 重启cgconfig,将cgconfig配置文件中的配置写入cgroup
[root@bogon ~]# systemctl enable cgred       # 启用cgred
[root@bogon ~]# systemctl restart cgred      # 重启cgred,将规则配置文件的配置写入cgroup

注意:

3.3 复杂配置范例

group seabox_limit {
  cpu {
    # 最多使用4个CPU核的80%:100000 * 4 * 0.8 = 320000
    cpu.cfs_quota_us = 320000;
    cpu.cfs_period_us = 100000;
  }
  cpuacct {
  }
  cpuset {
    # 设置可用内存节点,通常设置为全部可用内存节点即可。
    cpuset.mems = '0-1';
    # 仅使用0、1、2和3这4个CPU核
    cpuset.cpus = '0-3';
  }
  memory {
    # 最多使用50GB物理内存
    memory.limit_in_bytes = 50G;
    # 最多使用60GB物理和swap内存
    memory.memsw.limit_in_bytes = 60G;
  }
}

获得全部可用内存节点的方法:

方法1:查询cpuset子系统根节点的cpuset.mems

snippet.bash
[root@bogon ~]# cat /sys/fs/cgroup/cpuset/cpuset.mems
0-1

方法2:用numactl查询

snippet.bash
[root@bogon ~]# numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 2
node 0 size: 9215 MB
node 0 free: 283 MB
node 1 cpus: 1 3
node 1 size: 9215 MB
node 1 free: 321 MB
node distances:
node   0    1
  0:  10   21
  1:  21   10

3.4 校验方法

本节说明如何检查以上配置是否写入cgroup系统。

Step 1. 确定子系统挂载点(cgroup子系统在文件系统中的虚拟文件)。

snippet.bash
[root@bogon ~]# lssubsys -am
cpuset /sys/fs/cgroup/cpuset
cpu,cpuacct /sys/fs/cgroup/cpu,cpuacct
memory /sys/fs/cgroup/memory
devices /sys/fs/cgroup/devices
freezer /sys/fs/cgroup/freezer
net_cls,net_prio /sys/fs/cgroup/net_cls,net_prio
blkio /sys/fs/cgroup/blkio
perf_event /sys/fs/cgroup/perf_event
hugetlb /sys/fs/cgroup/hugetlb
pids /sys/fs/cgroup/pids

从以上输出可以知道:cpuset子系统挂载在/sys/fs/cgroup/cpusetcpucpuacct子系统挂载在/sys/fs/cgroup/cpu,cpuacct,……

Step 2. 检查子系统下是否创建了cgroup组。

cpu子系统下的cgroup组为例,其它子系统类似。

snippet.bash
[root@bogon ~]# ls -al /sys/fs/cgroup/cpu,cpuacct/
drwxr-xr-x   9 root root   0 38 11:26 .
drwxr-xr-x  13 root root 340 213 11:36 ..
-rw-r--r--   1 root root   0 213 11:36 cgroup.clone_children
……
drwxr-xr-x   2 root root   0 38 11:16 seabox_limit
……

可以看到cpu子系统下已经创建了seabox_limit组。

Step 3. cgroup组下的参数值是否正确。

seabox_limit组的cpu.cfs_quota_us参数为例,其它参数类似。

snippet.bash
[root@bogon ~]# cat /sys/fs/cgroup/cpu,cpuacct/seabox_limit/cpu.cfs_quota_us
80000

也可以用以下命令:

snippet.bash
[root@bogon ~]# cgget -g cpu /seabox_limit/
/seabox_limit/:
cpu.rt_period_us: 1000000
cpu.rt_runtime_us: 0
cpu.stat: nr_periods 7257
        nr_throttled 2912
        throttled_time 43156965570
cpu.cfs_period_us: 100000
cpu.cfs_quota_us: 80000
cpu.shares: 1024

四、多用户配置

多用户是以上单个用户的简单扩展。

cgconfig配置文件类似这样:

group user1_limit {
  cpu {
    ……
  }
  cpuset {
    ……
  }
  cpuacct {
  }
  memory {
    ……
  }
}

group user2_limit {
  cpu {
    ……
  }
  memory {
    ……
  }
}

……

cgrules配置类似这样:

user1        cpu,cpuset,cpuacct,memory        user1_limit/
user2        cpu,memory                       user2_limit/
……

五、资源限制参数说明

本章按照子系统说明常用参数。

5.1 cpu

cpu.cfs_period_us

设定CPU的时间片周期,是一个基准值,单位为微秒(μs,这里以 “us” 表示)。与cpu.cfs_quota_us配套使用。

通常设置为100000。上限为 1 秒,下限为 1000 微秒。

cpu.cfs_quota_us

设定在某一周期(由 cpu.cfs_period_us规定)某个cgroup所有任务可运行的时间总量上限,单位为微秒。一旦 cgroup 中任务用完配额时间,它们就会被限制流量,并在进入下阶段前禁止运行。这样就限制了该 cgroup 的CPU使用率。

cpu.cfs_quota_us 为 -1,表示该 cgroup 的 CPU 使用时间不受限制。

【例子】:假设系统总共有4个CPU核,欲限制某个 cgroup 最多使用全部CPU资源的50%,则配置如下:

cpu.cfs_period_us = 100000;
cpu.cfs_quota_us = 200000;

解释一下,每个CPU核时间周期是100000微妙,那么cpu.cfs_quota_us设置为200000毫秒,表示可以使用2倍的单核CPU时间,即可以使用全部CPU核的50%资源。

5.2 cpuset

cpuset.cpus

设定该 cgroup 任务可以使用的 CPU核。这是一个逗号分隔列表,格式为 ASCII,小横线("-")代表范围。例如:

0-3,16

表示 CPU核:0、1、2、3 和 16。

cpuset.mems

设定该 cgroup 中任务可以使用的内存节点。格式同cpuset.cpus

注意:设置cpuset.cpus必须同时设置cpuset.mems

5.3 cpuacct

cpuacct子系统作用是统计和报告CPU使用情况,如果不关心可以不配置。

cpuacct.usage

报告此 cgroup 中所有任务(包括层级中的低端任务)使用 CPU 的总时间(纳秒)。

cpuacct.stat

报告此 cgroup 的所有任务(包括层级中的低端任务)使用的用户和系统 CPU 时间。

cpuacct.usage_percpu

报告 cgroup 中所有任务(包括层级中的低端任务)在每个 CPU 中使用的 CPU 时间(纳秒)。

5.4 memory

memory子系统参数分为两大类:控制类与统计报告类。

控制类包括:

memory.limit_in_bytes

设定物理内存的最大使用量。如果没有指定单位,则默认为字节。但是可以使用后缀代表更大的单位(k 或者 K 代表千字节,m 或者 M 代表兆字节 ,g 或者 G 代表千兆字节)。

memory.limit_in_bytes 设为 -1,表示不限制。

memory.memsw.limit_in_bytes

设定物理内存与 swap 用量之和的最大值。单位同memory.limit_in_bytes

memory.memsw.limit_in_bytes 设为 -1,表示不限制。

注意:memory.limit_in_bytes必须在 memory.memsw.limit_in_bytes 参数之前设定,顺序颠倒会导致错误。

【例子】:为某一 cgroup 设定 memory.limit_in_bytes = 2Gmemory.memsw.limit_in_bytes = 4G, 可以让该 cgroup 中的进程最多分得 2GB 物理内存,并且一旦用尽,只能再分得 2GB swap。memory.memsw.limit_in_bytes 参数表示物理内存和 swap 的总和。没有设置 memory.memsw.limit_in_bytes 参数的 cgroup 的进程可以使用全部可用 swap (当限定的内存用尽后),并会因为缺少可用 swap 触发 OOM 状态。

如果使用cgconfig.conf配置,也需要遵照此顺序:

memory {
  memory.limit_in_bytes = 2G;
  memory.memsw.limit_in_bytes = 4G;
}

memory.oom_control

可以为 cgroup 启用或者禁用“内存不足”(Out of Memory,OOM)终止程序。

0表示启用,尝试消耗超过其允许内存的任务会被 OOM 终止程序立即终止。默认所有使用 memory 子系统的 cgroup 都会启用 OOM 终止程序。如果要禁用,则设置 memory.oom_control 为 1。

memory.swappiness

设置使用swap的倾向优先级。与 /proc/sys/vm/swappiness 作用类似,只是这里设置的是cgroup范围的。默认值为 60。低于 60 会降低内核换出进程内存的倾向;高于 0 会增加内核换出进程内存的倾向。高于 100 时,kernel 将开始换出作为该 cgroup 中进程地址空间一部分的页面。

注意:值 0 不会阻止进程内存被换出;系统内存不足时,换出仍可能发生,因为全局虚拟内存管理逻辑不读取该 cgroup 值。

统计报告类:

memory.usage_in_bytes

报告 cgroup 中进程当前所用的内存总量(以字节为单位)。

memory.memsw.usage_in_bytes

报告该 cgroup 中进程当前所用的内存量和 swap 空间总和(以字节为单位)。

memory.max_usage_in_bytes

报告 cgroup 中进程所用的最大内存量(以字节为单位)。

memory.memsw.max_usage_in_bytes

报告该 cgroup 中进程的最大内存用量和最大 swap 空间用量(以字节为单位)。

六、参考