6月6日 20:29

Docker 怎么限制容器资源?CPU、内存和磁盘 IO 配置

不限制容器资源,一个失控的容器就能吃光宿主机内存,拖垮同一台机器上的所有服务。Docker 提供了 CPU、内存、磁盘 IO 的精细限制手段,核心是 docker run 的资源参数和 docker-compose 的 deploy.resources 配置。

内存限制:最常用也最重要

bash
# 限制最大内存 4GB docker run -d --name myapp --memory=4g myapp:latest # 限制内存 + 禁用 swap docker run -d --name myapp --memory=4g --memory-swap=4g myapp:latest

--memory-swap=4g 等于 --memory 的值意味着容器不能用 swap。如果 --memory-swap--memory 大,差值就是允许的 swap 大小。

docker-compose 配置

yaml
services: app: image: myapp:latest deploy: resources: limits: memory: 4G # 硬上限,超过会被 OOM Kill reservations: memory: 1G # 软保底,调度时保证至少 1G

OOM 时会发生什么

容器内存超过 limits.memory 时,内核 OOM Killer 杀掉容器里内存占用最大的进程:

bash
# 检查容器是否被 OOM 杀掉 docker inspect myapp --format '{{.State.OOMKilled}}' # true = 被 OOM 杀了 # 查看 OOM 事件 dmesg | grep -i oom

OOM 优先级调整

多个容器抢内存时,可以设 OOM 优先级:

bash
# 不容易被 OOM Kill(-1000 到 1000,越小越不容易被杀) docker run -d --name important-app --oom-score-adj=-500 myapp # 容易被 OOM Kill(优先杀这个) docker run -d --name cache-app --oom-score-adj=500 redis

核心服务(数据库、API 网关)设低值,缓存类服务(Redis、CDN)设高值。

CPU 限制

限制 CPU 核数

bash
# 最多用 2 核 docker run -d --cpus=2.0 myapp # 最多用 0.5 核 docker run -d --cpus=0.5 myapp
yaml
services: app: deploy: resources: limits: cpus: '2.0' reservations: cpus: '0.5'

--cpus=2.0 不是绑核——容器可以在任意 2 个核心上运行,只是总使用时间不超过 200%。绑核用 --cpuset-cpus

bash
# 只在第 0 和第 2 个核心上运行 docker run -d --cpuset-cpus=0,2 myapp # 只在第 1-3 个核心上运行 docker run -d --cpuset-cpus=1-3 myapp

绑核适合对 CPU 缓存一致性敏感的应用(如高性能计算),一般 Web 服务不需要。

CPU 权重

多个容器抢 CPU 时,按权重分配:

bash
# 默认权重 1024 # 高权重 = 抢到更多 CPU 时间 docker run -d --cpu-shares=2048 high-priority-app docker run -d --cpu-shares=512 low-priority-app

注意:--cpu-shares 只在 CPU 资源紧张时生效。CPU 空闲时低权重容器也能用满 CPU。

磁盘 IO 限制

限制容器读写磁盘的速率,防止一个容器把磁盘 IO 吃光:

bash
# 限制写速率 10MB/s docker run -d \ --device-write-bps /dev/sda:10mb \ myapp # 限制读速率 20MB/s docker run -d \ --device-read-bps /dev/sda:20mb \ myapp # 限制 IOPS(每秒 IO 操作数) docker run -d \ --device-write-iops /dev/sda:1000 \ myapp

磁盘 IO 限制在 docker-compose 里不支持——需要 docker run 方式启动。

PIDs 限制:防止进程炸弹

限制容器内的进程数,防止 fork 炸弹:

bash
# 最多 100 个进程 docker run -d --pids-limit=100 myapp
yaml
services: app: deploy: resources: limits: pids: 100

没有这个限制,一个容器可以 fork 出几千个进程,耗尽宿主机的 PID 表。

运行时修改资源限制

不需要重建容器就能调整限制:

bash
# 动态调整内存限制 docker update --memory=8g myapp # 动态调整 CPU 限制 docker update --cpus=4.0 myapp # 同时调整多个 docker update --memory=8g --cpus=4.0 myapp

注意:docker update 不能修改 --pids-limit--cpuset-cpus——这些需要重建容器。

资源限制最佳实践

容器类型内存CPU其他
Web API2-4G1-2 核pids-limit: 200
数据库4-16G2-4 核绑核、禁 swap
缓存 Redis2-8G1-2 核禁 swap
日志采集512M-1G0.5 核限制磁盘 IO
后台任务1-2G0.5-1 核pids-limit: 50

原则

  • 所有生产容器都设内存限制,防止单个容器拖垮宿主机
  • 数据库容器禁 swap(--memory-swap 等于 --memory
  • 核心服务设低 OOM 优先级
  • 不确定资源需求时先设宽松限制,用 docker stats 观察实际用量再收紧
标签:Docker