4.4.2 cgroups原理与实践

cgroups原理与实践#

核心概念与架构#

cgroups(Control Groups)对进程组资源进行限制、统计与隔离,核心由控制器(controller)、层级(hierarchy)与进程(task/proc)组成。Linux 有 cgroups v1 与 v2 两种实现:v1 为多控制器多层级,v2 为统一层级与统一接口。

文章图片

版本与挂载检查#

# 查看内核是否启用cgroups v2
stat -fc %T /sys/fs/cgroup
# 期望输出:cgroup2fs 表示 v2;tmpfs 表示 v1或混合

# 查看挂载点与控制器
mount | grep cgroup

# v1 控制器列表
ls /sys/fs/cgroup/

# v2 可用控制器
cat /sys/fs/cgroup/cgroup.controllers

关键能力#

  • 资源限制:CPU 配额、内存上限、磁盘 IO 权重与限速、进程数限制
  • 资源统计:CPU 使用、内存占用、IO 统计
  • 隔离与优先级:避免单进程抢占系统资源
  • 与容器集成:Docker/Kubernetes 基于 cgroups 实现资源隔离

cgroups v1 实践要点(CPU+内存)#

以下示例演示对一个测试进程限制 CPU 50% 与内存 512M。

1)准备测试进程#

# 启动一个持续消耗CPU的进程
yes > /dev/null &
echo $! > /tmp/cgroup_test.pid
cat /tmp/cgroup_test.pid

2)创建 cgroup 并配置限制#

# 创建CPU与内存控制组
mkdir -p /sys/fs/cgroup/cpu/testcpu
mkdir -p /sys/fs/cgroup/memory/testmem

# 100ms 周期内最多使用 50ms CPU 时间 => 50%
echo 100000 > /sys/fs/cgroup/cpu/testcpu/cpu.cfs_period_us
echo 50000  > /sys/fs/cgroup/cpu/testcpu/cpu.cfs_quota_us

# 内存上限 512MB
echo 536870912 > /sys/fs/cgroup/memory/testmem/memory.limit_in_bytes

3)将进程加入 cgroup#

PID=$(cat /tmp/cgroup_test.pid)
echo $PID > /sys/fs/cgroup/cpu/testcpu/tasks
echo $PID > /sys/fs/cgroup/memory/testmem/tasks

4)验证效果#

# CPU限制生效:观察进程的CPU占用低于单核100%
top -p $(cat /tmp/cgroup_test.pid)

# 内存限制生效:观察上限与使用统计
cat /sys/fs/cgroup/memory/testmem/memory.usage_in_bytes
cat /sys/fs/cgroup/memory/testmem/memory.limit_in_bytes

cgroups v2 实践要点(统一层级)#

统一挂载点通常为 /sys/fs/cgroup/,需显式启用控制器。

1)启用控制器并创建子组#

# 在根cgroup启用控制器
echo "+cpu +memory +pids" > /sys/fs/cgroup/cgroup.subtree_control

# 创建子组
mkdir -p /sys/fs/cgroup/app

2)设置资源限制并加入进程#

# 50% CPU
echo "50000 100000" > /sys/fs/cgroup/app/cpu.max

# 内存上限 512M
echo "512M" > /sys/fs/cgroup/app/memory.max

# 进程数限制 256
echo 256 > /sys/fs/cgroup/app/pids.max

# 将测试进程加入
PID=$(cat /tmp/cgroup_test.pid)
echo $PID > /sys/fs/cgroup/app/cgroup.procs

3)查看统计#

cat /sys/fs/cgroup/app/cpu.stat
cat /sys/fs/cgroup/app/memory.current
cat /sys/fs/cgroup/app/pids.current

与 systemd 的结合(服务资源管理)#

systemd 将服务映射为 cgroup,适合生产环境。

1)设置限制(即刻生效)#

# 对 nginx 服务限制 CPU 60%,内存 1G,最大任务数 256
systemctl set-property nginx CPUQuota=60% MemoryMax=1G TasksMax=256
systemctl show nginx -p CPUQuota -p MemoryMax -p TasksMax

2)写入服务配置(永久生效)#

路径示例:/etc/systemd/system/nginx.service.d/limit.conf

[Service]
CPUQuota=60%
MemoryMax=1G
TasksMax=256
systemctl daemon-reload
systemctl restart nginx

排错与注意事项#

1)限制不生效#

# 确认版本与挂载类型
stat -fc %T /sys/fs/cgroup

# v2 必须启用控制器
cat /sys/fs/cgroup/cgroup.subtree_control

说明:v1/v2 混用会导致参数写入却无效,需确认实际控制器路径。

2)内存限制触发 OOM#

# 观察内存事件
cat /sys/fs/cgroup/app/memory.events
dmesg | tail -n 20

说明memory.eventsoom/oom_kill 计数增长说明限制过低。

3)CPU 配额过低导致延迟#

# 查看CPU统计与节流情况
cat /sys/fs/cgroup/app/cpu.stat

说明nr_throttled 持续增长说明被频繁节流。

典型场景#

  • 限制批处理任务资源,避免影响线上服务
  • 对备份/日志分析设置 IO/CPU 配额
  • 多租户或容器环境中隔离资源
  • 限制 fork/线程数防止系统资源耗尽

练习#

  1. 使用 cgroups v2 创建 batch 组,限制 CPU 20%,内存 256M,并验证 cpu.stat 的节流次数。
  2. 对一个自写脚本服务(systemd unit)设置 MemoryMax=200M,观察触发 OOM 时日志变化。
  3. 比较 v1 cpu.cfs_quota_us 与 v2 cpu.max 的配置方式,记录差异与优缺点。