1、Docker 内存优化概述

说到 Docker 内存优化,我第一反应就是「别让它吃到撑」。容器化部署刚火那几年,我遇到过太多「莫名 OOM」的案例了——进程挂掉、服务中断、半夜被报警吵醒。后来才慢慢摸清,内存问题说白了就是资源争抢和配置不当。这一章咱们先把这个领域的地图画出来。

1.1 为什么内存优化这么重要?

你想想看,一台物理机或虚机上有十几个容器在跑。如果每个容器都不设限制,某个容器内存泄漏,其他容器就跟着遭殃。我最早在生产环境吃过的亏就是:一个 Node.js 容器慢慢吃掉所有内存,最后宿主机 OOM Killer 直接把所有进程干掉了,包括数据库容器。嗯,从那之后我再也不信「默认没限制也无所谓」这种话了。

Docker 的内存管理其实分两层:宿主机内核用 cgroups 做资源隔离,容器内的进程自己管理内存申请。优化要同时看这两层,光调一边往往不够。

核心目标:在保证服务稳定的前提下,降低整体内存占用,避免 OOM,提升容器密度。

我个人习惯把优化方向拆成四个维度:

  • 限制配置 – 设置容器内存上限、预留值、swap 策略
  • 应用层优化 – 语言运行时、线程数、缓存策略
  • 镜像与存储 – 分层缓存、临时文件、overlay2 写时复制
  • 监控与调优 – 持续采集指标,识别异常行为

1.2 常见内存问题与症状

问题 症状 常见原因
容器被 OOM Kill 进程突然消失,`docker logs` 无最后输出 内存限制过小,或应用泄漏
宿主机 SWAP 高 容器响应变慢,磁盘 IO 升高 swap 未限制,内存不足时换页
容器启动慢/失败 启动时申请大内存失败 预留值过大,总和要求超过宿主机
镜像拉取占用内存 `docker pull` 时宿主机内存飙升 分层解压缓存,或并发拉取过多

我曾经踩过一个坑:一个 Java 应用容器只设了 --memory=512m,但 JVM 默认堆就 1/4 物理内存,在 2G 宿主机上它申请了 500MB 堆,加上元空间和线程,瞬间突破限制被 Kill。后来加了 -Xmx 参数才解决。这告诉我们:容器限制和应用配置要对齐。

1.3 内存优化的知识体系

下面这张图帮你快速建立整体视角——怎么从「监控发现问题」到「定位根因」再到「实施优化」。

Docker 内存优化知识全景 ① 监控与告警 ② 定位内存热点 ③ 调整资源限制 ③ 应用层降内存 ④ cgroups 参数 ④ 运行时与 GC 调优 ⑤ 验证与回归 持续循环,每轮降低 10%~20%

提示:刚开始做优化时,我建议从「监控」入手。先装好 cAdvisor 或者 Prometheus + Node Exporter,把容器内存指标收上来。没有数据做支撑,一切优化都是瞎猜。

1.4 你需要知道的核心概念

在动手之前,几个关键术语必须清楚。记不住没关系,后面每章都会用到。

  • memory.limit_in_bytes – cgroups 里限制容器可用内存上限(硬限制),超过后可能触发 OOM Kill。
  • memory.soft_limit_in_bytes – 软限制,宿主机内存充足时允许超过,紧张时优先回收。
  • swap 限制 – 默认容器可以用宿主机 swap,但生产环境我通常禁掉或设很小的 swap 限制,避免性能抖动。
  • RSS vs Page Cache – RSS 是常驻物理内存,Page Cache 是磁盘缓存。Docker 的 --memory 限制的是 RSS + Page Cache,不是只算应用自己。
  • OOM Score – 内核选择杀哪个进程的优先级,可以调整 --oom-score-adj。

警告:永远不要在关键生产容器上设 --memory-swap=-1(无限 swap)。我曾经见过一台服务器因为 swap 使用过度导致磁盘 IO 打满,所有容器都卡死。宁可 OOM Kill 一个容器,也不要让整个宿主机瘫痪。

1.5 快速上手:用 docker stats 看内存

打开终端,跑个命令感受一下:

docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}"

输出大概长这样:

CONTAINER      MEM USAGE / LIMIT    MEM %
web-app        128.4MiB / 512MiB    25.08%
redis          4.75MiB / 256MiB     1.86%
nginx          8.12MiB / 128MiB     6.34%

注意看 LIMIT 列,如果显示 -- 表示没设限制。这是个大坑——没限制的容器,一旦泄漏就无上限。我个人习惯每个容器至少设一个 --memory,哪怕设大一点,也比没有强。

快速检查:docker inspect <container> | jq '.[0].HostConfig.Memory' 可以查看限制值,如果是 0 就是没设。

1.6 本章小结与接下来的方向

这一章带你搭了骨架:内存优化到底要优化什么、从哪开始、用什么工具看。我不喜欢一上来就给大堆参数,先理解问题域,后面的章节才有落脚点。

嗯,回头整理一下知识点:

  • 优化目标:稳定、低占用、高密度
  • 四维度:限制配置、应用层、存储、监控
  • 典型问题:OOM Kill、swap 抖动、配置与应用不对齐
  • 先上监控,再动手,每轮小步验证

后面我们会依次深入每个维度。从最基础的 --memory 参数开始,到 JVM/Node/Go 的运行内存调优,再到镜像分层缓存优化——一步步把内存吃透。