Kubernetes 集群迁移到 Cgroup V2:原理、兼容性验证与启用步骤

Cgroup V2 相较于 V1 提供了统一层级、更完善的 IO QoS 支持,尤其是对 Buffer IO 的限速能力,是 Kubernetes 集群提升资源利用率的重要基础。本文记录在生产 Kubernetes 集群上迁移到 Cgroup V2 的完整过程:依赖版本要求、启用步骤,以及 CPU、内存、PID、IO 等各资源控制器的兼容性验证结果。

为什么迁移到 Cgroup V2

Cgroup V1 的问题

Cgroup V1 允许多个独立的层级同时存在(每个 controller 一个层级),导致:

  • 同一进程在多个层级中有不同的位置,管理复杂
  • Controller 之间行为不一致,难以推理
  • 资源统计存在重复计算

Cgroup V2 的改进

V2 将所有 controller 合并到单一统一层级(unified hierarchy):

1
2
3
4
5
6
7
8
9
10
/sys/fs/cgroup/
├── system.slice/
├── user.slice/
└── kubepods/ ← Kubernetes 使用的层级,与 V1 路径一致
├── burstable/
│ └── pod-xxx/
│ ├── cpu.max
│ ├── memory.max
│ └── io.max
└── guaranteed/

核心优势:

  • 统一层级:所有 controller 在同一目录树下,管理简单
  • Buffer IO 限速:V1 只能限制 Direct IO,V2 通过 io.max 实现对 Buffer IO 的限速
  • IO 权重调度:配合 BFQ/CFQ 调度器实现 io.weight 按比例分配 IO 带宽
  • iocost 模型:基于延迟的 IO 代价模型,实现更智能的 QoS 调控

依赖版本要求

组件 最低版本 推荐版本 说明
Linux Kernel 4.15 5.2+ 5.6+ 才支持 hugetlb controller
Kubernetes 1.19 1.25+ 1.19 起正式支持 Cgroup V2
containerd 1.4.4 1.6+ 需支持 unified cgroup hierarchy
runc v1.0.0-rc91 v1.0.0+ rc93 起全面支持 unified mode
systemd 244 249+ 244 起支持 cpuset controller delegation
cAdvisor v0.36.1 v0.45+ 支持 cgroup v2 指标采集

启用步骤

1. 升级内核

推荐使用 5.4 或 5.15 内核。低于 4.15 的内核不支持 Cgroup V2。

2. 升级 containerd

containerd 1.4.4 起支持 cgroup v2 unified mode。升级后无需额外配置,containerd 会自动检测宿主机的 cgroup 版本。

3. 添加内核启动参数

在 GRUB 或内核命令行中添加:

1
2
cgroup_no_v1=all
systemd.unified_cgroup_hierarchy=1

可选参数(用于 IO 调试和优化):

1
2
blk_cgroup.blkcg_debug_stats=1   # 开启 blkcg 调试统计
scsi_mod.use_blk_mq=1 # 启用 SCSI 多队列

重启后验证:

1
2
3
4
5
6
# 确认已切换到 cgroup v2
mount | grep cgroup
# 预期输出: cgroup2 on /sys/fs/cgroup type cgroup2

ls /sys/fs/cgroup/
# 应看到 cgroup.controllers, cpu.stat, io.stat 等 v2 接口

4. 加载 BFQ 调度器模块

IO 权重调度依赖 BFQ 调度器:

1
2
3
modprobe bfq
# 切换磁盘到 BFQ 调度器
echo bfq > /sys/block/sda/queue/scheduler

5. 升级 Kubernetes(或 cherry-pick)

Kubernetes 1.19 起原生支持 Cgroup V2,主要相关 commit:

  • bb5ed1b7:kubelet 初始 cgroupv2 支持
  • a9772b22:CPU hard cap 在 cgroupv2 上的实现
  • 79be8be1:hugetlb cgroup 设为可选(兼容低版本内核)
  • 26d94ad6:device cgroup 在 v2 模式下不配置

对于低版本 Kubernetes,可以 cherry-pick 上述 commit。


兼容性验证

以下基于实际测试环境(kernel 5.4,Kubernetes 1.23,containerd 1.4.4)的验证结果。

CPU 控制器

Cgroup V2 使用 cpu.max 替代 V1 的 cpu.cfs_quota_us + cpu.cfs_period_us

1
2
3
4
5
6
7
8
# Pod 级别(Pod 总 CPU limit)
/sys/fs/cgroup/kubepods/burstable/pod<uid>/cpu.max
# 示例: 75600 100000 → 表示 period=100ms 内最多使用 75.6ms CPU(约 0.756 core)

# 容器级别(单个容器)
cat .../pod<uid>/<container-id>/cpu.max
# 25600 100000 → 0.256 core
# 50000 100000 → 0.5 core

行为与 V1 一致,对应用无感知差异。

内存控制器

memory.max 替代 V1 的 memory.limit_in_bytes

1
2
3
4
5
6
7
8
9
10
11
# Pod 总内存 limit
cat .../pod<uid>/memory.max
# 201326592 → 192Mi

# 容器 1: 64Mi limit
cat .../pod<uid>/<container1>/memory.max
# 67108864

# 容器 2: 128Mi limit
cat .../pod<uid>/<container2>/memory.max
# 134217728

注意:V2 新增了 memory.min(硬保证)、memory.low(软保证)、memory.high(压制阈值)等接口,Kubernetes 目前未使用这些字段(值为 0 或 max)。上游 KEP(#2571)正在推进 Memory QoS 支持。

PID 控制器

pids.max 替代 V1 的 pids.limit

1
2
3
4
5
6
7
8
cat /var/lib/kubelet/config.yaml | grep podPidsLimit
# podPidsLimit: 24

cat .../pod<uid>/pids.max
# 24

# 超出限制时:
# sh: can't fork: Resource temporarily unavailable

行为完全一致。

Hugetlb 控制器

内核 5.4 不支持 hugetlb 加入 cgroupv2 controller,需要内核 5.6+。

这是从 V1 迁移到 V2 时最主要的兼容性限制点。如果业务 Pod 使用了 HugePages 资源限制,需要确认内核版本满足要求,或在迁移前评估影响。

IO 控制器(核心新功能)

Cgroup V2 IO 接口:

接口 作用
io.max 限制最大带宽(BPS)或 IOPS,支持 Direct IO 和 Buffer IO
io.weight 按权重比例分配 IO 带宽(需 BFQ/CFQ 调度器)
io.stat IO 统计信息
io.cost.qos 基于延迟的代价模型 QoS 配置
io.cost.model iocost 的物理参数建模

启用示例:

1
2
3
4
5
6
7
8
9
# 对设备 8:0 启用 iocost
echo "8:0 enable=1" > /sys/fs/cgroup/io.cost.qos

# 对 Pod 设置 IO 带宽上限(100MB/s 写入)
echo "8:0 wbps=104857600" > .../pod<uid>/io.max

# 设置 IO 权重(两个容器比例 1:9)
echo "100" > .../pod<uid>/<container1>/io.weight
echo "900" > .../pod<uid>/<container2>/io.weight

V2 IO 控制器相较 V1 最重要的突破是对 Buffer IO 的支持。V1 只能通过 blkio.throttle.write_bps_device 限制 Direct IO,Buffer IO 完全不受控制。V2 通过跟踪 page cache 的 writeback 来实现对 Buffer IO 的限速,这对于实际生产中以 Buffer IO 为主的工作负载(如数据库、日志服务)意义重大。


迁移注意事项

  1. 滚动升级:内核参数变更需要重启节点,建议通过节点轮转(cordon + drain + reboot)的方式平滑迁移
  2. Hugetlb 兼容性:使用 HugePages 的工作负载需要内核 5.6+,迁移前评估
  3. BFQ 模块:IO 权重功能依赖 BFQ 模块,需要在启动脚本中持久化 modprobe bfq
  4. iocost 稳定性io.cost.qos 的自动权重调整(ctrl=auto)在高并发 IO 场景下可能导致 CPU 软锁,建议先在测试环境充分验证,生产使用时优先考虑 ctrl=user 模式