限制 CPU 和内存
面试题
Docker 如何限制容器的 CPU 和内存使用?参数怎么配置?底层原理是什么?
标准答案
Docker 通过 Linux 内核的 cgroups(Control Groups)机制实现资源限制。合理设置资源限制可以避免某个容器耗尽宿主机资源,是生产环境部署的基本要求。
内存限制
基本参数
# 硬限制:最大内存 512MB
docker run -d --memory=512m --name app myapp
# 软限制:尽量不超过 256MB(宿主机内存不足时才严格限制)
docker run -d --memory=512m --memory-reservation=256m myapp
# 限制内存 + Swap 总量
docker run -d --memory=512m --memory-swap=1g myapp
# memory-swap = memory + swap 之和
# 不设置时默认 memory-swap = memory * 2
# 设置 -1 表示 swap 不限
# 禁用 Swap(完全不使用交换分区)
docker run -d --memory=512m --memory-swap=512m myapp
# 限制内存常驻区域(RSS)
docker run -d --memory=512m --kernel-memory=128m myapp
关键参数说明
| 参数 | 作用 | 默认值 |
|---|---|---|
-m / --memory |
内存硬上限 | 无限制 |
--memory-reservation |
内存软限制 | 同硬限制 |
--memory-swap |
内存+Swap 总量 | 等于 2×memory |
--memory-swappiness |
Swap 倾向(0-100) | 继承宿主机 |
--kernel-memory |
内核内存上限 | 无限制 |
--oom-kill-disable |
禁止 OOM Kill | 不禁止 |
OOM(Out Of Memory)行为
# 默认情况下,内存超限会被 OOM Killer 杀掉
docker run -m 100m alpine python -c "x = ' ' * 1024**3"
# 容器会被 OOM Kill,退出码 137
# 禁止 OOM Kill(不推荐,可能导致宿主机 OOM)
docker run -m 100m --oom-kill-disable alpine stress --vm 1 --vm-bytes 200M
# OOM 优先级调整
docker run -m 100m --oom-score-adj=-500 myapp
# oom_score_adj 范围 -1000 到 1000
# 值越低,越不容易被 OOM Kill
CPU 限制
基本参数
# 限制 CPU 核数
docker run -d --cpus=1.5 myapp # 使用 1.5 个 CPU 核心
docker run -d --cpus="2.0" myapp # 使用 2 个 CPU 核心
# 绑定到特定 CPU 核心
docker run -d --cpuset-cpus=0,1 myapp # 只能在 CPU 0 和 1 上运行
docker run -d --cpuset-cpus=0-3 myapp # CPU 0-3
# CPU 份额(相对权重)
docker run -d --cpu-shares=1024 service-a # 默认权重
docker run -d --cpu-shares=512 service-b # 低优先级
# CPU 充足时权重无效;CPU 争抢时按权重比例分配
# service-a : service-b = 1024 : 512 = 2:1
CFS(Completely Fair Scheduler)参数
# CFS 周期和配额(底层参数,一般用 --cpus 即可)
# --cpus = --cpu-quota / --cpu-period
# 每 100ms 周期中最多使用 50ms CPU
docker run -d --cpu-period=100000 --cpu-quota=50000 myapp
# 相当于限制为 0.5 核
参数对照表
| 参数 | 作用 | 等价 CPU 限制 |
|---|---|---|
--cpus=2 |
使用 2 个核 | CPU 时间 = 2核 × 100% |
--cpus=0.5 |
使用半个核 | CPU 时间 = 0.5核 × 100% |
--cpuset-cpus=0 |
绑定到核 0 | 单核专用 |
--cpu-shares=2048 |
其他容器 1024 | 竞争时多获得 2 倍 |
组合限制示例
# 生产环境 Web 服务
docker run -d \
--name web \
--memory=512m \
--memory-reservation=256m \
--memory-swap=768m \
--cpus=1.5 \
--cpuset-cpus=0,1 \
--restart always \
nginx:alpine
# 批量任务 Worker(放宽 CPU,限制内存)
docker run -d \
--name worker \
--memory=2g \
--memory-swap=2g \
--cpus=4 \
my-worker:latest
# 低优先级批处理
docker run -d \
--name batch-job \
--memory=1g \
--cpu-shares=256 \
--cpuset-cpus=2,3 \
batch-processor:latest
底层原理:cgroups
# 查看容器的 cgroup 配置
# 在宿主机上
ls -l /sys/fs/cgroup/memory/docker/ # 容器 ID 对应的目录
cat /sys/fs/cgroup/memory/docker/<容器ID>/memory.limit_in_bytes
# 查看容器的 CPU cgroup
cat /sys/fs/cgroup/cpu/docker/<容器ID>/cpu.cfs_quota_us
cat /sys/fs/cgroup/cpu/docker/<容器ID>/cpu.cfs_period_us
最佳实践
1. 始终设置内存上限
# 不设限制的容器可能撑爆宿主机
# 跑内存密集任务时尤其重要
2. memory-reservation 设置软限制
# 正常情况下容器可使用多于软限制的内存
# 仅在宿主机内存紧张时才回收
# 防止单个容器导致整体 OOM
3. 合理设置 CPU 份额而非核数
# 对于非关键服务,用低 cpu-shares 而非固定 --cpus
# 充分利用空闲 CPU 资源,竞争时自动降级
4. 不要轻易禁用 OOM Kill
# oom-kill-disable 可能导致宿主机 OOM
# 只在极特殊场景使用(如数据库)
常见问题
Q:内存限制对 Java 应用为什么需要特殊处理?
# Java 感知不到 Docker 的 cgroup 内存限制
# 需要显式设置 JVM 堆大小
docker run -m 512m -e JAVA_OPTS="-Xmx256m -Xms128m" myapp
# Java 10+ 已支持容器感知(UseContainerSupport)
Q:–cpus=1 和 –cpuset-cpus=0 的区别是什么?
--cpus=1 限制 CPU 时间总量,但调度可以跨任意核;--cpuset-cpus=0 绑定到特定核,独占该核的时间片。
Q:如何验证限制是否生效?
# 启动受限容器
docker run -d --memory=128m --name test alpine sleep 3600
# 查看限制配置
docker inspect test | jq '.[0].HostConfig.Memory'
# 压力测试
docker exec test stress --vm 1 --vm-bytes 256M
# 容器会因 OOM 被杀掉(退出码 137)
总结
核心配置思路:内存必设上限(-m)+ 合理设置 CPU 限制(--cpus 或 --cpu-shares)。理解 cgroups 是底层机制即可,不必深究内核细节。生产环境建议所有容器都设置资源限制,避免”坏邻居”问题。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容