4. 内存限制:使用 -m/--memory 参数限制容器内存

内存限制,说白了就是给容器划个“内存红线”。

我刚开始用 Docker 的时候,觉得这玩意儿可有可无。直到有一次线上服务突然 OOM,把宿主机整挂了……嗯,从那以后我再也不敢不设内存限制了。

4.1 为什么需要限制容器内存?

你想想看,一个容器如果不设内存上限,它就能把宿主机的内存吃光。Docker 默认是不限制的,这其实挺危险的。

我在项目中遇到过这样的情况:某个 Java 应用的内存泄漏,直接把 32G 的机器干到 Swap 爆满,最后所有容器都跟着遭殃。所以,内存限制不是“锦上添花”,而是“保命手段”。

4.2 基本用法:-m 或 --memory

最简单的用法,就是指定一个硬限制:

docker run -m 512m nginx

这条命令的意思是:这个 nginx 容器最多能用 512MB 内存。超过了怎么办?会被 OOM Kill。

我个人习惯用这些单位:

  • b:字节(不常用)
  • k:千字节
  • m:兆字节(最常用)
  • g:吉字节

举个例子:

docker run -m 1g --name myapp myapp:latest

这表示容器最多用 1GB 内存。超过就杀。

注意: 这里的“内存”包括物理内存和 Swap。如果你只设了 -m,没设 Swap 限制,Docker 默认会让 Swap 大小等于内存大小。也就是说,-m 512m 实际可用内存可能是 512m 物理 + 512m Swap = 1GB。

4.3 内存限制 + Swap 限制:--memory-swap

光设 -m 还不够。我建议你同时控制 Swap。

为什么要控制 Swap?因为 Swap 用多了,性能会急剧下降。我曾经排查过一个“慢查询”问题,最后发现是容器疯狂用 Swap,磁盘 I/O 爆了。

语法是这样的:

docker run -m 512m --memory-swap 512m nginx

这表示:物理内存 512MB,Swap 为 0。也就是说,容器最多只能用 512MB,超了就杀,绝不给你用磁盘当内存的机会。

如果你想给一点 Swap 空间:

docker run -m 512m --memory-swap 768m nginx

这表示:物理内存 512MB,Swap 256MB(768 - 512 = 256)。

参数组合 物理内存 Swap 总可用
-m 512m 512MB 512MB(默认) 1GB
-m 512m --memory-swap 512m 512MB 0 512MB
-m 512m --memory-swap 768m 512MB 256MB 768MB
-m 512m --memory-swap -1 512MB 无限制 无限制
我的建议: 生产环境尽量把 --memory-swap 设成和 -m 一样。这样容器不会用 Swap,性能更稳定。除非你的应用有突发内存需求,否则别开 Swap。

4.4 内存软限制:--memory-reservation

这个参数很多人不知道。它是个“软限制”。

什么意思呢?就是当宿主机内存充足时,容器可以用超过这个值的内存。但当宿主机内存紧张时,Docker 会尽量把容器内存压到软限制以下。

举个例子:

docker run -m 1g --memory-reservation 512m nginx

这表示:

  • 硬限制 1GB(不能超过)
  • 软限制 512MB(尽量不超过)

我一般在部署多个容器时用这个参数。比如一台机器跑 10 个容器,每个软限制 512MB,硬限制 1GB。平时大家用 512MB 左右,某个容器突然需要更多内存时,可以临时“借”一点。但谁也别想超过 1GB。

核心区别:
  • -m / --memory:硬限制,超过就杀
  • --memory-reservation:软限制,尽量遵守

4.5 查看内存使用情况

设了限制之后,怎么知道容器用了多少内存?

我最常用的命令:

docker stats

这个命令会实时显示所有容器的 CPU、内存、网络 I/O 等信息。输出大概长这样:

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O
abc123         myapp     0.50%     256MiB / 512MiB       50.00%    1.2kB / 0B

看到 MEM USAGE / LIMIT 那列了吗?256MiB / 512MiB 表示用了 256MB,限制是 512MB。

如果你想看更详细的内存信息:

docker inspect myapp | grep -i memory

或者直接进容器看:

docker exec myapp cat /sys/fs/cgroup/memory/memory.limit_in_bytes

这个文件里存的就是内存限制的字节数。536870912 就是 512MB。

4.6 实战中的避坑指南

我踩过不少坑,分享几个给你:

  • 别把 -m 设得太小:比如 Java 应用,JVM 本身就要占不少内存。设得太小,容器刚启动就被杀了。我一般会留 20%-30% 的余量。
  • 注意 --memory-swap 的默认行为:如果不显式设置,Docker 会让 Swap 等于内存。这可能导致容器实际可用内存比你设的大一倍。
  • OOM 优先级:Docker 会调整容器的 OOM 优先级。但如果你手动设了 --oom-kill-disable,容器就不会被 OOM Kill,而是卡住。这通常不是好主意。
我曾经犯过的错: 有一次我把 -m 设成 128m,但应用需要 200MB。结果容器每隔几分钟就被杀一次,重启、被杀、重启、被杀……循环了半小时我才发现。所以,一定要先压测,再定内存限制。

4.7 总结一下

内存限制这事儿,说白了就三点:

  1. 用 -m 设硬限制,防止容器吃光宿主机内存
  2. 用 --memory-swap 控制 Swap,避免性能下降
  3. 用 --memory-reservation 设软限制,让资源分配更灵活

我个人习惯是:生产环境一律设 -m 和 --memory-swap 相等,再根据压测结果定一个合理的软限制。这样既安全,又不会浪费资源。

嗯,下一章我们聊聊 CPU 限制。那个比内存限制稍微复杂一点,但原理是相通的。