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 配置
yamlservices: 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
yamlservices: 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
yamlservices: 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 API | 2-4G | 1-2 核 | pids-limit: 200 |
| 数据库 | 4-16G | 2-4 核 | 绑核、禁 swap |
| 缓存 Redis | 2-8G | 1-2 核 | 禁 swap |
| 日志采集 | 512M-1G | 0.5 核 | 限制磁盘 IO |
| 后台任务 | 1-2G | 0.5-1 核 | pids-limit: 50 |
原则:
- 所有生产容器都设内存限制,防止单个容器拖垮宿主机
- 数据库容器禁 swap(
--memory-swap等于--memory) - 核心服务设低 OOM 优先级
- 不确定资源需求时先设宽松限制,用
docker stats观察实际用量再收紧