4. Cgroups 资源限制:CPU、内存、磁盘 IO 与 cgroups v2
在多租户容器环境中,单一宿主机上运行着来自不同租户的多个容器实例。如果没有资源限制,一个“吵闹的邻居”容器可能耗尽宿主机 CPU、内存或磁盘 IO,导致其他租户的服务降级甚至中断。Cgroups(Control Groups)是 Linux 内核提供的一项核心功能,它允许系统管理员对进程组所使用的物理资源(CPU、内存、磁盘 IO、网络等)进行精细化的限制、记录和隔离。
本章将深入讲解 Docker 如何利用 cgroups 实现 CPU、内存和磁盘 IO 的限制,并介绍新一代 cgroups v2 的关键变化与配置方法。
4.1 CPU 资源限制
Docker 通过 --cpus、--cpu-shares、--cpuset-cpus 等参数控制容器对 CPU 的访问。这些参数最终会写入容器对应的 cgroup 控制文件中。
| 参数 | 作用 | 底层 cgroup 文件 |
|---|---|---|
--cpus |
限制容器能使用的 CPU 核心数(如 1.5 表示 1.5 个核) | cpu.cfs_period_us 和 cpu.cfs_quota_us |
--cpu-shares |
设置 CPU 相对权重(默认 1024),仅在 CPU 竞争时生效 | cpu.shares |
--cpuset-cpus |
将容器绑定到指定的物理 CPU 核心(如 0-3) | cpuset.cpus |
Docker 的
--cpus 参数基于 CFS 的带宽控制。内核以 cfs_period_us(通常 100000 微秒 = 100ms)为一个周期,容器在每个周期内最多能运行 cfs_quota_us 微秒。例如 --cpus=2 意味着 quota = 2 * period = 200000 微秒。
# 限制容器最多使用 1.5 个 CPU 核心
docker run -d --name app-cpu --cpus="1.5" nginx
# 查看该容器对应的 cgroup 配置
cat /sys/fs/cgroup/cpu/docker/<container-id>/cpu.cfs_quota_us
# 输出: 150000
cat /sys/fs/cgroup/cpu/docker/<container-id>/cpu.cfs_period_us
# 输出: 100000
--cpuset-cpus 绑定专用核心,避免上下文切换带来的抖动。对于批处理任务,使用 --cpus 配合 --cpu-shares 实现弹性分配。
4.2 内存资源限制
内存限制是防止容器内存泄漏或突发占用导致宿主机 OOM(Out Of Memory)的关键防线。Docker 提供硬限制和软限制两种机制。
- 硬限制(
--memory/-m):容器使用的内存(包括物理内存和 swap)不得超过此值。超过时,内核会触发 OOM Killer 终止容器进程。 - 软限制(
--memory-reservation):当宿主机内存充足时,容器可以超过此值;当宿主机内存紧张时,内核会尝试将容器内存回收到此值以下。 - Swap 限制(
--memory-swap):控制容器可以使用的总内存+swap 大小。默认与--memory相同(即禁用 swap)。
# 限制容器最多使用 512MB 内存,且禁用 swap
docker run -d --name app-mem -m 512m --memory-swap 512m redis
# 查看内存限制
cat /sys/fs/cgroup/memory/docker/<container-id>/memory.limit_in_bytes
# 输出: 536870912
--oom-kill-disable 可以禁止内核杀死容器进程,但必须配合 --memory 使用。不推荐在生产环境随意禁用 OOM Killer,否则可能导致宿主机内核 panic。
4.3 磁盘 IO 资源限制
磁盘 IO 限制在传统 cgroups v1 中通过 blkio 子系统实现,支持两种模型:
| 模型 | 参数 | 说明 |
|---|---|---|
| 权重模型 | --blkio-weight |
相对权重(10-1000),仅在 IO 竞争时生效 |
| 限速模型 | --device-read-bps--device-write-bps--device-read-iops--device-write-iops |
按设备限制每秒字节数或 IO 次数 |
# 限制容器对 /dev/sda 的写入速度为 10MB/s
docker run -d --name app-io --device-write-bps /dev/sda:10mb nginx
# 查看对应的 blkio 限制
cat /sys/fs/cgroup/blkio/docker/<container-id>/blkio.throttle.write_bps_device
# 输出: 8:0 10485760 (8:0 是 /dev/sda 的主次设备号)
4.4 cgroups v2:统一层级与新一代资源控制
cgroups v2 是 Linux 内核 4.5 引入的重大重构,旨在解决 v1 中多个子系统层级不一致、管理混乱的问题。主流 Linux 发行版(如 Ubuntu 22.04+、Debian 12+、RHEL 9+)已默认使用 cgroups v2。Docker 从 20.10 版本开始全面支持 cgroups v2。
下图展示了 cgroups v1 与 v2 在架构上的核心差异:
4.5 cgroups v2 下的资源限制配置
在 cgroups v2 环境中,Docker 的配置参数基本保持不变,但底层文件路径和接口发生了变化。以下是在 cgroups v2 下配置资源限制的示例:
# 检查当前系统是否使用 cgroups v2
stat -fc %T /sys/fs/cgroup/
# 输出: cgroup2fs (表示 v2)
# 运行容器并限制资源
docker run -d --name app-v2 \
--cpus="2.0" \
--memory="1g" \
--memory-reservation="512m" \
--device-write-bps /dev/sda:50mb \
nginx
# 查看 cgroups v2 下的控制文件
cat /sys/fs/cgroup/system.slice/docker-<container-id>.scope/cpu.max
# 输出: 200000 100000 (quota period)
cat /sys/fs/cgroup/system.slice/docker-<container-id>.scope/memory.max
# 输出: 1073741824
cat /sys/fs/cgroup/system.slice/docker-<container-id>.scope/io.max
# 输出: 8:0 wbps=52428800
memory.limit_in_bytes变为memory.maxcpu.cfs_quota_us和cpu.cfs_period_us合并为cpu.maxblkio.throttle.write_bps_device变为io.max,且格式改为major:minor wbps=值- 不再支持
--blkio-weight,需使用io.weight接口
4.6 多租户场景下的资源隔离策略
在实际的多租户 Docker 环境中,建议采用以下组合策略来确保隔离效果:
| 资源类型 | 推荐策略 | 说明 |
|---|---|---|
| CPU | --cpus + --cpuset-cpus |
对关键租户绑定专用核心,其他租户使用 --cpus 限制上限 |
| 内存 | --memory + --memory-reservation |
硬限制防止 OOM,软限制提高内存利用率 |
| 磁盘 IO | --device-write-bps + --device-read-bps |
按设备限速,避免 IO 干扰 |
| PID | --pids-limit |
限制容器内进程数,防止 fork 炸弹 |
通过合理配置 cgroups 资源限制,运维团队可以在共享基础设施上为每个租户提供可预测的服务质量(QoS),同时最大化硬件利用率。cgroups v2 的引入进一步简化了管理模型,并为未来的资源感知调度(如基于 PSI 指标的自动扩缩容)奠定了基础。