如何让Docker容器内进程充分使用全部可用交换空间
问题现象
我编写了一个Node.js内存测试程序,每次以约80MB(new Array(1e7))的增量消耗内存,目标是突破宿主机(Ubuntu 20.04.4 LTS)8GB物理内存限制,利用配置的16GB交换空间继续分配内存。
宿主机直接运行时功能正常:
node --max-old-space-size=16384 ./memtest/memtest.js
但在Docker容器内运行时,进程在内存占用约3GB时就被杀死,远未达到8GB物理内存上限,此时宿主机仍有5GB空闲内存。即使添加了Docker内存参数配置,问题依旧:
docker run -m 8g --memory-swap -1 --memory-swappiness 100 ...
进程被杀时的内存占用为3037.08 MB。
宿主机交换空间状态:
# swapon --show NAME TYPE SIZE USED PRIO /swapfile file 16G 187M -2 # free total used free shared buff/cache available Mem: 8148324 1022788 5538364 124 1587172 6813484 Swap: 16777212 191228 16585984
测试用JS代码:
const bufs = []; function testMemory() { setInterval(() => { console.log(`${(process.memoryUsage().heapUsed / (1024 * 1024)).toFixed(2)} MB`); bufs.push(new Array(1e7)); }, 100); } testMemory();
可能原因分析
- Node.js内存参数遗漏:容器内运行Node时未设置
--max-old-space-size=16384,导致Node默认堆内存限制(64位系统约4GB)触发OOM崩溃(但这种情况是Node主动抛出错误,而非被系统杀死)。 - Docker参数配置问题:
--memory-swap=-1的生效依赖正确的参数顺序,或宿主机cgroup版本不支持该配置;-m 8g设置的物理内存限制可能被误解为总内存(物理+swap)限制。 - OOM Killer触发逻辑:容器的cgroup内存限制可能未正确应用,导致宿主机或容器内的OOM Killer提前杀死进程。
- 内存统计偏差:原代码仅统计V8堆内存(
heapUsed),未包含进程实际占用的物理内存(RSS),无法准确判断容器内存使用情况。
解决步骤
1. 确认进程被杀原因
查看宿主机OOM日志,判断是系统还是容器cgroup触发的杀死操作:
dmesg | grep -i oom
如果日志包含cgroup out of memory,说明是容器内存限制问题;若为宿主机OOM,则需排查其他进程内存占用。
2. 修正Docker运行参数
去掉-m 8g限制,直接允许容器无限制使用物理内存+交换空间,同时确保Node参数正确:
docker run --memory-swap=-1 --memory-swappiness=100 node --max-old-space-size=16384 ./memtest/memtest.js
--memory-swappiness=100确保容器优先使用交换空间,--memory-swap=-1表示不限制交换空间使用。
3. 验证容器cgroup配置
进入容器,检查内存和交换空间限制是否生效:
cat /sys/fs/cgroup/memory/memory.limit_in_bytes cat /sys/fs/cgroup/memory/memory.memsw.limit_in_bytes
若memory.memsw.limit_in_bytes返回9223372036854771712,说明交换空间无限制配置生效。
4. 检查Docker daemon全局配置
查看/etc/docker/daemon.json,确保无默认内存限制覆盖:
{ "default-runtime": "runc" }
若存在"memory"或"memory-swap"等全局配置,需删除或调整,之后重启Docker daemon:
systemctl restart docker
5. 优化测试代码的内存统计
修改代码,同时输出堆内存和实际物理内存占用,便于排查:
const bufs = []; function testMemory() { setInterval(() => { const { heapUsed, rss } = process.memoryUsage(); console.log(`堆内存占用: ${(heapUsed / 1024 / 1024).toFixed(2)} MB, 实际物理内存: ${(rss / 1024 / 1024).toFixed(2)} MB`); bufs.push(new Array(1e7)); }, 100); } testMemory();
总结
最可能的问题是容器内运行Node时遗漏了--max-old-space-size=16384参数,或Docker的-m 8g限制导致cgroup提前触发OOM。通过修正运行参数、验证cgroup配置,即可让容器内进程充分利用宿主机交换空间。
内容的提问来源于stack exchange,提问作者Tom

