讲解

默认情况下容器对宿主机的资源予取予求:一个内存泄漏的容器可以拖垮整台机器上的所有邻居(包括 Docker 守护进程自己)。生产容器必须设限。内存用 --memory(硬上限,超过即触发 OOM Kill,退出码 137)和 --memory-swap;CPU 用 --cpus(等效于几个核的配额,0.5 即半个核)或更底层的 --cpu-shares(相对权重,只在争抢时生效)。

OOM 是设限后最常见的事故形态:容器突然退出、docker ps -a 显示 Exited (137)、docker inspect 的 State.OOMKilled 为 true。处理路径:确认是应用真泄漏(看监控曲线持续爬升)还是限制给低了(Java 这类运行时自身开销大,JVM 要配 -Xmx 与容器限制联动,新版 JDK 能自动识别容器内存)。盲目调大限制掩盖泄漏,是饮鸩止渴。

除了 CPU/内存,还有几个实用限制:--pids-limit 限制进程数(防 fork 炸弹);--read-only 把容器根文件系统设为只读(配合 --tmpfs /tmp 给临时目录,安全性显著提升);--security-opt no-new-privileges 禁止提权。资源限制在 Compose 里的写法是 services.x.deploy.resources.limits(v2 语法,单机 compose 也认 mem_limit/cpus 简写)。

示例

内存限制实测:给 64m 上限跑一个吃内存的命令,容器被 OOM 杀掉(预期失败,退出码 137):

docker run --rm --memory 64m alpine:3.21 sh -c 'head -c 200m /dev/zero | tail > /dev/null' && rc=0 || rc=$?
echo "退出码: $rc (137 = 被 OOM Kill)"

CPU 限制:--cpus 0.5 的容器 stats 上限约为 50%(起个忙循环看配额生效):

docker run -d --name ohmydocs-verify-cpu --cpus 0.5 alpine:3.21 sh -c 'while true; do :; done'
sleep 2 && docker stats --no-stream --format '{{.Name}} CPU={{.CPUPerc}}' ohmydocs-verify-cpu

只读根文件系统 + tmpfs 临时目录:写根目录失败(安全),写 /tmp 成功(tmpfs 在内存):

docker run --rm --read-only --tmpfs /tmp:rw,size=16m alpine:3.21 sh -c 'touch /etc/x 2>/dev/null && echo "不该看到这行" || echo "根文件系统只读, 写入被拒"; echo ok > /tmp/note && cat /tmp/note'

清理忙循环容器:

docker rm -f ohmydocs-verify-cpu

常见坑

  • 不设内存上限:一个泄漏容器拖死全机;生产容器的 --memory 是底线配置。
  • JVM/Node 不认容器限制:老版本运行时按宿主机内存算堆,容器限 512m 它敢要 4G——升级运行时或显式配堆大小。
  • --cpus 理解成「独占核」:它是时间片配额不是绑核;争抢激烈的场景还要配 cpu-shares 或绑核(--cpuset-cpus)。
  • 只读文件系统漏配 tmpfs:应用总要写点临时文件(pid 文件、锁),--read-only 之后按需在 /tmp、/run 挂 tmpfs。

小结

--memory 防拖垮(137 即 OOM)、--cpus 限配额;--read-only + tmpfs 顺手提升安全;JVM 类应用要和运行时参数联动。下一章系统化讲安全。