Cgroup 是什么?
一、基本概念
Cgroup(Control Groups)是 Linux 内核的一个功能,用于限制、记录和隔离进程组的资源使用。
简单理解:
- Cgroup = Control Groups = 控制组
- 作用:限制和管理进程的资源使用
- 位置:Linux 内核功能,通过 /sys/fs/cgroup 文件系统访问
二、Cgroup 的作用
Cgroup 可以限制和管理:
| 资源类型 | 可以限制什么 | 实际例子 |
|---|---|---|
| CPU | CPU 使用率、CPU 时间片 | 限制容器最多使用 50% CPU |
| Memory | 内存使用量、Swap | 限制容器最多使用 512MB 内存 |
| Block IO | 磁盘读写速度 | 限制容器磁盘 IO 为 10MB/s |
| Network | 网络带宽 | 限制容器网络带宽为 100Mbps |
| Process | 进程数量 | 限制容器最多 100 个进程 |
| Device | 设备访问权限 | 允许/禁止访问特定设备 |
三、Cgroup 的工作原理
1. 文件系统接口
Cgroup 通过虚拟文件系统暴露:
/sys/fs/cgroup/
├── cpu/ # CPU 控制器
│ ├── docker/ # Docker 容器的 cgroup
│ └── ...
├── memory/ # 内存控制器
│ ├── docker/
│ └── ...
├── blkio/ # 块 IO 控制器
│ └── ...
└── pids/ # 进程数控制器
└── ...
2. 层次结构
Cgroup 是树形结构:
/sys/fs/cgroup/cpu/
├── docker/ # Docker 的父 cgroup
│ ├── <container-id-1>/ # 容器 1 的 cgroup
│ │ ├── cpu.cfs_quota_us # CPU 配额
│ │ └── tasks # 属于这个组的进程
│ └── <container-id-2>/ # 容器 2 的 cgroup
└── system.slice/ # systemd 服务的 cgroup

root@LAPTOP-LU4HDAFV:/sys/fs/cgroup# tree -L 1
.
├── cgroup.controllers
├── cgroup.max.depth
├── cgroup.max.descendants
├── cgroup.pressure
├── cgroup.procs
├── cgroup.stat
├── cgroup.subtree_control
├── cgroup.threads
├── cpu.pressure
├── cpu.stat
├── cpu.stat.local
├── cpuset.cpus.effective
├── cpuset.mems.effective
├── dev-hugepages.mount
├── dev-mqueue.mount
├── init.scope
├── io.pressure
├── io.stat
├── memory.numa_stat
├── memory.pressure
├── memory.reclaim
├── memory.stat
├── sys-fs-fuse-connections.mount
├── sys-kernel-config.mount
├── sys-kernel-debug.mount
├── sys-kernel-tracing.mount
├── system.slice
└── user.slice
10 directories, 19 files
root@LAPTOP-LU4HDAFV:/sys/fs/cgroup#
=== cgroup.controllers 文件说明 ===
这个文件列出了系统可用的 Cgroup 资源控制器
=== 各个控制器的含义 ===
1. cpuset - CPU 核心和内存节点分配
2. cpu - CPU 使用限制和调度
3. io - 块设备 IO 限制
4. memory - 内存使用限制
5. hugetlb - 大页内存管理
6. pids - 进程数量限制
7. rdma - RDMA 资源限制
二、各分类详细说明
1. Cgroup 核心文件(8 个)- 管理 Cgroup 本身
作用:管理 Cgroup 系统本身,不直接限制资源
类比:公司总部的组织架构管理
| 文件 | 作用 | 类比 |
|---|---|---|
| cgroup.controllers | 列出有哪些控制器可用 | 公司有哪些部门 |
| cgroup.procs | 记录哪些进程在这个组 | 哪些员工在这个部门 |
| cgroup.subtree_control | 控制子组能用哪些控制器 | 子部门能用哪些工具 |
| cgroup.max.depth | 限制 Cgroup 树的深度 | 部门层级最多几层 |
| cgroup.stat | Cgroup 统计信息 | 部门统计报表 |
关键点:这些文件管理"谁在哪个组",不直接限制资源。
2. CPU 控制器文件(3 个)- 控制 CPU 资源
作用:限制和管理 CPU 使用
类比:CPU 部门的规则和统计
| 文件 | 作用 | 实际内容 |
|---|---|---|
| cpu.stat | 记录 CPU 用了多少时间 | usage_usec 5624888000(用了 5624 秒) |
| cpu.pressure | 告诉你 CPU 是否紧张 | CPU 压力信息 |
| cpu.stat.local | 本地 CPU 统计 | 本地 CPU 使用情况 |
关键点:这些文件限制和统计 CPU 使用,类似"电表"。
3. CPUset 控制器文件(2 个)- 控制 CPU 核心分配
作用:决定进程能用哪些 CPU 核心
类比:分配座位
| 文件 | 作用 | 实际内容 |
|---|---|---|
| cpuset.cpus.effective | 实际能用的 CPU 核心 | 0-11(能用所有 12 个核心) |
| cpuset.mems.effective | 实际能用的内存节点 | 内存节点编号 |
关键点:这些文件限制"能用哪些 CPU 核心",不是限制"用多少 CPU"。
4. IO 控制器文件(2 个)- 控制磁盘 IO
作用:限制和管理磁盘读写
类比:限速标志和流量统计
| 文件 | 作用 | 实际内容 |
|---|---|---|
| io.stat | 记录磁盘读写了多少 | rbytes=2492019712(读了 2.3GB) |
| io.pressure | 告诉你磁盘是否繁忙 | IO 压力信息 |
关键点:这些文件限制和统计磁盘 IO,类似"限速标志"。
5. 内存控制器文件(4 个)- 控制内存资源
作用:限制和管理内存使用
类比:水表和限流阀
| 文件 | 作用 | 实际内容 |
|---|---|---|
| memory.stat | 记录内存用了多少 | anon 1314009088(匿名页用了 1.2GB) |
| memory.pressure | 告诉你内存是否紧张 | 内存压力信息 |
| memory.reclaim | 可以手动回收内存 | 内存回收接口 |
| memory.numa_stat | NUMA 内存统计 | NUMA 节点内存使用 |
关键点:这些文件限制和统计内存使用,类似"水表"。
6. Cgroup 组目录(9 个)- 实际的 Cgroup 组
作用:实际的 Cgroup 组,包含进程和资源限制
类比:实际的部门
| 目录 | 类型 | 包含什么 |
|---|---|---|
| system.slice/ | slice | 所有 systemd 系统服务 |
| docker-<id>.scope/ | scope | Docker 容器的进程 |
| init.scope/ | scope | init 进程(PID 1) |
| *.mount/ | mount | 各种挂载点的进程 |
关键点:
- 目录 = 实际的组,里面有进程
- 目录里也有控制文件(cpu.max、memory.current 等)
- 这些文件控制目录里的进程
三、关键区别
文件 vs 目录
| 类型 | 是什么 | 作用 | 例子 |
|---|---|---|---|
| 文件 | 工具/规则 | 设置限制、查看统计 | cpu.max, memory.current |
| 目录 | 部门/组织 | 包含进程和资源限制 | system.slice/, docker-<id>.scope/ |
根目录 vs 子目录
/sys/fs/cgroup/ # 根目录
├── cpu.stat # 整个系统的 CPU 统计
├── memory.stat # 整个系统的内存统计
│
└── system.slice/ # 子目录
├── cpu.stat # system.slice 的 CPU 统计
├── memory.stat # system.slice 的内存统计
│
└── docker-<id>.scope/ # 子子目录
├── cpu.stat # 容器的 CPU 统计
└── memory.stat # 容器的内存统计
四、实际例子
例子 1:查看整个系统的 CPU 使用
cat /sys/fs/cgroup/cpu.stat
输出: usage_usec 5624888000 (整个系统用了 5624 秒 CPU)
例子 2:查看容器的 CPU 使用
cat /sys/fs/cgroup/system.slice/docker-<id>.scope/cpu.stat
输出: usage_usec 134142 (这个容器用了 0.13 秒 CPU)
例子 3:限制容器的 CPU
写入限制到容器的 cpu.max 文件
echo "50000 100000" > /sys/fs/cgroup/system.slice/docker-<id>.scope/cpu.max
cpu.max 文件格式
格式:<quota> <period>
| 值 | 含义 | CPU 使用率 |
|---|---|---|
| max 100000 | 无限制 | 100% |
| 100000 100000 | 限制 100% | 100% |
| 50000 100000 | 限制 50% | 50% |
| 25000 100000 | 限制 25% | 25% |
说明:
- period(周期):通常是 100000 微秒 = 100ms
- quota(配额):在这个周期内可以使用的时间(微秒)
- CPU 使用率 = quota / period
五、总结
各分类的含义:
- Cgroup 核心文件 = 管理员
- 管理"谁在哪个组",不直接限制资源
- CPU/内存/IO 控制器文件 = 资源管理员
- 限制和统计资源使用(CPU、内存、IO)
- Cgroup 组目录 = 实际的部门
- 包含进程和资源限制
- 目录里也有控制文件
关系:
- 文件 = 工具(用来设置限制)
- 目录 = 组织(包含进程和限制)
- 目录里的文件 = 控制目录里的进程
这就是这些分类的含义。
Cgroup 可以设置的所有资源限制
除了 CPU 使用时间,Cgroup 还可以设置以下限制:
一、所有可设置的资源限制
1. CPU 相关限制(4 种)
| 文件 | 作用 | 设置示例 |
|---|---|---|
| cpu.max | CPU 使用时间限制 | echo "50000 100000" > cpu.max (50% CPU) |
| cpu.weight | CPU 权重(公平调度) | echo "100" > cpu.weight (默认权重) |
| cpu.idle | CPU 空闲时间 | echo "0" > cpu.idle (禁用空闲) |
| cpu.max.burst | CPU 突发限制 | echo "10000" > cpu.max.burst |
2. 内存相关限制(5 种)
| 文件 | 作用 | 设置示例 |
|---|---|---|
| memory.max | 最大内存使用量 | echo "536870912" > memory.max (512MB) |
| memory.high | 内存警告阈值 | echo "268435456" > memory.high (256MB) |
| memory.low | 内存保护阈值 | echo "134217728" > memory.low (128MB) |
| memory.swap.max | Swap 限制 | echo "268435456" > memory.swap.max (256MB) |
| memory.min | 最小内存保证 | echo "67108864" > memory.min (64MB) |
3. IO 限制(1 种)
| 文件 | 作用 | 设置示例 |
|---|---|---|
| io.max | 磁盘读写速度限制 | echo "8:48 rbps=10485760 wbps=10485760" > io.max (10MB/s) |
4. 进程数限制(1 种)
| 文件 | 作用 | 设置示例 |
|---|---|---|
| pids.max | 最大进程数 | echo "100" > pids.max |
5. CPU 核心分配(2 种)
| 文件 | 作用 | 设置示例 |
|---|---|---|
| cpuset.cpus | 指定可用的 CPU 核心 | echo "0-3" > cpuset.cpus (只用 CPU 0-3) |
| cpuset.mems | 指定可用的内存节点 | echo "0" > cpuset.mems (只用内存节点 0) |
二、实际设置示例
示例 1:限制 CPU 为 50%
echo "50000 100000" > /sys/fs/cgroup/system.slice/docker-<id>.scope/cpu.max
含义: 限制使用 50% CPU
示例 2:限制内存为 512MB
echo "536870912" > /sys/fs/cgroup/system.slice/docker-<id>.scope/memory.max
536870912 = 512 * 1024 * 1024 (字节)
示例 3:限制进程数为 100
echo "100" > /sys/fs/cgroup/system.slice/docker-<id>.scope/pids.max
示例 4:限制只能使用 CPU 0-3
echo "0-3" > /sys/fs/cgroup/system.slice/docker-<id>.scope/cpuset.cpus
示例 5:限制磁盘读写速度为 10MB/s
先找到设备号
lsblk
假设设备是 8:48
echo "8:48 rbps=10485760 wbps=10485760" > /sys/fs/cgroup/system.slice/docker-<id>.scope/io.max
10485760 = 10 * 1024 * 1024 (10MB/s)
示例 6:设置内存警告阈值
echo "268435456" > /sys/fs/cgroup/system.slice/docker-<id>.scope/memory.high
256MB,超过这个值会触发警告但不会杀死进程
示例 7:设置 CPU 权重
echo "200" > /sys/fs/cgroup/system.slice/docker-<id>.scope/cpu.weight
权重越高,CPU 分配越多(默认 100)
三、Docker 命令对应的 Cgroup 设置
| Docker 命令 | 对应的 Cgroup 文件 | 设置的值 |
|---|---|---|
| --cpus="0.5" | cpu.max | 50000 100000 |
| --memory="512m" | memory.max | 536870912 |
| --memory-swap="1g" | memory.swap.max | 1073741824 |
| --pids-limit=100 | pids.max | 100 |
| --cpuset-cpus="0-3" | cpuset.cpus | 0-3 |
| --device-read-bps="/dev/sda:10mb" | io.max | 8:48 rbps=10485760 |
| --device-write-bps="/dev/sda:10mb" | io.max | 8:48 wbps=10485760 |
四、完整的资源限制列表
Cgroup 可以设置的资源限制:
- CPU 限制
✓ cpu.max - CPU 使用时间(主要限制)
✓ cpu.weight - CPU 权重(公平调度)
✓ cpu.idle - CPU 空闲时间
✓ cpu.max.burst - CPU 突发限制
- 内存限制
✓ memory.max - 最大内存(主要限制)
✓ memory.high - 内存警告阈值
✓ memory.low - 内存保护阈值
✓ memory.min - 最小内存保证
✓ memory.swap.max - Swap 限制
- IO 限制
✓ io.max - 磁盘读写速度(主要限制)
- 进程数限制
✓ pids.max - 最大进程数(主要限制)
- CPU 核心分配
✓ cpuset.cpus - 可用的 CPU 核心
✓ cpuset.mems - 可用的内存节点
五、总结
除了设置 CPU 使用时间(cpu.max),Cgroup 还可以设置:
- CPU 权重(cpu.weight)- 公平调度
- 内存限制(memory.max)- 最大内存
- 内存阈值(memory.high/low)- 警告和保护
- Swap 限制(memory.swap.max)- Swap 使用
- IO 限制(io.max)- 磁盘读写速度
- 进程数限制(pids.max)- 最大进程数
- CPU 核心分配(cpuset.cpus)- 指定可用 CPU
- 内存节点分配(cpuset.mems)- 指定可用内存节点
这些限制共同控制容器的资源使用。