本文讨论如何使用cgroup限制Linux用户各种资源使用,主要演示了CPU和内存的限制方法。
注意:因Linux发行版本的差异,本方法并不适用于所有Linux发行版。以下方法在Redhat 7/CentOS 7上测试通过。
限制Linux特定用户(的所有进程)资源使用上限,例如:限制用户yz
的CPU使用上限为10%。
cgroup
配置需要Linux root权限。[root@bogon ~]# yum install libcgroup [root@bogon ~]# yum install libcgroup-tools
子系统(sub system
)是cgroup
的内核子模块,被看作资源控制器,为不同cgroup
配不同级别的系统资源。这里仅列出我们用到的子系统(其它子系统可参阅文献1):
cpu
:CPU资源控制器,可以控制CPU使用率与调度策略等。cpuset
:CPU核控制器,可以控制资源组只使用哪些CPU核。puacct
:CPU统计子系统,用于统计与报告CPU使用情况,例如资源组实际的CPU使用时间等。memory
:内存控制与统计子系统,可以控制内存使用量与使用策略,也提供内存使用情况报告。
一句话概括就是,先创建资源组(描述各种资源的限制值,也可称为cgroup
),然后建立用户与资源组之间的关联规则(rule
),即哪个用户用哪个资源组限制什么资源。
有两个相关的系统配置文件:
/etc/cgconfig.conf
:cgroup
资源组描述信息文件,配置每个资源组各种资源(CPU、内存、IO等)的配额。为了不影响/etc/cgconfig.conf
文件,我们还可以将配置文件放到/etc/cgconfig.d/
目录下(如果目录不存在,可以手动创建。)。/etc/cgrules.conf
:规则配置文件,将Linux用户与/etc/cgconfig.conf
中的资源组进行配对,即哪个用户用哪个资源组限制什么资源。
【例子】:假设要限制Linux
用户seabox
的资源:
80%
(即使有多个核,也只能用一个核的80%
)。10GB
,总内存(物理和swap
)使用上限为15GB
。
首先,编写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
相关服务:
[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
注意:
.conf
文件不得被删除,因为系统重启时,cgconfig
服务仍然需要读取这些配置文件以重建资源组。cgroup
配置,则需要重启cgroup
相关服务。cgroup
组名称不能重复,也不能与已有的组名称重复。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
[root@bogon ~]# cat /sys/fs/cgroup/cpuset/cpuset.mems 0-1
方法2:用numactl
查询
[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
本节说明如何检查以上配置是否写入cgroup
系统。
Step 1. 确定子系统挂载点(cgroup
子系统在文件系统中的虚拟文件)。
[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/cpuset
,cpu
和cpuacct
子系统挂载在/sys/fs/cgroup/cpu,cpuacct
,……
Step 2. 检查子系统下是否创建了cgroup
组。
以cpu
子系统下的cgroup
组为例,其它子系统类似。
[root@bogon ~]# ls -al /sys/fs/cgroup/cpu,cpuacct/ drwxr-xr-x 9 root root 0 3月 8 11:26 . drwxr-xr-x 13 root root 340 2月 13 11:36 .. -rw-r--r-- 1 root root 0 2月 13 11:36 cgroup.clone_children …… drwxr-xr-x 2 root root 0 3月 8 11:16 seabox_limit ……
可以看到cpu
子系统下已经创建了seabox_limit
组。
Step 3. cgroup
组下的参数值是否正确。
以seabox_limit
组的cpu.cfs_quota_us
参数为例,其它参数类似。
[root@bogon ~]# cat /sys/fs/cgroup/cpu,cpuacct/seabox_limit/cpu.cfs_quota_us 80000
也可以用以下命令:
[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/ ……
本章按照子系统说明常用参数。
设定CPU的时间片周期,是一个基准值,单位为微秒(μs,这里以 “us” 表示)。与cpu.cfs_quota_us配套使用。
通常设置为100000。上限为 1 秒,下限为 1000 微秒。
设定在某一周期(由 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%资源。
设定该 cgroup 任务可以使用的 CPU核。这是一个逗号分隔列表,格式为 ASCII,小横线("-")代表范围。例如:
0-3,16
表示 CPU核:0、1、2、3 和 16。
设定该 cgroup 中任务可以使用的内存节点。格式同cpuset.cpus
。
注意:设置cpuset.cpus必须同时设置cpuset.mems
。
cpuacct子系统作用是统计和报告CPU使用情况,如果不关心可以不配置。
报告此 cgroup 中所有任务(包括层级中的低端任务)使用 CPU 的总时间(纳秒)。
报告此 cgroup 的所有任务(包括层级中的低端任务)使用的用户和系统 CPU 时间。
报告 cgroup 中所有任务(包括层级中的低端任务)在每个 CPU 中使用的 CPU 时间(纳秒)。
memory子系统参数分为两大类:控制类与统计报告类。
控制类包括:
设定物理内存的最大使用量。如果没有指定单位,则默认为字节。但是可以使用后缀代表更大的单位(k 或者 K 代表千字节,m 或者 M 代表兆字节 ,g 或者 G 代表千兆字节)。
memory.limit_in_bytes
设为 -1,表示不限制。
设定物理内存与 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 = 2G
和 memory.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; }
可以为 cgroup 启用或者禁用“内存不足”(Out of Memory,OOM)终止程序。
0表示启用,尝试消耗超过其允许内存的任务会被 OOM 终止程序立即终止。默认所有使用 memory 子系统的 cgroup 都会启用 OOM 终止程序。如果要禁用,则设置 memory.oom_control
为 1。
设置使用swap的倾向优先级。与 /proc/sys/vm/swappiness
作用类似,只是这里设置的是cgroup范围的。默认值为 60。低于 60 会降低内核换出进程内存的倾向;高于 0 会增加内核换出进程内存的倾向。高于 100 时,kernel 将开始换出作为该 cgroup 中进程地址空间一部分的页面。
注意:值 0 不会阻止进程内存被换出;系统内存不足时,换出仍可能发生,因为全局虚拟内存管理逻辑不读取该 cgroup 值。
统计报告类:
报告 cgroup 中进程当前所用的内存总量(以字节为单位)。
报告该 cgroup 中进程当前所用的内存量和 swap 空间总和(以字节为单位)。
报告 cgroup 中进程所用的最大内存量(以字节为单位)。
报告该 cgroup 中进程的最大内存用量和最大 swap 空间用量(以字节为单位)。