最近Docker Engine v29.0.0 和 containerd 2.2.0 均宣布 cgroup v1 is deprecated,支持将持续到 2029 年 5 月,但建议尽快迁移到 cgroup v2。
对于运维团队来说,现在就是规划迁移的最佳时机。
什么是 cgroup
cgroup (Control Groups) 是 Linux 内核提供的一种资源管理机制,用于限制、记录和隔离进程组使用的物理资源。它是容器技术的核心基础之一,Docker、Kubernetes 等容器平台都依赖 cgroup 来实现资源隔离和限制。
核心功能
cgroup 提供四大类功能:
- 资源限制 (Resource Limiting): 限制进程组可以使用的资源量,比如 CPU、内存的上限
- 优先级分配 (Prioritization): 控制进程组的资源分配优先级,确保关键服务获得足够资源
- 资源统计 (Accounting): 统计进程组使用的资源量,用于计费、监控或性能分析
- 进程控制 (Control): 可以冻结进程组或重启进程组,实现批量管理
可控制的资源类型
cgroup 可以精细控制多种系统资源:
- CPU: 限制 CPU 使用份额、绑定特定 CPU 核心
- 内存: 限制内存使用上限、控制 swap 使用
- 网络: 控制网络流量和优先级
- 块设备 I/O: 限制磁盘读写操作的速率
- 设备访问: 控制对设备文件的访问权限
- 进程数量: 限制进程组可以创建的进程数量
这些能力使得 cgroup 成为容器技术、系统资源管理的关键组件。systemd 也使用 cgroup 来管理系统服务的资源。
为什么 Docker 和 containerd 要弃用 cgroup v1
Docker Engine v29.0.0 和 containerd 2.2.0 的release note中都将 cgroup v1 标记为弃用状态,这个决定背后有几个重要原因:
1. 技术架构的演进
cgroup v2 采用了全新的统一层级架构,相比 v1 的混合层级模型有显著优势:
- 更简单的管理模型: 单一控制树替代多个独立的层级树
- 更好的性能: 内核级别的优化,减少了资源管理开销
- 更一致的行为: 强制进程在所有资源控制器中的位置一致
2. Linux 内核社区的推荐
Linux 内核社区已经将重心转移到 cgroup v2 的开发和维护上。cgroup v1 虽然仍在维护,但新功能主要在 v2 中实现。
3. 简化代码库维护
同时维护两套 cgroup 接口增加了代码库的复杂度。通过弃用 v1, 可以减少技术债务,更专注于新特性开发。
4. 行业标准化趋势
现代 Linux 发行版正在逐步默认启用 cgroup v2:
- Ubuntu 22.04 LTS 及以上版本
- Fedora 31 及以上版本
- RHEL/CentOS 9 及以上版本
- Debian 11 及以上版本
虽然支持会持续到 2029 年 5 月,但这给了企业用户充足的迁移时间。对于新项目, 可直接使用 cgroup v2。
检查系统的 cgroup 版本
在规划迁移之前,首先需要确认系统当前使用的 cgroup 版本。下面介绍几种常用的检查方法。
方法 1: 使用 stat 命令
这是最直接的检查方法:
stat -fc %T /sys/fs/cgroup/输出解读:
cgroup2fs- 系统使用 cgroup v2 (统一层级)tmpfs- 系统使用 cgroup v1 (混合层级)
这个命令通过检查 /sys/fs/cgroup/ 的文件系统类型来判断。cgroup v2 使用专门的 cgroup2fs 文件系统,而 v1 则挂载为 tmpfs。
方法 2: 通过 Docker 检查
如果使用的是 Docker,可以直接查询:
docker info | grep -i cgroup示例输出:
Cgroup Driver: systemd
Cgroup Version: 2这个方法不仅显示版本,还能看到 Docker 使用的 cgroup 驱动 (driver)。常见的驱动有 systemd 和 cgroupfs。
方法 3: 检查内核挂载点
通过查看挂载信息可以更详细地了解 cgroup 配置:
mount | grep cgroupcgroup v2 的典型输出:
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)只有一个挂载点,所有资源控制器都在统一的 cgroup2 文件系统中。
cgroup v1 的典型输出:
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
...可以看到多个挂载点,每个资源控制器 (cpu、memory、blkio 等) 都有独立的层级树。
方法 4: 检查发行版版本
不同的 Linux 发行版在不同版本开始默认使用 cgroup v2:
cat /etc/os-release默认使用 cgroup v2 的发行版:
| 发行版 | 起始版本 |
|---|---|
| Ubuntu | 22.04 LTS+ |
| Fedora | 31+ |
| RHEL/CentOS | 9+ |
| Debian | 11+ |
| Arch Linux | 滚动更新 |
如果系统版本在此列表中,很可能已经在使用 cgroup v2。但最好还是通过前面的方法明确确认。
统一层级 vs 混合层级
理解这两种架构的区别,是掌握 cgroup 版本差异的关键。
统一层级 (Unified Hierarchy) - cgroup v2
cgroup v2 使用单一的统一层级树:
- 单一控制树: 所有的资源控制器 (CPU、内存、IO 等) 都在同一个层级树中管理
- 一致的结构: 进程在所有资源控制器中的位置是相同的
- 简化管理: 只需要在一个地方管理进程的资源限制
目录结构示例:
/sys/fs/cgroup/
├── user.slice/
│ └── user-1000.slice/
│ └── session-1.scope/
│ ├── cpu.max # CPU 控制
│ ├── memory.max # 内存控制
│ └── io.max # IO 控制
└── system.slice/
└── docker.service/
├── cpu.max
├── memory.max
└── io.max在这个结构中,每个 cgroup 都包含所有资源控制器的配置文件。想要限制 docker.service 的资源,只需要在一个目录下设置相应的参数。
混合层级 (Hybrid Hierarchy) - cgroup v1
cgroup v1 使用多个独立的层级树:
- 多个控制树: 每个资源控制器 (CPU、内存、IO 等) 都有自己独立的层级树
- 独立管理: 同一个进程在不同资源控制器中可能处于不同的层级位置
- 复杂性高: 需要在多个地方分别配置和管理
目录结构示例:
/sys/fs/cgroup/
├── cpu/ # CPU 控制器的独立树
│ ├── docker/
│ └── user.slice/
├── memory/ # 内存控制器的独立树
│ ├── docker/
│ └── system.slice/
├── blkio/ # IO 控制器的独立树
│ ├── docker/
│ └── user.slice/
└── cpuacct/ # CPU 统计的独立树
├── docker/
└── system.slice/在 v1 中,要限制 docker 容器的资源,需要分别在 cpu/docker/、memory/docker/、blkio/docker/ 等多个目录下设置。
迁移到 cgroup v2
如果还在使用 cgroup v1,并且计划迁移到 v2,下面是一些实用建议。
迁移前的准备
1. 评估兼容性
检查关键应用和工具是否支持 cgroup v2:
- 容器运行时: Docker 20.10+、containerd 1.4+、Podman 2.0+ 都支持 v2
- 编排系统: Kubernetes 1.25+ 推荐使用 v2
- 监控工具: 确认 Prometheus、cAdvisor 等工具版本兼容
- 自定义脚本: 审查直接操作 cgroup 的脚本
检查系统兼容性:
除了应用兼容性,还需要确认系统组件版本满足要求。
最低内核版本要求:
# 检查内核版本
uname -r
# cgroup v2 要求:
# - 最低: Linux 4.15
# - 推荐: Linux 5.2+如果内核版本低于 4.15,必须先升级操作系统。内核 5.2 以上版本包含了完整的 cgroup v2 freezer 支持,在生产环境中更稳定。
systemd 版本要求:
# 检查 systemd 版本
systemd --version
# 推荐: systemd 244+systemd 是现代 Linux 发行版的核心组件,与 cgroup 紧密集成。版本 244 以下的 systemd 在 cpuset 控制器的委托功能上存在限制,可能影响容器的 CPU 亲和性配置。
2. 查阅容器运行时的迁移文档
对于 Docker:
Docker 20.10+ 原生支持 cgroup v2,在检测到系统使用 cgroup v2 时会自动切换到 systemd cgroup 驱动。需要确保配置文件 /etc/docker/daemon.json 中设置了正确的驱动。详细内容可参考 Docker 官方文档。
对于 containerd:
containerd 1.4+ 支持 cgroup v2,需要在配置文件 /etc/containerd/config.toml 中显式启用 systemd cgroup 驱动。在 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] 部分设置 SystemdCgroup = true。更多配置细节见 containerd cgroups v2 文档。
对于 Kubernetes:
Kubernetes 1.25+ 正式支持 cgroup v2 (GA)。kubelet 在检测到 cgroup v2 系统时会自动适配,但需要确保 kubelet 和容器运行时都使用 systemd cgroup 驱动。节点迁移时需要逐个替换。完整迁移指南参考 Kubernetes cgroup v2 文档。
迁移策略建议
在非生产环境先进行充分测试,验证应用行为和性能指标。对于生产环境,建议采用滚动升级策略,逐步迁移节点,并保持回滚能力。
总结
cgroup 作为容器技术的基础,正在经历从 v1 到 v2 的重要演进。虽然 Docker Engine v29.0.0 和 containerd 2.2.0 将 cgroup v1 的支持延续到 2029 年 5 月,但现在就是开始规划迁移的最佳时机。