Skip to content
60.Docker»30.Cgroups»LV010-Cgroups子系统实例.md

LV010-Cgroups子系统实例

接下来我们来 linux 系统中看一下 cgroups 子系统,来深入了解一下。

一、测试环境

1. Windows

markdown
版本	       Windows 11 专业版
版本号	      23H2
安装日期	  2024/9/16
操作系统版本  22631.4169
体验	       Windows Feature Experience Pack 1000.22700.1034.0

其中 Windows 通过连接路由器的 WiFi 上网,

2. Ubuntu

Ubuntu 安装在 Windows 中的 VMware 中。

  • VMware
markdown
产品:VMware® Workstation 17 Pro
版本:17.6.0 build-24238078
  • Ubuntu
markdown
# uname -a
Linux sumu-vm 5.15.0-139-generic #149~20.04.1-Ubuntu SMP Wed Apr 16 08:29:56 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux

# lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.6 LTS
Release:	20.04
Codename:	focal

二、默认都启动了哪些子系统

shell
sudo mount -t cgroup

【例】

image-20251102205258860

通过输出,可以看到当前系统已经挂载了我们常用的 cgroups 子系统,例如 cpu、memory、pids 等我们常用的 cgroups 子系统。

可以看到 cgroup 是 mount 在 /sys/fs/cgroup/ 下的,值得注意的是 /sys/fs/cgroup/ mount 的文件系统类型是 tempfs,说明目录下的文件都是存在于内存中的临时文件。

到该路径下 ls 可以看到,里面有很多文件夹和硬连接,这些文件夹便是一个个的 root cgroup,分别 mount 了不同的 subsystem,从文件夹的名称可以看到 blkio 是 subsystem blkio 的 mount 点,cpu,cpuacct 是 subsystem cpu 和 cpuacct 的 mount 点,其他的依次类推。说他们是 root cgroup 是因为它们处于 hierarchy 树状结构的根部,它们的子文件夹就是 child cgroup。可以说 root cgroup 以及它的 child cgroup 以及 child 的 child 等等组成的一个树状的 cgroup 层级结构就是 hierarchy。

这些子系统中,cpu 和 memory 子系统是容器环境中使用最多的子系统,下面我们来了解一些这两个子系统。

三、CPU 子系统

以 cpu 子系统为例,我们来看一下 cgroups 如何限制进程的 cpu 使用时间。由于 cgroups 的操作很多需要用到 root 权限,我们在执行命令前要确保已经切换到了 root 用户,以下命令的执行默认都是使用 root 用户。

1. 在 cpu 子系统下创建 cgroup

shell
sudo mkdir /sys/fs/cgroup/cpu/mydocker

【例】

shell
root@sumu-vm:/sys/fs/cgroup/cpu/mydocker# ls -alh
总用量 0
drwxr-xr-x 2 root root 0 11月  2 20:55 .
dr-xr-xr-x 7 root root 0 11月  2 18:45 ..
-rw-r--r-- 1 root root 0 11月  2 20:55 cgroup.clone_children
-rw-r--r-- 1 root root 0 11月  2 20:55 cgroup.procs
-r--r--r-- 1 root root 0 11月  2 20:55 cpuacct.stat
-rw-r--r-- 1 root root 0 11月  2 20:55 cpuacct.usage
-r--r--r-- 1 root root 0 11月  2 20:55 cpuacct.usage_all
-r--r--r-- 1 root root 0 11月  2 20:55 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 11月  2 20:55 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 11月  2 20:55 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 11月  2 20:55 cpuacct.usage_sys
-r--r--r-- 1 root root 0 11月  2 20:55 cpuacct.usage_user
-rw-r--r-- 1 root root 0 11月  2 20:55 cpu.cfs_burst_us
-rw-r--r-- 1 root root 0 11月  2 20:55 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 11月  2 21:04 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 11月  2 20:55 cpu.idle
-rw-r--r-- 1 root root 0 11月  2 20:55 cpu.shares
-r--r--r-- 1 root root 0 11月  2 20:55 cpu.stat
-rw-r--r-- 1 root root 0 11月  2 20:55 cpu.uclamp.max
-rw-r--r-- 1 root root 0 11月  2 20:55 cpu.uclamp.min
-rw-r--r-- 1 root root 0 11月  2 20:55 notify_on_release
-rw-r--r-- 1 root root 0 11月  2 20:58 tasks

可以看到这里面有很多 cpuacct.*cpu.* 的文件,这些文件记载了 cgroup 对其控制下的进程的 cpu 和 cpuacct 资源限制。 除了这些文件,重要文件的就是 tasks 文件,它记载了这个 cgroup 控制的 task 也就是进程的 PID,如果想把某个进程交给 cgroup 管,只需要将该进程 PID 写进 cgroup 的 tasks 里面就可以了。

其中 cpu.cfs_quota_us 文件代表在某一个阶段限制的 CPU 时间总量,单位为微秒 。例如,我们想限制某个进程最多使用 1 核 CPU,就在这个文件里写入 100000(100000 代表限制 1 个核) ,tasks 文件中写入进程的 ID 即可(如果要限制多个进程 ID,在 tasks 文件中用换行符分隔即可)。

此时,我们所需要的 cgroup 就创建好了。对,就是这么简单。

2. 创建进程并加入 cgroup

这里为了方便演示,我们把当前运行的 shell 进程加入 cgroup,然后在当前 shell 运行 cpu 耗时任务(这里利用到了继承,子进程会继承父进程的 cgroup)。

使用以下命令将 shell 进程加入 cgroup 中:

shell
cd /sys/fs/cgroup/cpu/mydocker
echo $$ > tasks

【例】

shell
root@sumu-vm:/sys/fs/cgroup/cpu/mydocker# echo $$ > tasks
root@sumu-vm:/sys/fs/cgroup/cpu/mydocker# cat tasks
16237
16244
root@sumu-vm:/sys/fs/cgroup/cpu/mydocker# ps
    PID TTY          TIME CMD
  16236 pts/1    00:00:00 su
  16237 pts/1    00:00:00 bash
  16246 pts/1    00:00:00 ps

其中第一个进程 ID 为当前 shell 的主进程,也就是说,当前 shell 主进程为 16237。

3. 执行 CPU 耗时任务

3.1 制造一个死循环

我们使用以下命令制造一个死循环,来提升 cpu 使用率:

shell
while true;do echo;done;

执行完上述命令后,我们新打开一个 shell 窗口(这个不需要进 root 也可以),使用 top -p 命令查看当前 cpu 使用率,-p 参数后面跟进程 ID,我这里是 16237。

image-20251102210145515

通过上面输出可以看到 16237 这个进程被限制到了只能使用 100 % 的 cpu,也就是 1 个核。说明我们使用 cgroup 来限制 cpu 使用时间已经生效。此时,执行 while 循环的命令行窗口可以使用 Ctrl+C 退出循环。退出循环后,该进程的 CPU 占用会立刻降低:

image-20251102210253488

3.2 CPU 限制为 0.5 核

为了进一步证实 cgroup 限制 cpu 的准确性,我们修改 cpu 限制时间为 0.5 核,命令如下:

shell
cd /sys/fs/cgroup/cpu/mydocker
echo 50000 > cpu.cfs_quota_us

同样使用上面的命令来制造死循环:

shell
while true;do echo;done;

保持当前窗口,新打开一个 shell 窗口,使用 top -p 参数查看 cpu 使用率:

image-20251102210527885

通过上面输出可以看到,此时 cpu 使用率已经被限制到了 50%,即 0.5 个核。

四、memroy 子系统

我们在执行命令前要确保已经切换到了 root 用户,以下命令的执行默认都是使用 root 用户。

1. 在 memory 子系统下创建 cgroup

shell
mkdir /sys/fs/cgroup/memory/mydocker

【例】

shell
root@sumu-vm:/sys/fs/cgroup/memory/mydocker# ls -alh
总用量 0
drwxr-xr-x 2 root root 0 11月  2 21:07 .
dr-xr-xr-x 7 root root 0 11月  2 18:45 ..
-rw-r--r-- 1 root root 0 11月  2 21:07 cgroup.clone_children
--w--w--w- 1 root root 0 11月  2 21:07 cgroup.event_control
-rw-r--r-- 1 root root 0 11月  2 21:07 cgroup.procs
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.failcnt
--w------- 1 root root 0 11月  2 21:07 memory.force_empty
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.kmem.failcnt
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.kmem.limit_in_bytes
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.kmem.max_usage_in_bytes
-r--r--r-- 1 root root 0 11月  2 21:07 memory.kmem.slabinfo
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.kmem.tcp.failcnt
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.kmem.tcp.limit_in_bytes
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.kmem.tcp.max_usage_in_bytes
-r--r--r-- 1 root root 0 11月  2 21:07 memory.kmem.tcp.usage_in_bytes
-r--r--r-- 1 root root 0 11月  2 21:07 memory.kmem.usage_in_bytes
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.limit_in_bytes
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.max_usage_in_bytes
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.memsw.failcnt
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.memsw.limit_in_bytes
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.memsw.max_usage_in_bytes
-r--r--r-- 1 root root 0 11月  2 21:07 memory.memsw.usage_in_bytes
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.move_charge_at_immigrate
-r--r--r-- 1 root root 0 11月  2 21:07 memory.numa_stat
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.oom_control
---------- 1 root root 0 11月  2 21:07 memory.pressure_level
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.soft_limit_in_bytes
-r--r--r-- 1 root root 0 11月  2 21:07 memory.stat
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.swappiness
-r--r--r-- 1 root root 0 11月  2 21:07 memory.usage_in_bytes
-rw-r--r-- 1 root root 0 11月  2 21:07 memory.use_hierarchy
-rw-r--r-- 1 root root 0 11月  2 21:07 notify_on_release
-rw-r--r-- 1 root root 0 11月  2 21:07 tasks

其中 memory.limit_in_bytes 文件代表内存使用总量,单位为 byte

例如,这里我希望对内存使用限制为 1G,则向 memory.limit_in_bytes 文件写入 1073741824,命令如下:

shell
cd /sys/fs/cgroup/memory/mydocker
echo 1073741824 > memory.limit_in_bytes

2. 创建进程并加入 cgroup

同样把当前运行的 shell 进程加入 cgroup,然后在当前 shell 运行 cpu 耗时任务(这里利用到了继承,子进程会继承父进程的 cgroup)。

使用以下命令将 shell 进程加入 cgroup 中:

shell
cd /sys/fs/cgroup/memory/mydocker
echo $$ > tasks

【例】

shell
root@sumu-vm:/sys/fs/cgroup/memory/mydocker# echo $$ > tasks  
root@sumu-vm:/sys/fs/cgroup/memory/mydocker# cat tasks 
16237
16284
root@sumu-vm:/sys/fs/cgroup/memory/mydocker# ps
    PID TTY          TIME CMD
  16236 pts/1    00:00:00 su
  16237 pts/1    00:01:55 bash
  16285 pts/1    00:00:00 ps

其中第一个进程 ID 为当前 shell 的主进程,也就是说,当前 shell 主进程为 16237。

3. 申请内存测试

我们设置当前 shell 进程对内存使用限制为 100M ,则向 memory.limit_in_bytes 文件写入 100x1024x1024(104,857,600),命令如下:

shell
cd /sys/fs/cgroup/memory/mydocker
echo 104857600 > memory.limit_in_bytes

3.1 memtester 工具

这里我们需要借助一下工具 memtester,memtester 的安装这里不再详细介绍了。直接 apt 安装即可。

shell
apt install memtester

3.2 申请 150M 内存

我们执行以下命令:

shell
memtester 110M 1

该命令会申请 110 M 内存,并且做内存测试。由于上面我们对当前 shell 进程内存限制为 100 M,当 memtester 使用的内存达到 100M 时,cgroup 便将 memtester 杀死。

image-20251102211929269

上面最后一行的输出结果表示 memtester 想要 110 M 内存,但是由于 cgroup 限制,达到了内存使用上限,被杀死了,与我们的预期一致。

3.3 申请 90M 内存

我们可以使用以下命令,降低一下内存申请,将内存申请调整为 90M:

shell
memtester 90M 1
image-20251102212105826

这里可以看到,此时 memtester 已经成功申请到 500M 内存并且正常完成了内存测试。

三、删除 Cgroups

前面创建的 cgroups 如果不想使用了,直接删除创建的文件夹即可。例如我想删除内存下的 mydocker 目录,使用以下命令即可:

shell
rmdir /sys/fs/cgroup/memory/mydocker/

参考资料:

Docker 底层:cgroups 实现资源限制 - 拾月凄辰 - 博客园

莫道桑榆晚 为霞尚满天.