Cgroup v2和v1的区别

本文主要介绍cgroup v2和v1的区别。

Cgroups(Control Groups)是 Linux 内核提供的一种资源限制、审计与隔离机制。它允许

将进程分组,并对这些组应用资源限制(如 CPU、内存、IO 等)。目前存在两个主要版本:cgroup v1 和 cgroup v2。

1. cgroup v1 和 v2 的主要区别

1.1. 层级结构设计

cgroup v1:

  • 每个子系统(controller,如 cpu、memory)可以挂载在不同的层级上。

  • 不同资源控制器可以拥有不同的层级结构。

  • 同一个进程可以出现在多个层级中(因为各 controller 独立)。

缺点:

  • 管理复杂:不同 controller 的层级可能冲突。

  • 用户空间程序难以协调多个资源的管理。

cgroup v2:

  • 所有 controller 使用统一的单层次树形结构(single unified hierarchy)。

  • 每个进程只能属于一个 cgroup。

  • 更简洁、更一致、更容易管理。


1.2. 资源控制行为改进

cgroup v1:

  • 控制器之间独立实现,行为不一致。

  • 有些 controller 行为奇怪或不完整(如 memory 的 oom 行为、cpu shares)。

cgroup v2:

  • 控制器重写或增强,行为更加一致和合理。

  • 更加严格的限制与传播模型,例如:

    • 子 cgroup 的资源使用总量不能超过父级分配。

    • 支持精细的 IO 带宽控制(通过 io.max 取代 v1 的 blkio 控制器)。

    • 内存高水位线通知 (memory.high),支持 memory pressure 的渐进控制。


1.3. 配置接口(文件系统)

cgroup v1:

  • 每个 controller 会暴露多个文件,如 memory.limit_in_bytes、cpu.shares。

  • 文件名不一致、语义也不统一。

cgroup v2:

  • 使用统一的接口命名,如:

    • memory.max 替代 memory.limit_in_bytes

    • cpu.max 替代 cpu.cfs_quota_us 等

  • 配置方式更简洁、更一致。


1.4.进程行为控制

cgroup v1:

  • 没有明显的限制,父级 cgroup 中的进程可以和子 cgroup 并存。

  • 容易产生“叶子节点”和“中间节点”混用的问题。

cgroup v2:

  • 中间节点不能包含进程,只有叶子节点可以运行进程。

  • 强制遵循“控制权不可传递”,资源必须分配到底层。


1.5. 内核支持和兼容性

  • cgroup v1:默认启用在旧版本 Linux(如 CentOS 7)。

  • cgroup v2:Linux kernel 4.5 引入,5.x 逐渐成熟,现在主流发行版(如 Fedora、Ubuntu 22.04+)已默认使用 cgroup v2。


1.6. 总结对比表

特性 cgroup v1 cgroup v2
控制器挂载 多层级结构 单一统一层级
文件接口 各控制器自定义 统一命名和行为
进程限制 可混用中间/叶子节点 只能在叶子节点运行
控制器一致性 不一致,独立实现 统一,行为一致
内核支持 老系统默认使用 新内核推荐使用

2. Cgroup v2的资源隔离性优化

2.1. 统一的层级结构,消除隔离漏洞

📌 v1 问题:

  • 每个 controller(如 cpu、memory、blkio)可以挂载在不同的层级。

  • 一个进程可能出现在多个 cgroup(分别控制不同资源),导致资源隔离不一致或冲突。

    • 例如:cpu 限制在 /cg1,memory 限制在 /cg2,无法准确控制组合资源使用。

✅ v2 优化:

  • 所有 controller 使用统一的单层级树(unified hierarchy)。

  • 每个进程只能存在于一个 cgroup 中,所有资源限制统一生效。

  • 隔离策略具有一致性和传递性,防止“某些资源没隔离”的情况。

🔍 好处: 避免多 controller 层级不一致造成的隔离漏洞,资源限制行为更可预期。


2.2. 增强的内存隔离机制

v2 新特性:

  • 引入 memory.high(软限制):超过后触发页回收(但不立即 OOM)。

  • 强化 memory.max(硬限制):超过直接触发 OOM。

  • 支持 OOM group kill:可将整个 pod 内的多个进程作为一组统一 kill。

v1 缺陷:

  • 只有 memory.limit_in_bytes,OOM 后果不可控,不能渐进响应内存压力。

🔍 好处:

  • 实现更平滑的资源压力反馈,容器在压力下可“自救”。

  • 防止 noisy neighbor(内存噪声邻居)干扰其他 pod。


2.3. 更精准的 CPU 调度控制

v2 改进:

  • 使用 cpu.max 设置 CPU 带宽上限,精度提升。

  • 引入 cpu.weight(0~10000):代替 v1 的 cpu.shares,线性、可加性更强。

  • 支持 按比例调度,防止低优先 pod 抢占高优先资源。

v1 问题:

  • cpu.shares 非线性、不稳定。

  • cfs_quota 控制粒度粗,容易造成抖动。

🔍 好处: CPU 时间分配更加公平和稳定,防止容器之间 CPU 抢占影响彼此性能。


2.4. IO 隔离更细粒度和可控

v2 提供:

  • io.max:按设备设置带宽/IOPS 上限。

  • io.weight:设置权重调度优先级。

例如:

echo "8:0 rbps=1048576 wbps=1048576" > io.max # 限制 /dev/sda 读写速率为 1MB/s

v1 问题:

  • 使用 blkio.*,行为依赖块设备,支持不一致,精度低。

  • IOPS 与带宽不能同时控制,难以部署混合 workload。

🔍 好处: 提供IO 抢占控制、防止磁盘打满拖垮其他 pod,对数据库/日志类容器特别有利。


2.5. 压力感知能力(PSI)提升感知性隔离

v2 新特性:

  • 原生支持 Pressure Stall Information (PSI):

    • 提供 cpu.pressure、memory.pressure、io.pressure 等指标。

    • 显示资源的阻塞比例、等待时间(如 some / full stall)。

v1 不支持 PSI。

🔍 好处:

  • 可用于调度器做资源感知(例如 Koordinator、Kubelet QRM)。

  • 实现动态隔离/弹性扩缩容策略,提前感知资源瓶颈。


2.6. 进程放置约束提升层级隔离清晰度

v2 要求:

  • 只有叶子节点可以运行进程。

  • 中间节点用于组织和资源继承,避免"父子 cgroup 都有进程"的资源争用。

v1 无此限制,容易产生隔离混乱。

🔍 好处: 保证资源继承链明确,避免“父子抢资源”问题。


2.7. 更好的资源传播和继承规则

  • v2 明确规定:子 cgroup 的资源配额不可超过父级设置。

  • v1 中 controller 行为不一致,有些 controller(如 cpu)资源限制不会级联。

🔍 好处: 资源隔离链路完整,防止容器层越权使用 pod 层的资源限制之外的资源。

2.8. 资源隔离性提示对比

功能点 cgroup v1 cgroup v2 提升点
层级一致性 ❌ 多层级 ✅ 单层级 统一控制、隔离不混乱
内存隔离 一刀切、容易 OOM 支持 memory.high + OOM group kill 渐进隔离、防止雪崩
CPU 限制 粒度粗、抢占明显 带宽精细 + weight 线性 可控公平调度
IO 控制 粗糙、支持差 io.max, io.weight 精细控制 IO 不互相影响
PSI 支持 ❌ ✅ 可用于资源压力调度
运行位置限制 无要求 只能在叶子节点 结构清晰,避免冲突
资源继承传播 弱、不一致 强、自动级联 隔离逻辑严密

3. cgroup v1 和 cgroup v2 的层级结构差异

场景:一个 Pod 内有两个容器 containerA 和 containerB

3.1. cgroup v1 的层级格式(多树结构)

在 cgroup v1 中,每个 controller(如 memory、cpu)是单独挂载的子系统,彼此独立,层级不一致:

# memory 子系统的 cgroup 层级:
/sys/fs/cgroup/memory/kubepods/pod123/containerA
/sys/fs/cgroup/memory/kubepods/pod123/containerB

# cpu 子系统的 cgroup 层级可能不同(可能是按 qos 再分层):
/sys/fs/cgroup/cpu/kubepods/burstable/pod123/containerA
/sys/fs/cgroup/cpu/kubepods/burstable/pod123/containerB

# blkio 也可能完全不同
/sys/fs/cgroup/blkio/k8s.io/pod123/containerA
/sys/fs/cgroup/blkio/k8s.io/pod123/containerB

问题:

  • 资源限制配置分散,隔离策略难以统一。

  • kubelet 或 CRI 管理难度高,不易维护。

  • 某些 controller 中,父子 cgroup 会混放进程,造成资源混抢。

3.2. cgroup v2 的层级格式(统一树结构)

cgroup v2 所有控制器(memory、cpu、io)使用统一的挂载点,并严格限制结构层次:

# 所有 controller 都在统一挂载点 /sys/fs/cgroup
/sys/fs/cgroup/kubepods.slice/
└── kubepods-burstable.slice/
    └── kubepods-burstable-pod123.slice/
        ├── containerA.scope/
        └── containerB.scope/

说明:

  • 所有资源限制(CPU、memory、IO)都在 /sys/fs/cgroup/kubepods-burstable-pod123.slice/containerA.scope/ 这个目录中。

  • 每个 container 的资源控制逻辑清晰统一。

  • 不允许 container 和 pod 同时在一个目录中运行进程(叶子节点原则)。

3.3. 对比总结表

项目 cgroup v1 cgroup v2
控制器层级 每个控制器独立(多棵树) 所有控制器统一(单棵树)
示例目录结构 memory/..., cpu/..., blkio/... /sys/fs/cgroup/...(统一)
资源隔离配置 分散在不同路径 集中在统一路径
父子节点进程混放 可能混放 严格禁止,父不能运行进程
管理复杂度 高,需多层次同步 低,集中管理

4. cgroup v2 常用资源限制文件汇总

4.1. CPU 控制器文件(cpu.*)

文件名 用途说明 示例值说明
cpu.max 限制 CPU 最大使用带宽,格式为 quota period 50000 100000:每 100ms 最多用 50ms,即 0.5 CPU
cpu.weight 设置 CPU 相对调度权重(1~10000) 100(默认)权重越高调度越积极
cpu.stat CPU 使用统计 包含 usage_usec、throttled_usec 等累计指标
cpu.pressure CPU 资源压力监控(PSI) some avg10=0.00 avg60=0.00 avg300=0.00 total=0

4.2. 内存控制器文件(memory.*)

文件名 用途说明 示例值说明
memory.max 内存硬限制,超出则 OOM 536870912 表示 512MB
memory.high 内存软限制,超过则触发回收(不 OOM) 268435456 表示 256MB
memory.low 设置最低保证值,低于此值尽量不回收 134217728 表示 128MB
memory.current 当前使用内存 123456789 表示当前用约 117MB
memory.stat 内存使用细节统计 包括 anon、file、rss、slab、cache 等
memory.events 记录 OOM、high 命中等事件次数 high 1, oom 0 等
memory.pressure 内存 PSI 指标 some avg10=0.10 avg60=0.08 avg300=0.05 total=1234

4.3. IO 控制器文件(io.*)

文件名 用途说明 示例值说明
io.max 限制块设备 IO 带宽/IOPS 8:0 rbps=1048576 wbps=1048576 表示限速 1MB/s
io.weight 设置相对 IO 权重(1~10000) 100(默认)
io.stat 实际 IO 统计(读写字节/次数) 8:0 rbytes=12345678 wbytes=87654321 rios=123 wios=321
io.pressure IO PSI 指标 some avg10=0.00 avg60=0.00 avg300=0.00 total=0

4.4. 进程数控制器文件(pids.*)

文件名 用途说明 示例值说明
pids.max 限制最大进程/线程数 512:最多允许 512 个进程/线程
pids.current 当前进程/线程数量 18:当前在此 cgroup 中运行的数量

4.5. 通用控制器文件(所有 cgroup 目录都会包含)

文件名 用途说明 示例值说明
cgroup.procs 包含在该 cgroup 中的进程 PID 1234\n5678 等
cgroup.threads 包含线程 ID(TID) 1234\n5678(不同于 PID)
cgroup.controllers 当前节点支持的 controller 列表 cpu memory io pids
cgroup.subtree_control 子 cgroup 可继承的 controller 设置 +cpu +memory +io(必须手动 echo 设置)
cgroup.stat cgroup 统计信息 nr_descendants=3 nr_dying_descendants=0
cgroup.events 当前 cgroup 状态事件布尔值 populated 1 memory.events oom=0

4.6. 高阶行为控制

常见于 systemd 管理或进程终止场景

文件名 用途说明 示例值说明
cgroup.freeze 是否冻结此 cgroup 中所有进程(0=运行,1=冻结) 0:未冻结
cgroup.kill 向所有进程发送 SIGKILL,强制终止 (执行 echo 1 > cgroup.kill)
cgroup.type 当前 cgroup 类型 domain / threaded / domain threaded 等

5. Koordinator vs Volcano cgroup v2对比

Koordinator和Volcano是常用的在服务混布和调度方面的开源调度组件,以下是 Koordinator vs Volcano 在 cgroup v2 层面对 Pod 资源隔离增强能力的对比表格,从四大类资源(CPU、内存、IO、网络)出发,列出各自支持的能力、作用机制和相关的 cgroup v2 文件名。

类别 能力 说明 Koordinator Volcano 使用的 cgroup v2 文件
CPU cpu.weight 调整调度优先级,高权重获得更多 CPU 时间片 ✅ ✅ cpu.weight
cpu.max 限速 限制低优 pod 最大可用 CPU 带宽 ✅ ✅ cpu.max
动态 CPU Throttle 根据压力指标自动调整低优 CPU 限速 ✅(PSI 感知) ❌ cpu.max, cpu.pressure
PSI 感知调度 根据 cpu.pressure 控制 pod 的调度去向 ✅ ❌ cpu.pressure
NUMA / CPU 绑核 结合 Topology 策略、QoS 实现高优绑核 ✅ ✅ 非标准:cpuset.cpus(结合 CRI)
内存 memory.high 软限 超过此值将触发内核回收机制,避免高优被影响 ✅ ✅ memory.high
memory.low 保底 高优 pod 内存保底,不被回收 ✅ ❌ memory.low
OOM Group Kill 高优任务不被单独 kill,统一 OOM 策略 ✅ ✅ memory.events, cgroup.kill
PSI 感知调度 通过 memory.pressure 控制任务调度,避免拥塞节点 ✅ ❌ memory.pressure
IO io.max 带宽限速 精确限制某容器的磁盘读写速率(rbps/wbps) ✅ ✅ io.max
io.weight 权重 调整 IO 调度优先级 ✅ ✅ io.weight
IO PSI 感知调度 避免将 pod 调度到高 IO 压力节点 ✅ ❌ io.pressure
网络 网络速率控制(带宽) 非 cgroup v2 标准,通过 eBPF/TC/net_cls 配合实现 ✅(需扩展组件) ❌ 非标准:需 eBPF + tc + net_cls
网络 NUMA 感知调度 网络与计算资源拓扑感知,避免带宽竞争 ✅ ❌ 非标准(TopologyHints)
通用 PSI 感知 综合调度参考 CPU/Memory/IO pressure ✅ ❌ *.pressure
Freeze / Kill 控制 在极端负载时暂停或终止低优任务 ✅(结合 memory.qos) ✅ cgroup.freeze, cgroup.kill
容器级 QoS 管控 每个容器细粒度配置 resource policy ✅ ❌ cpu.*, memory.*, io.*

6. 配置 Kubernetes 使用 cgroup v2

6.1. 版本要求

条件/组件 要求/版本
Linux 内核 ≥ 5.4(推荐 ≥ 5.10)
containerd ≥ 1.6,配置 SystemdCgroup = true
Kubernetes ≥ 1.24,建议 1.28+

6.2. 配置 Kubernetes 使用 cgroup v2

配置 kubelet 使用 systemd 驱动(匹配 cgroup v2)

  • kubelet 启动参数:
--cgroup-driver=systemd
  • containerd 的配置文件(/etc/containerd/config.toml):
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] 
    SystemdCgroup = true
  • 重启 containerd:
systemctl restart containerd

或者

使用 kubeadm init 时建议显式指定:

kubeadm init --cgroup-driver=systemd ...

6.3. 验证 Kubernetes 是否运行在 cgroup v2

检查 cgroup 挂载类型:

mount | grep cgroup
# 输出如下则为cgroup v2
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)

验证 Pod 使用的是 cgroup v2 路径:

ps -eo pid,comm,cgroup | grep containerd-shim

输出中 cgroup 路径应以 /kubepods.slice/ 开头,无 cpu,cpuacct: 等逗号分隔子系统。

参考:


最后修改 July 12, 2025: cgroup v2 vs v1 (ca46a21)