Linux cgroup v2是内核提供的资源隔离与限制机制,相比cgroup v1在架构上做了统一设计,将原本分散的子系统整合为统一的层级结构。在服务器运维和容器化部署场景中,cgroup v2用于精确控制进程组的CPU、内存、IO等资源配额,防止单个异常进程耗尽系统资源。本文从cgroup v2的架构变化讲起,结合实际配置案例说明各类资源限制的设置方法。
cgroup v2与v1架构差异对比
cgroup v1采用多层级结构,每个子系统(cpu、memory、blkio等)拥有独立的层级树。这种设计导致进程可以同时挂载在不同子系统的不同层级节点上,管理复杂且容易产生配置冲突。cgroup v2采用统一层级(Unified Hierarchy),所有资源控制器挂载在同一棵cgroup树上,进程只能存在于一个cgroup节点中,资源限制通过该节点的各控制文件统一管理。
内核4.5开始引入cgroup v2支持,5.10版本后功能趋于完善。查看当前系统使用的cgroup版本:
# 查看cgroup挂载信息
mount | grep cgroup
# cgroup v2的典型输出
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)
# 查看已启用的控制器
cat /sys/fs/cgroup/cgroup.controllers
# 输出示例: +cpu +memory +io +pids +rdma
如果系统仍使用cgroup v1,可在内核启动参数中添加systemd.unified_cgroup_hierarchy=1切换至v2。
CPU资源配额限制配置方法
cgroup v2的CPU控制通过cpu.weight和cpu.max两个文件实现。cpu.weight用于相对权重分配,取值范围1-10000,默认100。cpu.max用于绝对限制,格式为”$MAX $PERIOD”,表示在PERIOD微秒内最多使用MAX微秒CPU时间。
通过systemd管理服务时,在service单元文件中配置CPU限制:
# /etc/systemd/system/myapp.service
[Service]
ExecStart=/usr/bin/python3 /opt/myapp/server.py
# CPU权重,相对值,默认100
CPUWeight=500
# 硬限制:每100ms内最多使用50ms CPU时间(即50%单核)
CPUQuota=50%
# 也可直接操作cgroup文件
mkdir /sys/fs/cgroup/myapp
echo "500" > /sys/fs/cgroup/myapp/cpu.weight
echo "50000 100000" > /sys/fs/cgroup/myapp/cpu.max
# 将进程PID写入cgroup.procs
echo 12345 > /sys/fs/cgroup/myapp/cgroup.procs
在多核场景下,CPUQuota=200%表示允许使用2个完整核心。通过cpu.max设置的限制作用于cgroup下所有进程的总CPU时间,不会自动绑定到特定核心。如果需要CPU亲和性绑定,需配合taskset或cgroup的cpuset控制器(v2中仍在开发中,部分发行版通过v1兼容层提供)。
内存资源限制与OOM控制策略
cgroup v2将内存限制整合到memory.max和memory.high两个文件中。memory.max是硬限制,当cgroup内存使用达到该值时触发OOM Killer。memory.high是软限制,超过后内核会优先回收该cgroup的内存页,但不立即杀进程。
# 设置内存硬限制为2GB
echo "2147483648" > /sys/fs/cgroup/myapp/memory.max
# 设置内存软限制为1.5GB
echo "1610612736" > /sys/fs/cgroup/myapp/memory.high
# systemd方式配置
[Service]
MemoryMax=2G
MemoryHigh=1500M
MemorySwapMax=1G # 限制swap使用量
# 查看当前内存使用
cat /sys/fs/cgroup/myapp/memory.current
# 查看内存事件(OOM次数等)
cat /sys/fs/cgroup/myapp/memory.events
memory.events文件记录关键事件计数,包括low(低于memory.low)、high(超过memory.high)、max(超过memory.max触发OOM)、oom_kill(OOM杀死的进程数)等。生产环境建议监控oom_kill字段,持续增长表明内存配额设置过低。
cgroup v2新增了memory.oom.group功能。当设置echo 1 > memory.oom.group时,OOM Killer会杀死该cgroup下所有进程而非仅杀死占用内存最多的进程。适用于需要全部重启或全部保留的应用场景。
磁盘IO带宽限制配置
cgroup v2的IO控制通过io.max文件实现,支持对特定块设备设置读写带宽和IOPS限制。格式为”$MAJOR:$MINOR rbps=bytes wbps=bytes riops=iops wiops=iops”。
# 获取块设备主从设备号
lsblk -o NAME,MAJ:MIN
# 输出示例: sda 8:0
# 限制sda读取带宽为100MB/s,写入50MB/s
echo "8:0 rbps=104857600 wbps=52428800" > /sys/fs/cgroup/myapp/io.max
# 限制IOPS:读1000 IOPS,写500 IOPS
echo "8:0 riops=1000 wiops=500" > /sys/fs/cgroup/myapp/io.max
# systemd方式
[Service]
IOReadBandwidthMax=/dev/sda 100M
IOWriteBandwidthMax=/dev/sda 50M
IO限制仅对直接IO和缓冲写入生效。对于使用O_DIRECT标志的应用程序,IO限制效果精确。对于缓冲IO,由于内核page cache的存在,实际IO行为可能有延迟,限制效果存在一定滞后。
进程数量限制与fork炸弹防护
cgroup v2的pids控制器用于限制cgroup内的进程、线程和子进程总数。这是防护fork炸弹的有效手段。
# 限制最大进程数为500
echo "500" > /sys/fs/cgroup/myapp/pids.max
# 查看当前进程数
cat /sys/fs/cgroup/myapp/pids.current
# systemd方式
[Service]
TasksMax=500
在使用容器(Docker/Podman)时,pids.max默认值通常在4096左右。对于运行大量线程的Java应用,可能需要适当调高该限制,否则线程创建会失败并抛出Unable to create new native thread错误。
cgroup v2资源监控与告警集成
在生产环境中,需要持续监控各cgroup的资源使用情况。cgroup v2提供了丰富的统计文件:
# CPU使用统计
cat /sys/fs/cgroup/myapp/cpu.stat
# 输出: usage_usec user_usec system_usec nr_periods nr_throttled throttled_usec
# 内存使用明细
cat /sys/fs/cgroup/myapp/memory.stat
# 包含anon、file、slab、sock等各种内存分类统计
# IO使用统计
cat /sys/fs/cgroup/myapp/io.stat
# 输出: 8:0 rbytes=12345678 wbytes=87654321 rios=1234 wios=5678
结合Prometheus的cgroups_exporter可实现自动化监控。关键告警规则示例:
# PromQL告警规则
# 内存使用率超过90%
100 * myapp_memory_current / myapp_memory_max > 90
# CPU限流时间占比超过10%
rate(myapp_cpu_stat_throttled_usec[5m]) / rate(myapp_cpu_stat_usage_usec[5m]) > 0.1
当CPU throttled时间持续增长时,说明CPU配额设置过低,应用性能将出现明显下降。此时应增大CPUQuota或优化应用的CPU使用模式。内存监控中需注意anon(匿名内存)和file(文件缓存)的区分,file内存可以被内核回收,只有anon内存接近memory.max时才有OOM风险。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/linuxcgroupv2-zi-yuan-pei-e-kong-zhi-yu-jin-cheng-zi-yuan/