前言
cgroups
是什么?简单来说,cgroups(Control Groups)
是linux 内核
提供的一种可以限制程序资源
的机制, 被广泛用于容器隔离技术. 使用cgroups
, 我们可以限制一个或一组进程的资源, 如 cpu, 内存, 进而让我们实现两个目的
- 提前规划好资源, 能确保各个程序都有足够的资源使用
- 可让资源利用更加高效, 可靠
本文通过几个实际的使用案例, 来探索
cgroups
的使用,
目前cgroups
有cgroups-v1
cgroups-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.procs
echo 15336 > cgroup.procs
启动程序,我们看到os.sched_getaffinity(0)
的返回一开始是0-19
(机器20核),在将进程id15336
放进cgroup.procs
后,立马变成了我们设置的0-1
,这样就成功对该进程进行了cpu资源限制。 -
[自动] 启动程序时加入该cgroup
我们需要先安装
cgroup-tools
sudo 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.max
cd test-cpu-usage-limit
, 并执行
我们配置echo "50000 100000" > cpu.max
50000 100000
, 这两个值表示, 在每100000
个cpu周期
内, 最多只会有50000
是分配给这个cgroup
, 这意味着, 该cgroup
最大的cpu使用率
是50%
-
创建程序进行验证
这里我们仍然使用python
程序, 由于python
存在全局解释器锁(GIL)
, 导致单进程最多只使用一个核, 因此, 我们使用top
命令观察python
程序的%cpu
会比较方便, 在没有限制下, 最多会是100%
, 如果是空循环程序, 在没有任何限制下, 我们的预期正是100%
, 在我们的cgroup
test-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
, 即20971520
bytes
我们使用 python 程序验证这个配置, 由于 python 程序启动就会占用约echo 20971520 > memory.max
10M
, 因此我们分别尝试在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