一、从 v1 到 v2:架构演进的必然性
Linux Control Groups(cgroups)是内核的基础资源隔离机制。cgroups v1 本质上是多个独立子系统(cpu、memory、blkio、pids 等)的集合,导致资源管理划分不明确、配额计算不一致、子树迁移不兼容。cgroups v2 重新设计了统一层级树(Unified Hierarchy),解决了 v1 的“不可避免失败”问题。
v2 的核心变革包括:单一挂载点、基于权重的资源分配模型(cpu.weight 替代 cpu.shares)、一致的内存控制接口(memory.max/memory.min/memory.high)、统一的 I/O 控制(io.max/io.low),以及至关重要的 cgroup.freeze 无 kill 进程暂停能力。
二、统一层级树与 subtree_control
cgroups v2 只有一个层级树,通过 cgroup.subtree_control 文件控制哪些控制器在子树中生效。这是一个“包含-no-进程”规则:v2 要求进程只能存在于叶子 cgroup 中,中间节点不能持有进程。
# 查看 cgroup v2 挂载点
mount |grep cgroup
# cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)
# 启用 CPU 和内存控制器(在根 cgroup 下)
echo "+cpu +memory +io +pids" > /sys/fs/cgroup/cgroup.subtree_control
# 创建子 cgroup 并自动继承控制器
mkdir /sys/fs/cgroup/myapp
cat /sys/fs/cgroup/myapp/cgroup.controllers
# cpu memory io pids
这一机制保证了从根到叶的资源分配是一条连续的决策链,避免了 v1 中不同子系统层级树分离导致的不确定性。
三、cpu.weight:替代 shares 的科学分配模型
v1 使用 cpu.shares(默认 1024),但其分配行为随 CPU 数量变化不一致。v2 引入 cpu.weight(1-10000,默认 100),基于“权重比例”计算公式——某 cgroup 获得的 CPU 时间 = 该 cgroup 权重 / 同级所有竞争 cgroup 权重之和 × 可用 CPU 总量。
# web 服务分配 60% 权重,background 任务 5%
echo "6000" > /sys/fs/cgroup/myapp/web/cpu.weight
echo "500" > /sys/fs/cgroup/myapp/background/cpu.weight
# 硬限制:web 服务最多使用 200% CPU(2 个核全满载)
echo "200000 100000" > /sys/fs/cgroup/web/cpu.max
# 格式: ,表示每 100000μs 内最多用 200000μs CPU 时间
此外 cpu.max.burst(v5.15+)允许 cgroup 积累空闲 CPU 额度供突发使用,避免不必要的限流毛刺——这对 Web 服务应对流量尖峰至关重要。
四、内存控制:max/min/high/low 四级水位线
memory.max 是硬限制:cgroup 使用量达到此值将立即触发同步 OOM killer。memory.min 是硬保护:内核不会回收此 cgroup 的内存(即使系统全局内存紧张),适合数据库 buffer pool 等关键缓存场景。
# 硬性保护 512MB
echo "536870912" > /sys/fs/cgroup/database/memory.min
# 软上限(目标值)——内核尽力将使用量压到 2GB 以下
echo "2147483648" > /sys/fs/cgroup/database/memory.high
# 硬限制——OOM 触发线
echo "2684354560" > /sys/fs/cgroup/database/memory.max
# low(v5.19+)——被动回收起始线,通常设低于 high
实战中的黄金组合:memory.min 保护热数据缓存 → memory.high 触发异步回收 → memory.max 作为最后防线。配合 PSI(Pressure Stall Information)监控,可以在 OOM 前主动降载。
五、io.max:块设备带宽与 IOPS 限速
v1 的 blkio 控制器混乱且难以操作。v2 的 io.max 支持按设备号精确定制 rbps(读带宽)、wbps(写带宽)、riops、wiops 四个维度。
# 找到块设备号
lsblk -d -o NAME,MAJ:MIN
# sda 8:0
# 限制 sda 读取带宽 100MB/s,写入 IOPS 5000
echo "8:0 rbps=104857600 wbps=0 riops=0 wiops=5000" \
> /sys/fs/cgroup/myapp/io.max
# 支持通配符(v5.19+)
echo "* rbps=52428800" > /sys/fs/cgroup/myapp/io.max
io.low 则提供“尽力而为”的 QoS 下限保证。注意:限速只对直接 I/O(direct I/O)生效,经过页缓存的 writeback I/O 受 memory.high 间接调控。
六、cgroup.freeze:无 kill 进程暂停
这是 v2 最被低估的功能。相比 v1 需要发送 SIGSTOP 信号,cgroup.freeze 将整个 cgroup 进入冻结状态——进程不会被杀死,只是暂停执行,解冻后继续运行。本质上是系统级 suspend。
# 冻结整个 cgroup
echo 1 > /sys/fs/cgroup/myapp/cgroup.freeze
# 查看状态
cat /sys/fs/cgroup/myapp/cgroup.freeze
# 1(THAWED=0, FREEZING=1, FROZEN=2)
# 解冻
echo 0 > /sys/fs/cgroup/myapp/cgroup.freeze
典型应用场景:滚动更新期间冻结流量处理进程 → 确保不丢连接;暂停批处理任务给在线服务让路;应急降级时秒级暂停非核心负载。相比 stop/start 进程,freeze/thaw 保持 TCP 连接、内存状态、文件描述符完整。
七、systemd 集成:原生 cgroup v2 管理
systemd 自 v233 起深度集成 cgroups v2。Slice、Scope、Service 三种单元类型都是 cgroup 包装器:
[Unit]
Description=Web Application Slice
[Slice]
# CPU 权重
CPUWeight=6000
# 硬 CPU 限制
CPUQuota=200%
# 内存硬保护
MemoryMin=512M
# 内存软上限
MemoryHigh=2G
# 内存硬上限
MemoryMax=2.5G
# IO 权重
IOWeight=500
# IO 读写带宽限制
IOReadBandwidthMax=/dev/sda 100M
IOWriteBandwidthMax=/dev/sda 50M
# PIDs 限制
TasksMax=4096
Instantiated templates(如 [email protected])自动为每个用户创建独立 cgroup 树,彻底替代手工 mkdir + echo 管理。
八、生产环境最佳实践
1. 预留系统 slice:确保 system.slice 和 user.slice 保留足够的资源分配,避免工作负载 slice 饿死内核守护进程。
# /etc/systemd/system.conf
[Manager]
DefaultCPUWeight=100
DefaultMemoryMin=
DefaultIOWeight=100
2. 容器编排适配:Docker(v20.10+)、containerd、Kubernetes 均已支持 cgroups v2。K8s v1.28+ 推荐启用 SupportPodPidsLimit 与 CgroupsV2 特性门控。
3. PSI 监控闭环:通过 /sys/fs/cgroup/.../cpu.pressure、memory.pressure、io.pressure 获取压力停滞信息,配合 prometheus node_exporter 构建预警体系。
# Prometheus 指标示例
node_pressure_cpu_waiting_seconds_total
node_pressure_memory_stalled_seconds_total
node_pressure_io_stalled_seconds_total
4. OOM 通知链:使用 cgroup.oom 事件通知(通过 inotify 或 sd-event)捕获 OOM 时机,配合 cgroup 统计文件回溯内存分配趋势。在 K8s 中,MemoryQoS特性依赖 memory.min 实现 Guaranteed QoS 等级。
九、与 v1 的兼容共存
内核允许多个 cgroup 文件系统同时挂载:v2 为主层级(unified),v1 控制器可挂载到独立目录(legacy)。现代过渡方案是 hybrid mode:先用 v2 管理 CPU/内存,磁盘 IO 仍走 v1 blkio。
使用 systemd.unified_cgroup_hierarchy=1 内核参数强制纯 v2 模式。RHEL 9、Ubuntu 22.04+、Debian 12 已默认启用纯 cgroup v2。
十、写在最后
cgroups v2 不是对 v1 的渐进优化,而是对 Linux 资源管理范式的重新定义。权重模型替代份额模型、四级水位线替代高低水位、freeze 替代 STOP 信号、io.max 取代混乱的 throttling——每一项改进都直指 v1 的生产环境痛点。
对于平台工程师而言,掌握 cgroups v2 已不再是“可选技能”,而是理解容器运行时、构建可观测性栈、保障 SLA 的基石能力。当你的 Pod 因 OOM 被 Kill、你的批次任务拖慢在线服务、你的 SSD 被某个日志线程打满时,cgroups v2 就是你手中最精确的手术刀。

发表评论 取消回复