前言
cgroups是什么?简单来说,cgroups(Control Groups)是linux 内核提供的一种可以限制程序资源的机制, 被广泛用于容器隔离技术. 使用cgroups, 我们可以限制一个或一组进程的资源, 如 cpu, 内存, 进而让我们实现两个目的
- 提前规划好资源, 能确保各个程序都有足够的资源使用
 - 可让资源利用更加高效, 可靠
 本文通过几个实际的使用案例, 来探索
cgroups的使用,
目前cgroups有cgroups-v1cgroups-v2, 本文只探索cgroups-v2
cgroup小Demo-限制进程可用cpu数
我们创建一个 cgroup,设置在这个 cgroup 下的进程只能使用两个处理器核, 然后启动一个测试程序, 观察这个程序的可用cpu列表, 然后将进程pid绑定到这个 cgroup,  观察可用cpu列表的变化
- 
创建cpu控制策略
- 
创建 cgroup 文件夹
mkdir limit-core-01在目录
/sys/fs/cgroups下创建一个目录limit-core-01进入目录,会发现里面自动生成了好多文件 (第一次见的我感觉好神奇)

 - 
配置处理器核限制
这里需要使用目录文件下的
cpuset.cpus, 这就是用来限制当前cgroup的可用处理器核的, 一般不配置会是空白,表示所有核都能使用,我们希望只使用两个,那么可配置0-1, 表示只能使用前两个echo "0-1" > cpuset.cpus 
 - 
 - 
将进程涵盖进 cgroup
有手动和自动的办法:- 程序启动后手动将 pid 涵盖进 
cgroup.procs - 在程序启动时, 使用控制工具 
cgroup-tools 
- 
先创建一个测试程序
import os import time for i in range(1000): print(f"{os.getpid()}-{i}, limit_cpu: {os.sched_getaffinity(0)}") time.sleep(1)其中 os.sched_getaffinity(0) 为获取当前进程被限制使用的cpu
 - 
启动测试程序,将PID加入
cgroup.procsecho 15336 > cgroup.procs
启动程序,我们看到os.sched_getaffinity(0)的返回一开始是0-19(机器20核),在将进程id15336放进cgroup.procs后,立马变成了我们设置的0-1,这样就成功对该进程进行了cpu资源限制。 - 
[自动] 启动程序时加入该cgroup
我们需要先安装
cgroup-toolssudo apt install cgroup-tools启动程序
sudo cgexec -g cpuset:limit-core-01 python test.py
 
 - 程序启动后手动将 pid 涵盖进 
 
cgroup-v2 的目录结构
cgroup-v2 是一个树形结构,/sys/fs/cgroup 就是一个 cgroup,  其中有文件和目录
- 文件:基本都是跟当前 
cgroup相关的资源控制策略文件 - 子目录:也是一个 
cgroup,但其只拥有父cgroup开放给它的资源控制权限- 如在 
父cgroup中配置cgroup.subtree_control可用控制器cpuset memory, 那么子cgroup就只能通过这两个控制器cpuset memory进行控制的权限。 
 - 如在 
 
cgroup-v2 可控制资源
按照 控制器 分类可控制资源, 在 cgroup  根目录 /sys/fs/cgroup 下
cat cgroup.controllers
walkerjun@walkerjun:/sys/fs/cgroup$ cat cgroup.controllers
cpuset cpu io memory hugetlb pids rdma misc
| 控制器 | 用途 | 
|---|---|
cpuset | 
可用来限制进程可用的cpu 数量 | 
cpu | 
在cpu繁忙 时生效,可限制进程cpu的使用额,如在 cpuset 的基础上,只能使用其中 50% 的配额,这个在 cpu 不繁忙 时不会生效 | 
io | 
用来限制进程对某设备的带宽, 如通过设置上限来实现 | 
memory | 
限制进程使用的内存 | 
hugetlb | 
限制 huge pages 使用 | 
pids | 
限制在这个 cgroup 下绑定的 进程数量 | 
rdma | 
限制 RDMA/IB-specific 资源使用 | 
misc | 
- | 
cpu控制器Demo-限制cpu使用率
我们创建一个名为 test-cpu-usage-limit 的新 cgroup, 并使用配置 cpu.max 来控制这个 cgroup 的 cpu 使用率,  然后我们创建一个只有空循环的测试程序, 启动并观察在与不在这个cgroup 下的 cpu 使用率
- 
创建 cgroup test-cpu-usage-limit
在cgroup根目录/sys/fs/cgroup下执行mkdir test-cpu-usage-limit - 
配置
进入这个目录cpu.maxcd test-cpu-usage-limit, 并执行
我们配置echo "50000 100000" > cpu.max50000 100000, 这两个值表示, 在每100000个cpu周期内, 最多只会有50000是分配给这个cgroup, 这意味着, 该cgroup最大的cpu使用率是50% - 
创建程序进行验证
这里我们仍然使用python程序, 由于python存在全局解释器锁(GIL), 导致单进程最多只使用一个核, 因此, 我们使用top命令观察python程序的%cpu会比较方便, 在没有限制下, 最多会是100%, 如果是空循环程序, 在没有任何限制下, 我们的预期正是100%, 在我们的cgrouptest-cpu-usage-limit下, 则预期是50%, 我们来试一下- 
创建程序
import os print(f"pid: {os.getpid()}") while True: pass - 
不使用cgroup时cpu使用率
python test-cpu-usage.py
如上, 在没有做任何限制情况下, 该进程的
cpu使用率是100%, 这表示完全了1 个核, 这符合我们的预期 - 
使用cgroup时cpu使用率
sudo cgexec -g cpu:test-cpu-usage-limit python test-cpu-usage.py
如上, 当我们使用
cgroup时, 我们成功将测试进程的最大cpu使用率限制在了50% 
 - 
 
memory控制器Demo-限制最大内存
创建一个新 cgroup, 我们限制其最大使用内存是 20 M, 使用 memory.max 配置, 如果超过配置就会触发系统oom,  杀死相关程序, 因此我们通过观察程序是否被杀死来判断配置是否生效
- 
创建 cgroup memory-limit
 
mkdir memory-limit
- 
配置 memory.max
在cgroup中所有内存相关的单位都是byte, 对于memory.max如果不限制最大内存, 则设置为max(默认配置), 我们这里设置20 M, 即20971520bytes
我们使用 python 程序验证这个配置, 由于 python 程序启动就会占用约echo 20971520 > memory.max10M, 因此我们分别尝试在python 程序中申请8M和15M内存, 观察15M是否会触发oom- 
启动程序
sudo cgexec -g memory:memory-limit python - 
尝试申请内存

如上, 我一开始申请了8M内存, 没有问题, 可当再次申请8M则触发了oom, 程序被杀死, 随后重新启动程序, 一开始就申请15M, 程序则直接就被杀死了, 说明我们的配置已经生效了 
 - 
 
cgroup-v2 配置文件
除了上面Demo 中的配置文件, 还有诸如 memory.min 这样的配置, 这些说明可以在 Linux内核文档-core-interface-files中找到, 这里就不搬运了
参考
- linux cgroups 简介 | 51CTO博客
 - Control Group v2 | Linux 内核文档
 - [容器底层技术] cgroup v2使用与测试 | CSDN
 - Memory Resource Controller | Linux 内核文档
 - The Power of Linux Cgroups: How Containers Take Control of Their Resources | medium
 - Separation Anxiety: A Tutorial for Isolating Your System with Linux Namespaces | Developers