近期 Kubernetes 官方已正式弃用 cgroup v1(v1.35 起,kubelet 默认不再在 cgroup v1 节点上启动),节点启用 cgroup v2 正在从"可选项"变成"必答题"。本文整理:如何用一条命令确认系统用的是 v1 还是 v2、在支持的发行版上如何开启、Kubernetes 对 cgroup v2 的完整要求,以及一个高频疑问——CentOS 7 到底支不支持 cgroup v2。

一、cgroup v1 与 cgroup v2

cgroup(control group,控制组)是 Linux 内核用于约束、统计进程组资源(CPU、内存、IO 等)的机制,kubelet 和容器运行时正是通过它来落实 Pod 的 CPU/内存 requests 与 limits。

两个版本的核心差异:

cgroup v1 cgroup v2
层级结构 每个控制器(cpu、memory、blkio……)各挂一棵独立的层级树,目录分散 单一统一层级,统一挂在 /sys/fs/cgroup
资源核算 各控制器独立统计,内存/内核内存分开记账 统一核算(含网络内存、内核内存、页面缓存回写)
子树委派 粒度粗,安全边界弱 支持更安全的子树委派给容器
新特性 冻结不再新增 支持 PSI(压力阻塞信息)等新原语,K8s 的 MemoryQoS 等特性依赖它

一句话:v2 是下一代的 cgroup API,Kubernetes 自 v1.25 起 cgroup v2 支持已是 GA。

二、一条命令确认当前是 v1 还是 v2

在节点上执行:

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

判定标准(来自 Kubernetes 官方文档):

  • 输出 cgroup2fs → 当前使用 cgroup v2
  • 输出 tmpfs → 当前使用 cgroup v1

辅助交叉验证:

# v1:/sys/fs/cgroup 下是一堆控制器目录(cpu、memory、blkio、pids...)
# v2:根目录下只有 cgroup.controllers、cgroup.subtree_control 等统一接口文件
ls /sys/fs/cgroup/

# v2 特有文件存在即为 v2
ls /sys/fs/cgroup/cgroup.controllers

# 看挂载方式
mount | grep cgroup

三、在受支持的发行版上开启 cgroup v2

1. 优先选默认启用 v2 的发行版

以下发行版默认启用 cgroup v2(K8s 官方列表):

  • Container-Optimized OS(M97 起)
  • Ubuntu(21.10 起,推荐 22.04+)
  • Debian GNU/Linux(11 Bullseye 起)
  • Fedora(31 起)
  • Arch Linux(2021.04 起)
  • RHEL 及类 RHEL 发行版(9 起)

2. 手动开启(以 RHEL/Rocky/Alma 8 为例)

RHEL 8 内核 4.18 支持 cgroup v2,但默认仍以 v1 方式挂载,需改内核启动参数。

方法一:grubby(RHEL 系推荐,改完立即写入所有内核条目,无需手改配置文件)

# 给所有已安装内核的启动参数追加 unified hierarchy 开关
grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1"

# 确认参数已写入(查看默认内核的启动参数)
grubby --info=DEFAULT | grep args

方法二:改 GRUB 配置(通用写法)

# 编辑 /etc/default/grub,在 GRUB_CMDLINE_LINUX 行末追加:
#   systemd.unified_cgroup_hierarchy=1

# 重新生成 grub 配置(BIOS 与 UEFI 路径不同,二选一)
grub2-mkconfig -o /boot/grub2/grub.cfg          # BIOS
grub2-mkconfig -o /etc/grub2-efi.cfg            # UEFI

# Debian/Ubuntu 系直接:
sudo update-grub

生效与验证:

# 重启生效
reboot

# 验证
stat -fc %T /sys/fs/cgroup/    # 期望输出:cgroup2fs

说明:systemd.unified_cgroup_hierarchy=1 由 systemd 负责解析(systemd v239 起支持),它决定把 cgroup 层级以 v2(unified)方式挂载;如果 systemd 太老不认识这个参数,加了也白加——这正是后面 CentOS 7 的死结之一。

四、Kubernetes 使用 cgroup v2 的要求

组件 要求
内核 5.8 及以上
containerd v1.4 及更高
cri-o v1.20 及更高
CGroup 驱动 kubelet 与容器运行时统一使用 systemd cgroup 驱动

几个实用结论:

  • kubelet 会自动检测节点跑在 v1 还是 v2 上,并自动调整行为,无需额外配置。
  • 切换 v2 后日常使用没有感知差异,但直接读 cgroup 文件系统的组件要跟着升级:
  • 第三方监控/安全代理:升级到支持 v2 的版本
  • 独立部署的 cAdvisor:v0.43.0+
  • Java:OpenJDK jdk8u372 / 11.0.16 / 15 及更高(IBM Semeru 8.0.382.0 / 11.0.20.0 / 17.0.8.0+)
  • Node.js:v20.3.0 起才能可靠读取 v2 内存限制(v18 线不可靠,可能误读宿主机总内存导致 OOM,需显式 --max-old-space-size)
  • uber-go/automaxprocs:v1.5.1+
  • cgroup v1 已弃用(K8s v1.35 起):默认 kubelet 不再在 v1 节点启动;确需过渡,要在 kubelet 配置文件中设置 failCgroupV1: false。迁移窗口不多,存量 v1 节点建议尽早规划。

五、重点:CentOS 7 是不是不支持 cgroup v2?

结论:不支持,而且是双重不支持,没有变通。

1. 内核层面:3.10 没有 cgroup v2

cgroup v2(unified hierarchy)在内核 3.16 才首次合入(实验性),4.5 起才稳定。CentOS 7 的内核是 3.10——比 v2 首次合入还要老 6 个小版本,内核代码里根本没有 cgroup2 这套东西。

所以在 CentOS 7 上:

$ stat -fc %T /sys/fs/cgroup/
tmpfs                          # 永远是 tmpfs,即 v1

# 就算把参数塞进 grub,内核 3.10 只会当成 unknown parameter 无视掉
# systemd.unified_cgroup_hierarchy=1

2. systemd 层面:219 也不支持 v2

cgroup v2 的挂载切换由 systemd 完成,这要求较新的 systemd。CentOS 7 的 systemd 是 219(2015 年的版本),完全不支持 unified hierarchy 模式,也不认识 systemd.unified_cgroup_hierarchy 参数。

这意味着:即使通过 ELRepo 把内核升级到 5.x(内核本身已支持 cgroup2),systemd 219 依然无法以 v2 方式挂载和管理 cgroup 层级。强行用内核参数 cgroup_no_v1=all 禁用 v1 去逼系统走 v2,只会让 systemd 的服务资源管理失效,得不偿失。另外别忘了,K8s 官方对 v2 的内核要求是 5.8+,el7 仓库归档前的最终内核 5.4.278 也够不着这条线。

3. 汇总对照

系统 内核 systemd cgroup v2
CentOS 7 3.10 219 完全不支持(内核、systemd 双重缺失)
RHEL/Rocky/Alma 8 4.18 239 支持但默认关闭,可手动开启(见第三节)
RHEL/Rocky/Alma 9 5.14 249 默认开启
Ubuntu 22.04 5.15+ 249 默认开启

4. 实际路径建议

想用 cgroup v2,CentOS 7 上没有升级项可打,只有一条路:换系统。结合 K8s 要求(内核 5.8+、systemd cgroup 驱动),推荐:

  • RHEL 9 / Rocky Linux 9 / AlmaLinux 9(内核 5.14,开箱即 v2,且是 RHEL 系平滑迁移路径)
  • Ubuntu 22.04 LTS、Debian 12

CentOS 7 本身已于 2024-06-30 EOL,与其在旧系统上反复打补丁,不如把"换发行版 + 启用 cgroup v2 + 重装 K8s 节点"作为一个整体迁移项目来做。内核升级的思路可以参考之前写的 CentOS 7.9 升级内核至 5.4.278 实战记录——但请明确:那解决的是内核新特性需求,解决不了 cgroup v2 的问题。

六、小结

  1. 判断版本只要一条命令:stat -fc %T /sys/fs/cgroup/,cgroup2fs 是 v2,tmpfs 是 v1。
  2. 开启 v2 首选默认启用的发行版(RHEL 9+、Ubuntu 22.04+ 等);RHEL 8 系推荐 grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1" 一条命令开启(或改 GRUB 配置)。
  3. K8s 侧要求:内核 5.8+、containerd 1.4+、systemd cgroup 驱动;v1 已弃用,kubelet 默认不再在 v1 节点启动。
  4. CentOS 7 = 内核 3.10 + systemd 219,两头都不支持 cgroup v2,升级内核也无解,只能换系统。

参考:

  • Kubernetes 官方文档《关于 CGroup v2》:https://kubernetes.io/zh-cn/docs/concepts/architecture/cgroups/
  • runc cgroup v2 文档(发行版支持列表):https://github.com/opencontainers/runc/blob/main/docs/cgroup-v2.md
  • cgroup 手册页:https://man7.org/linux/man-pages/man7/cgroups.7.html
文章作者: emporer
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Emporer-Linux!
喜欢就支持一下吧