当你在一台服务器上运行多个 Docker 容器时,是否遇到过某个容器突然占满 CPU 导致其他服务卡顿?或者内存泄漏的容器把宿主机内存耗尽,触发 OOM Killer?默认情况下,Docker 容器不设资源上限,它们可以尽情使用宿主机的 CPU、内存和磁盘 I/O。这种“无拘无束”的设计虽然简化了启动,却为生产环境埋下隐患。本文将从这些实际问题出发,带你掌握 Docker 容器资源限制的设置方法。
为什么必须设置资源限制
Docker 容器共享宿主机内核,默认没有资源配额。一个失控的容器可能拖垮整个宿主机,影响同机其他容器。例如,未限制内存的容器发生内存泄漏时,宿主机内核会触发 OOM Killer,随机杀死进程,可能导致关键服务中断。CPU 限制不足则会让计算密集型容器抢占所有核心,使其他应用响应变慢。磁盘 I/O 不受限时,频繁读写的容器会阻塞其他容器的存储访问。因此,合理设置资源限制是 Docker 运维的基本功。
内存限制:防止 OOM 的关键
设置内存限制最直接的方式是使用 docker run -m 或 --memory 参数。例如:
docker run -m 512m --memory-swap 1g nginx
这里 -m 512m 限制容器最多使用 512MB 内存,--memory-swap 1g 表示内存加交换分区总共 1GB,即交换分区可用 512MB。如果不设置 --memory-swap,默认与内存限制相同,容器无法使用交换分区。注意,--memory-swap 只有在设置了 -m 时才有效。
另一个重要参数是 --oom-kill-disable,它可以在容器内存超限时禁止内核杀死容器进程,但仅在设置了 -m 时生效。不过,禁用 OOM Killer 可能导致容器长时间卡死,需谨慎使用。
CPU 限制:按需分配核心
CPU 限制有两种方式:相对权重和绝对配额。
相对权重使用 -c 或 --cpu-shares,默认值为 1024。例如,容器 A 设置 512,容器 B 设置 1024,则 B 获得的 CPU 时间是 A 的两倍。但这只在 CPU 繁忙时生效,空闲时容器仍可自由使用。
绝对配额使用 --cpus 或 --cpu-period/--cpu-quota。例如:
docker run --cpus=1.5 myapp
这限制容器最多使用 1.5 个 CPU 核心。底层实现是通过 CFS 调度器,--cpu-period 默认 100ms,--cpu-quota 表示在该周期内允许的 CPU 时间。设置 --cpus 1.5 相当于 quota 为 150ms。这种方式更精确,适合需要保证性能的场景。
磁盘 I/O 限制:避免存储争抢
磁盘 I/O 限制相对复杂,Docker 提供了 --device-read-bps、--device-write-bps、--device-read-iops 和 --device-write-iops 参数。它们针对特定设备设置读写速率上限。例如:
docker run --device-read-bps /dev/sda:1mb --device-write-bps /dev/sda:1mb myapp
这限制容器对 /dev/sda 的读写速度不超过 1MB/s。需要注意,这些参数依赖宿主机内核支持 blkio cgroup,且必须指定块设备路径。对于使用卷的容器,限制的是卷所在设备的 I/O。
使用 docker-compose 统一配置
在 docker-compose.yml 中,可以通过 deploy.resources.limits 配置资源限制:
services:
app:
image: nginx
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
注意,deploy 字段在 Swarm 模式下才生效,使用 docker-compose up 时会被忽略。若需在普通模式生效,应使用 docker-compose 支持的 cpus 和 mem_limit 字段(旧版本)。
常见误区与失败条件
误区一:只设置内存限制,不设置交换分区,导致容器在内存不足时直接 OOM。正确做法是合理设置 --memory-swap。
误区二:认为 --cpu-shares 能限制 CPU 使用率。它只是权重,不是上限,单容器时无法限制。
误区三:磁盘 I/O 限制不生效。原因通常是未指定正确的设备路径,或内核未开启 blkio cgroup。可通过 lsblk 查看设备名。
失败条件还包括:内核版本过低(Docker 资源限制依赖 cgroup v2,需内核 4.15+);使用 Docker Desktop 时部分限制行为与 Linux 不同;在 Kubernetes 中运行时,Docker 的资源限制会被 Pod 的 limit 覆盖,需在 Pod 级别设置。
验证资源限制是否生效
启动容器后,可使用 docker stats 实时查看 CPU、内存使用情况。也可进入容器内执行 cat /sys/fs/cgroup/memory.max(cgroup v2)查看限制值。若限制未生效,检查 Docker 版本和宿主机内核。
参考资料
- Docker 官方入门文档
- Kubernetes 概念文档(了解容器编排中的资源管理)
延伸阅读
