本文主要介绍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
:设置权重调度优先级。
例如:
1 | 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)是单独挂载的子系统,彼此独立,层级不一致:
1 | # memory 子系统的 cgroup 层级: |
问题:
-
资源限制配置分散,隔离策略难以统一。
-
kubelet 或 CRI 管理难度高,不易维护。
-
某些 controller 中,父子 cgroup 会混放进程,造成资源混抢。
3.2. cgroup v2 的层级格式(统一树结构)
cgroup v2 所有控制器(memory、cpu、io)使用统一的挂载点,并严格限制结构层次:
1 | # 所有 controller 都在统一挂载点 /sys/fs/cgroup |
说明:
-
所有资源限制(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 启动参数:
1 | --cgroup-driver=systemd |
- containerd 的配置文件(
/etc/containerd/config.toml
):
1 | [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] |
- 重启 containerd:
1 | systemctl restart containerd |
或者
使用 kubeadm init
时建议显式指定:
1 | kubeadm init --cgroup-driver=systemd ... |
6.3. 验证 Kubernetes 是否运行在 cgroup v2
检查 cgroup 挂载类型:
1 | mount | grep cgroup |
验证 Pod 使用的是 cgroup v2 路径:
1 | ps -eo pid,comm,cgroup | grep containerd-shim |
输出中 cgroup 路径应以 /kubepods.slice/
开头,无 cpu,cpuacct:
等逗号分隔子系统。
参考:
赞赏一下