Docker 资源限制:3 个核心参数配置让你的容器告别 OOM 和 CPU 争抢
你有没有遇到过这种情况线上某个容器突然挂了docker ps -a一看Exited (137)或者某个 Java 容器把整个宿主机的 CPU 跑满其他服务全跟着遭殃我就是这么踩过来的。默认情况下Docker 容器对资源的使用是无限的——它可以使用主机内核调度器允许的任意资源。单个容器的内存泄漏就能拖垮整台物理机。生产环境不加资源限制就是在给自己埋雷。读完这篇文档你将会掌握--cpus、--cpu-shares、--cpuset-cpus的选型和踩坑点搞懂-m和--memory-swap的正确用法彻底告别 OOMKilled学会docker stats和docker update动态调整不重启也能救场拿到可直接复制的 Docker Compose 配置模板1. 为什么必须做资源限制很简单防一个坏容器搞死整台机器。Docker 底层用的是 Linux 的 cgroupsControl Groups说白了就是给每个容器划一个资源“配额”超过配额就拦住。没有限制的情况下一个跑偏的应用就能把主机的 CPU 或内存吃光。这里有个重要概念可压缩资源 vs 不可压缩资源。可压缩资源如 CPU不够用的时候只是跑得慢容器不会因此被干掉。不可压缩资源如内存一旦用完Linux 内核的 OOM Killer 会直接杀进程——通常是你的容器。这就是Exited 137的来源。所以内存限制比 CPU 限制更紧急必须设。不然泄露一次就要你半夜爬起来处理故障。2. CPU 资源限制三种方式选哪个Docker 提供了三种 CPU 限制方式2.1--cpu-shares相对权重弹性分配设置容器的 CPU 时间分配权重默认值是1024。只在 CPU紧张时才按比例分配空闲时容器随便用。docker run -d --name app-a --cpu-shares 1024 nginx docker run -d --name app-b --cpu-shares 512 nginx当 CPU 资源紧张时app-a 获得的 CPU 时间是 app-b 的两倍。什么时候用多容器混合部署想让核心服务多分点 CPU。但一旦 CPU 充裕这个设置根本无效——所以它不能作为硬性限制。2.2--cpus硬性核数限制推荐我最推荐这种方式简单直接不绕弯子# 限制最多使用 1.5 个 CPU 核心 docker run -d --name myapp --cpus1.5 nginx # 限制最多使用 2 个完整核心 docker run -d --name myapp --cpus2 nginx这个参数从 Docker 1.13 就有了生产环境可以直接用。50% 的核心数就用0.5别搞复杂了。2.3--cpuset-cpus绑定特定核心把容器绑死在固定的 CPU 核心上跑# 只在 CPU 0 和 CPU 3 上执行 docker run -d --name myapp --cpuset-cpus0,3 nginx # 绑定 0-2 三个核心 docker run -d --name myapp --cpuset-cpus0-2 nginx什么时候用低延迟场景可以试试因为能减少 CPU 跨核调度的开销。但我一般不用因为绑核心会降低整体资源的灵活调度能力。说实话除非你对延迟极度敏感否则--cpus就够了。2.4--cpu-period和--cpu-quota精确控制版这个是从 cgroup CFS完全公平调度器来的99% 的场景用不上。# 周期 100ms10万 µs配额 50ms5万 µs 50% CPU docker run -it --cpu-period100000 --cpu-quota50000 centos /bin/bash我的建议用--cpus就够了。--cpu-shares软限制 --cpus硬限制一起用是稳妥的生产实践。参数类型适用场景推荐优先级--cpus硬性核数绝大多数场景⭐⭐⭐⭐⭐--cpu-shares软性权重混合部署时弹性分配⭐⭐⭐--cpuset-cpus核心绑定低延迟/性能敏感⭐⭐--cpu-period/quota精确配额特殊定制需求⭐3. 内存资源限制不可压缩资源设置错了就等着 OOM这是最重要的一节多看几遍。内存一旦用完容器直接 OOMKilled不会给你缓冲的机会。我见过公司把 MySQL 容器内存设得太低半夜业务高峰直接崩溃亏惨了。3.1 核心参数-m/--memory必须设docker run -d --name mysql-prod -m 4g mysql:8.0容器最多用 4GB 物理内存。最小值是 6MBDocker 官方允许的最小值但实际上别设太极限。3.2--memory-swap内存 Swap 总量# 物理内存 512MB内存Swap 总量 1GB → Swap 实际可用 512MB docker run -d -m 512m --memory-swap1g myapp # 只设 -m 不设 --memory-swap → Swap 默认是 -m 的 2 倍 docker run -d -m 512m myapp # 128? 2 倍OK # 禁用 Swap docker run -d -m 512m --memory-swap-1 myapp这个坑我栽过--memory-swap必须 ≥-m。如果不想用 Swap设成-1。3.3--memory-reservation软性内存警戒线docker run -d -m 4g --memory-reservation 3g myapp软限制平时内存 3GB 不管超过 3GB 开始回收内存。可把它理解为内存版--cpu-shares。3.4--memory-swappiness换出倾向0-100# 尽量不用 Swap推荐 docker run -d -m 512m --memory-swappiness0 myapp默认内核可能会换出匿名内存页。我通常设成 0因为 Swap 很慢。数值越高越倾向换出。内存参数配置捷径命令效果-m 512m物理内存 512MBSwap 1GB-m 512m --memory-swap1g物理 512MBSwap 512MB-m 512m --memory-swap-1物理 512MB禁用 Swap-m 512m --memory-swap512m物理 512MB禁用 Swap等价⚠️换一句话说内存绝对是硬限制超了就杀。设值前务必评估应用峰值留 20–30% 缓冲。4. 磁盘 I/O 与文件句柄限制4.1 磁盘 IO 限制--device-read/write-bps# 读取限速 1MB/s写入限速 1MB/s docker run -it --device-read-bps /dev/sda:1mb --device-write-bps /dev/sda:1mb ubuntu读写 IO 用--device-read-iops和--device-write-iops。/dev/sda替换成你的实际块设备。4.2 文件句柄--ulimit nofiledocker run -d --name nginx-prod --ulimit nofile65535:65535 nginx这个防止 Nginx 这类应用跑出too many open files错误。生产环境建议 65535。5. 验证与动态调整5.1docker stats— 看资源占用$ docker stats --no-stream CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O a1b2c3d4 mysql 5.23% 1.2GiB / 4GiB 30% 1.2kB / 1.1kB5.2docker inspect— 查配置详情$ docker inspect mysql --format Memory: {{.HostConfig.Memory}}, CPU: {{.HostConfig.CpuShares}} Memory: 4294967296, CPU: 10245.3docker update— 动态调整不重启重磅运行时调整资源限制不用重启容器这是 Docker 20.10 的特性。# 实时加 CPU 核数 docker update --cpus 2.0 mysql-prod # 实时加内存 docker update --memory 6g mysql-prod # 同时调 docker update --cpus 2.0 --memory 6g --cpu-shares 2048 mysql-prod这个方法我在半夜救场过好几次——业务高峰内存不够了直接docker update扩容不用重启服务不断。注意--memory调大需要确保宿主机有货调小时可能导致 OOM三思。6. 常见错误与解决方法❌ 错误 1退出码 137 OOMKilled$ docker inspect myapp --format {{.State.ExitCode}} {{.State.OOMKilled}} 137 true原因内存用超了被内核直接杀了。解法docker stats看历史内存峰值调大-m或在宿主机层面dmesg -T | grep -i killed process看内核日志。如果排应用内存泄露没问题调大限制。❌ 错误 2容器慢得像蜗牛可能被 CPU 限流了。先看docker stats—— CPU % 一直逼近你设定的--cpus上限说明被 thottled 了。偶尔高于上限说明在排队。查看 cgroup 统计数据$ docker inspect myapp --format {{.Id}} a1b2c3d4... $ cat /sys/fs/cgroup/cpu/docker/a1b2c3d4.../cpu.stat | grep nthrottled nr_throttled 532 throttled_time 12345678数字不断增长说明容器在喘气等 CPU。解法增加--cpus或优化应用逻辑。❌ 错误 3No swap limit support警告$ docker info | grep -i swap WARNING: No swap limit support原因系统内核的参数没开。在/etc/default/grub加swapaccount1GRUB_CMDLINE_LINUX... swapaccount1然后update-grub reboot跑一下。建议尽早修复避免内存配置失效。7. 总结与互动3 个核心点记牢内存必须设限制-m不可压缩资源超了就死。CPU 推荐--cpus--cpu-shares组合硬配额 弹性权重两不误。docker update动态调参是生产救星不重启就能扩容半夜少哭几次。这还只是单机 Docker 层面的资源限制。如果你想了解 Kubernetes 下的资源管理比如requestsvslimits怎么配才不挖坑可以告诉我我回头再肝一篇。有用的话欢迎分享 你还在生产环境遇到过哪些资源限制导致的诡异问题评论区见。