Docker 容器资源限制设置指南

Docker 容器默认无资源限制,容易导致宿主机资源耗尽。本文从实际问题切入,介绍如何通过 docker run 参数和 docker-compose 配置设置 CPU、内存和磁盘 I/O 限制,并说明常见误区。

Docker 容器资源限制设置指南
封面图:ZuCDN · ZuCDN 原创

当你在一台服务器上运行多个 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 支持的 cpusmem_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 版本和宿主机内核。

参考资料

延伸阅读