当前位置:首页 > 技术 > 正文内容

Linux Cgroup V2 实践指南

访客 技术 2026年7月17日 1

本文主要记录 Linux Cgroup V2 版本基本使用操作,包括 cpu、memory 子系统演示。

  1. 开启 Cgroup V2

版本检查

通过下面这条命令来查看当前系统使用的 Cgroups 版本

stat -fc %T /sys/fs/cgroup/

如果输出是cgroup2fs 那就是 V2,就像这样

root@tezn:~# stat -fc %T /sys/fs/cgroup/
cgroup2fs

如果输出是tmpfs 那就是 V1,就像这样

[root@docker cgroup]# stat -fc %T /sys/fs/cgroup/
tmpfs

启用 cgroup v2

如果当前系统未启用 Cgroup V2,可以通过修改内核 cmdline 引导参数在你的 Linux 发行版上手动启用 cgroup v2。

如果你的发行版使用 GRUB,则应在 /etc/default/grub 下的 GRUB_CMDLINE_LINUX 中添加 systemd.unified_cgroup_hierarchy=1, 然后执行 sudo update-grub

具体如下:

1)编辑 grub 配置

vi /etc/default/grub

内容大概是这样的:

GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""

对最后一行GRUB_CMDLINE_LINUX进行修改

GRUB_CMDLINE_LINUX="quiet splash systemd.unified_cgroup_hierarchy=1"

2)然后执行以下命令更新 GRUB 配置

sudo update-grub

3)最后查看一下启动参数,确认配置修改上了

cat /boot/grub/grub.cfg | grep "systemd.unified_cgroup_hierarchy=1"

4)然后就是重启

reboot

重启后查看,不出意外切换到 cgroups v2 了

root@cgroupv2:~# stat -fc %T /sys/fs/cgroup/
cgroup2fs

发行版推荐

不过,推荐的方法仍是使用一个默认已启用 cgroup v2 的发行版。

有关使用 cgroup v2 的 Linux 发行版的列表,

  • Container-Optimized OS(从 M97 开始)
  • Ubuntu(从 21.10 开始,推荐 22.04+)
  • Debian GNU/Linux(从 Debian 11 Bullseye 开始)
  • Fedora(从 31 开始)
  • Arch Linux(从 2021 年 4 月开始)
  • RHEL 和类似 RHEL 的发行版(从 9 开始)
  1. 基本使用

cgroup v2 使用上和 v1 版本基本一致,v2 版本也是默认在/sys/fs/cgroup/目录。

root@mydocker:~# ls /sys/fs/cgroup/
cgroup.controllers      cgroup.subtree_control  init.scope       system.slice
cgroup.max.depth        cgroup.threads          io.cost.model    user.slice
cgroup.max.descendants  cpu.pressure            io.cost.qos
cgroup.procs            cpuset.cpus.effective   io.pressure
cgroup.stat             cpuset.mems.effective   memory.pressure

  • 创建 sub-cgroup: 只需要创建一个子目录
cd /sys/fs/cgroup
mkdir $CGROUP_NAME

  • 将进程移动到指定 cgroup:将 PID 写入到相应 cgroup 的 cgroup.procs 文件即可,就像这样:
echo 1001 > /sys/fs/cgroup/test/cgroup.procs

  • 删除 cgroup/sub-cgroup: 也是直接删除对应目录即可
  • 如果一个cgroup 已经没有任何children或活进程,那直接删除对应的文件夹就删除该cgroup了
  • 如果一个cgroup已经没有children,但是还有僵尸进程,也认为这个cgroup是空的,可以直接删除
rmdir /sys/fs/cgroup/test

  • 修改 cpu、memory 限制:往对应配置文件写入配置内容即可
  • cpu.max 用于配置 cpu 使用限制
  • memory.max 则用于配置 内存使用限制
  • ...

创建 cgroup

接下来,以 cpu、memory 为例,简单演示一下 cgroup v2 版本使用

root@mydocker:~# cd /sys/fs/cgroup/
root@mydocker:/sys/fs/cgroup# mkdir test
root@mydocker:/sys/fs/cgroup# cd test
root@mydocker:/sys/fs/cgroup/test# ls
cgroup.controllers      cpu.uclamp.max         memory.current
cgroup.events           cpu.uclamp.min         memory.events
cgroup.freeze           cpu.weight             memory.events.local
cgroup.max.depth        cpu.weight.nice        memory.high
cgroup.max.descendants  cpuset.cpus            memory.low
cgroup.procs            cpuset.cpus.effective  memory.max
cgroup.stat             cpuset.cpus.partition  memory.min
cgroup.subtree_control  cpuset.mems            memory.oom.group
cgroup.threads          cpuset.mems.effective  memory.pressure
cgroup.type             io.max                 memory.stat
cpu.max                 io.pressure            pids.current
cpu.pressure            io.stat                pids.events
cpu.stat                io.weight              pids.max

CPU

启动一个死循环

root@mydocker:/sys/fs/cgroup/test# while : ; do : ; done &
[1] 90482

不出意外的话,应该占用了 100% cpu

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND    
90482 root      20   0   10160   1772      0 R  99.3   0.1   0:05.01 bash 

接下来使用 cgroup v2 限制该进程只能使用 20% cpu

1)修改配置

echo 2000 10000 > cpu.max

含义是在 10000 微秒的 CPU 时间周期内,有 2000 微秒是分配给本 cgroup 的,也就是本 cgroup 管理的进程在单核 CPU 上的使用率不会超过 20%。

2)将进程加入当前 cgroup

echo 90482 > cgroup.procs

再次查看

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND    
90482 root      20   0   10160   1772      0 R  20.2   0.1   2:07.78 bash 

可以看到,已经被限制到了 20%

Memory

接下来演示内存限制,使用以下代码来模拟内存消耗,

cat <<EOF > ~/mem-allocate.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MB (1024 * 1024)

int main(int argc, char *argv[])
{
    char *p;
    int i = 0;
    while(1) {
        p = (char *)malloc(MB);
        memset(p, 0, MB);
        printf("%dM memory allocated\n", ++i);
        sleep(1);
    }

    return 0;
}
EOF

编译

gcc ~/mem-allocate.c -o ~/mem-allocate

然后启动该文件

root@mydocker:/sys/fs/cgroup/test# ~/mem-allocate
1M memory allocated
2M memory allocated
3M memory allocated
4M memory allocated
5M memory allocated
6M memory allocated
7M memory allocated
8M memory allocated
9M memory allocated
10M memory allocated
11M memory allocated
12M memory allocated
^C

可以看到,每秒会消耗 1M 内存,若不停止会一直运行直到 OOM。

接下来使用 cgroup v2 限制最多消耗 10M 内存。

1)修改配置

单位为字节 10485760= 10 * 1024 * 1024

echo 10485760 > memory.max

也就是本 cgroup 管理的进程内存使用不会超过 10M

2)将进程加入当前 cgroup

#将当前bash加入到test中,这样这个bash创建的所有进程都会自动加入到test中
sh -c "echo $$ >> cgroup.procs"

再次查看

root@mydocker:/sys/fs/cgroup/test# ~/mem-allocate
1M memory allocated
2M memory allocated
3M memory allocated
4M memory allocated
5M memory allocated
6M memory allocated
7M memory allocated
8M memory allocated
9M memory allocated
Killed

可以看到,到 10M 时就因为达到内存上限而被 Kill 了。

删除 cgroup

演示完成,把 cgroup 删除。

首先把进程 kill 一下

root@mydocker:/sys/fs/cgroup# cat test/cgroup.procs 
90444
90630
root@mydocker:/sys/fs/cgroup# kill -9 90630
root@mydocker:/sys/fs/cgroup# kill -9 90444

然后删除目录

root@mydocker:/sys/fs/cgroup# rmdir test

这样 cgroup 就删除了。

  1. v1 v2 对比

v1 的 cgroup 为每个控制器都使用独立的树(目录)

[root@docker cgroup]# ls /sys/fs/cgroup/
blkio  cpu  cpuacct  cpuacct,cpu  cpu,cpuacct  cpuset  devices  freezer  hugetlb  memory  net_cls  net_cls,net_prio  net_prio  perf_event  pids  rdma  systemd

每个目录就代表了一个 cgroup subsystem,比如要限制 cpu 则需要到 cpu 目录下创建子目录(树),限制 memory 则需要到 memory 目录下去创建子目录(树)。

比如 Docker 就会在 cpu、memory 等等目录下都创建一个名为 docker 的目录,在 docker 目录下在根据 containerID 创建子目录来实现资源限制。

各个 Subsystem 各自为政,看起来比混乱,难以管理

因此最终的结果就是:

  1. 用户空间最后管理着多个非常类似的 hierarchy
  2. 在执行 hierarchy 管理操作时,每个 hierarchy 上都重复着相同的操作

v2 中对 cgroups 的最大更改是将重点放在简化层次结构上

  • v1 为每个控制器使用独立的树(例如 /sys/fs/cgroup/cpu/GROUPNAME /sys/fs/cgroup/memory/GROUPNAME)。
  • v2 将统一/sys/fs/cgroup/GROUPNAME中的树,如果进程 X 加入/sys/fs/cgroup/test,则启用 test 的每个控制器都将控制进程 X。

更多 v1 和 v2 差异见 v1 存在的问题及 v2 的设计考虑

  1. 小结

本文主要分享了 Linux cgroup v2 版本的基本使用,以及 v1 和 v2 版本的差异。

更多 cgroup v2 信息推荐阅读:Control Group v2 及其译文 Control Group v2(cgroupv2 权威指南)(KernelDoc, 2021)

标签: LinuxCgroup

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。