Docker / 命令文档

Docker 资源限制(面试向)

$ docker 7 个章节 Linux 命令详解

概述

本篇是「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 内

同分类 · 其他命令文档