Docker / 命令文档
Docker 资源限制(面试向)
概述
本篇是「Docker 面试核心」系列第六篇,讲资源限制:CPU 的 quota/share 两套机制、内存限制与 OOM 行为、Block IO 限速,以及用 inspect/stats 验证限制是否真正生效。资源题的核心是「Cgroups 落到了哪个内核文件」。
所有输出在 Docker 26.1.4 + cgroup v1 测试机实测。
CPU 限制:cpus 与 quota
--cpus 是 --cpu-quota/--cpu-period 的易用包装:cpus=0.5 即每 100ms 周期最多用 50ms CPU 时间,硬上限。
# 限制 0.5 核并跑满,观察实际 CPU 占用被压在 50%
$ docker run -d --name kb-cpu --cpus=0.5 busybox:latest sh -c 'while :; do :; done'
$ docker inspect -f "{{.HostConfig.NanoCpus}}" kb-cpu
500000000
$ docker stats --no-stream --format "{{.Name}} CPU={{.CPUPerc}}" kb-cpu
kb-cpu CPU=50.26%面试要点:
- 实现:CFS 带宽控制,quota/period 写进 cfs_quota_us/cfs_period_us;NanoCpus=500000000 即 0.5 核
--cpus是硬限(超了就节流),与核数无关只与时间片有关;--cpuset-cpus则是绑核(把进程钉在指定核心上)- 超限的表现:容器内应用变慢而非报错,监控上 throttle 次数上涨 —— 排查性能问题先看 cfs_stat
CPU share:cpu-shares 权重
--cpu-shares 不设上限,只定「争抢时的相对权重」,CPU 空闲时可用满。
$ docker run -d --name kb-s1 --cpu-shares 1024 busybox:latest sleep 300
$ docker run -d --name kb-s2 --cpu-shares 512 busybox:latest sleep 300
$ docker inspect -f "{{.Name}} CpuShares={{.HostConfig.CpuShares}}" kb-s1 kb-s2
/kb-s1 CpuShares=1024
/kb-s2 CpuShares=512面试要点:
- quota vs share 标准答案:quota 是硬上限(绝对值),share 是软权重(相对比例,仅在争抢时生效)
- 默认 shares=1024;s1:s2 = 1024:512 = 2:1,CPU 饱和时按 2/3 与 1/3 分配
- 生产组合拳:在线服务用 cpus 硬限保底线,批处理任务用低 shares 让路
Memory 限制与 OOM
-m/--memory 设内存硬顶;超限触发内核 OOM Killer 杀容器内进程,退出码 137(128+9)。
# 写 /dev/shm(tmpfs 内存不可回收)触发真实内存压力
$ docker run -d --name kb-oom -m 32m busybox:latest \
sh -c 'dd if=/dev/zero of=/dev/shm/fill bs=1M count=64; sleep 60'
$ docker inspect -f "{{.State.OOMKilled}} {{.State.ExitCode}}" kb-oom
true 137
$ docker logs kb-oom
Killed
# 一次性验证退出码
$ docker run --rm -m 32m busybox:latest dd if=/dev/zero of=/dev/shm/fill bs=1M count=64
$ echo $?
137面试要点:
-m 32m对应 cgroup memory.limit_in_bytes=33554432;stats 里 MEM USAGE/LIMIT 直接可见- 137 = 128+SIGKILL(9):先看
docker inspect .State.OOMKilled确认是 OOM 还是外部 kill - 常见坑:写 stdout/页缓存不算匿名内存(可回收不触发 OOM),tmpfs(/dev/shm)才是真内存;Java 容器需配
-XX:MaxRAMPercentage而非看宿主机内存 --memory-swap未设时默认是 -m 的两倍(swap 空间另计),严格不换页要设--memory-swap等于-m
Block IO 限制
权重(--blkio-weight)与按设备限速(--device-read-bps/--device-write-bps)两类,最终落到 blkio cgroup 的 throttle 文件。
$ docker run -d --name kb-bio --blkio-weight 500 \
--device-read-bps /dev/vda1:1mb busybox:latest sleep 300
$ docker inspect -f "{{.HostConfig.BlkioWeight}}" kb-bio
500
# 限速已下发到 cgroup(按设备生效)
/sys/fs/cgroup/blkio/docker/<容器ID>/blkio.throttle.read_bps_device面试要点:
- weight 是软比例(10-1000,默认 500),bps/iops 是按设备的硬限速;两者都依赖 blkio cgroup
- 限制的是「容器内对该设备的 IO」,写法为
--device-read-bps <设备路径>:<速率> - 注意:cgroup v2 下 Docker 改用 io controller,部分老参数行为有差异;磁盘本身是网络盘(云盘)时本地限速意义有限
用 inspect 和 stats 验证限制
限制配了不等于生效,运维要会自证:
# CPU:NanoCpus/Quota、内存:Memory、OOM 记录:State.OOMKilled
$ docker inspect -f "{{.HostConfig.NanoCpus}} {{.HostConfig.Memory}} {{.State.OOMKilled}}" <容器>
# 实时水位:stats 的 LIMIT 列就是 -m 的值
$ docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}"
NAME CPU % MEM USAGE / LIMIT MEM %
kb-st 0.00% 128KiB / 64MiB 0.20%面试要点:
- 排查「容器变慢」三板斧:stats 看是否顶到 LIMIT → inspect 看限制配置 → 节流统计看 CFS throttle
- 137 排查顺序:inspect OOMKilled → dmesg 里的 oom-killer 日志 → 复核 -m 与应用真实水位
- 宿主机层面用
systemd-cgtop/cat /sys/fs/cgroup/...直接读 cgroup 数据,与 docker stats 互相印证
高频面试题速答
| 问题 | 一句话答案 |
|---|---|
| cpus 与 cpu-shares 区别 | cpus/quota 是硬上限节流,shares 是争抢时的软权重,空闲时不限制 |
| 容器退出码 137 是什么 | 128+SIGKILL(9),通常是内存超限被 OOM Killer 杀掉,用 inspect 的 OOMKilled 确认 |
| 怎么让容器 OOM | 限制 -m 后写 /dev/shm(tmpfs 不可回收)制造真实内存压力即可复现 |
| --cpus=0.5 的实现原理 | CFS 带宽控制:每 100ms 周期只允许消耗 50ms CPU 时间,超时被节流 |
| 内存限制下 swap 怎么算 | 默认 memory-swap 为 -m 两倍(含 swap);设两者相等即禁止换页 |
| Block IO 怎么限 | --blkio-weight 软权重或 --device-read/write-bps 按设备硬限速,落 blkio cgroup |
| 容器内存超限一定被杀吗 | 超的是不可回收内存(匿名页/tmpfs)才 OOM;页缓存会被内核回收,通常不触发 |
| 如何验证限制生效 | inspect 抽 HostConfig 字段确认配置,stats 看实时水位是否被压在 LIMIT 内 |