Compose文件明明写了内存限制,监控却显示容器占用超过设定值;或者应用把整台服务器内存都识别出来,让人误以为限制完全没有生效。这里最容易混淆的是配置是否下发、cgroup是否执行,以及工具展示的究竟是哪种内存口径。
容器看到宿主机总内存,不等于它可以无限使用。 判断限制是否生效,应读取Docker保存的运行参数和对应cgroup计数,而不是只看容器内 free -h。

先读取正在运行容器的实际限制
找到容器并检查HostConfig:
docker ps --format 'table {{.Names}}\t{{.Status}}'
docker inspect app --format 'Memory={{.HostConfig.Memory}} MemorySwap={{.HostConfig.MemorySwap}}'
docker stats --no-stream app
Memory 以字节记录硬限制。输出为0通常表示没有设置硬限制;如果数值正确,再继续检查监控口径和cgroup。不要只确认YAML写过什么,运行中的容器才是当前事实。
限制是在创建容器时写入的。编辑Compose文件后若只执行 docker restart,旧容器配置不会因此重建。可先运行 docker compose config 查看解析结果,再评估使用 docker compose up -d 重建服务。
正确理解mem_limit与deploy.resources
普通Compose服务可使用 mem_limit:
services:
app:
image: example/app:latest
mem_limit: 512m
mem_reservation: 384m
mem_limit 是容器可分配内存的限制,mem_reservation 是内存紧张时使用的软性保留目标。两者含义不同,软限制不能保证进程永远低于该数值。
有些文件把限制只写在 deploy.resources.limits.memory 下。Compose规范要求相关字段保持一致,但具体平台、Compose版本与部署模式会影响处理方式。先用 docker compose config 和 docker inspect 验证结果,不要依据缩进正确就认定限制已经落地。
容器内free显示宿主机内存并不矛盾
Linux容器共享宿主机内核,部分系统工具读取的是全局内存信息。Docker官方也提醒,容器内的 free 对Swap的显示可能是宿主机口径,不能据此判断容器实际可用Swap。
应用运行时可能根据 /proc/meminfo 估算堆大小。如果Java、Node.js或其他运行时没有正确感知cgroup限制,需要结合对应版本文档设置最大堆或进程内存上限。cgroup负责执行边界,应用自身的内存规划仍应留出原生内存、线程栈和缓存空间。
检查cgroup版本和内核支持
先读取Docker与宿主机信息:
docker info | grep -Ei 'Cgroup Driver|Cgroup Version|WARNING'
stat -fc %T /sys/fs/cgroup
cgroup v2通常显示 cgroup2fs,v1环境的控制器路径不同。Docker会通过宿主机内核控制器执行资源限制;若 docker info 出现没有内存或Swap限制支持的警告,应先按发行版文档检查内核和启动参数。
不要手工在 /sys/fs/cgroup 中随意写值来覆盖Docker配置。容器重建后路径可能变化,编排器也可能重新写入参数。修复应落在Compose或容器启动配置中,再由Docker维护cgroup。
区分内存、缓存和Swap
docker stats 的内存用量会根据平台扣除部分缓存,第三方监控可能展示cgroup总用量、工作集或RSS,因此同一容器出现不同数字并不罕见。
硬限制和 --memory-swap 也要一起理解。Docker文档中,--memory-swap 表示内存加Swap的总量;它与 --memory 相同表示不允许使用Swap,未设置时则可能允许额外Swap,取决于宿主机配置。
频繁Swap会让应用没有立刻OOM,却产生明显延迟。 处理时同时观察容器内存、宿主机Swap、磁盘I/O和应用响应,不能把进程没被杀当成配置合理。
识别真正触发限制的证据
查看容器退出状态和宿主机日志:
docker inspect app --format 'OOMKilled={{.State.OOMKilled}} ExitCode={{.State.ExitCode}}'
journalctl -k --since '30 minutes ago' | grep -Ei 'oom|killed process'
触达硬限制后,内核可能终止容器内进程。退出码、OOMKilled 和内核日志形成完整证据。应用主动退出、健康检查失败或被编排器重启,则不一定是OOM。
不建议通过 oom_kill_disable 规避限制。Docker官方明确提示,未设置内存上限时关闭OOM杀手可能让宿主机耗尽内存并影响关键进程。
为业务设置可验证的容量边界
限制值应来自峰值负载测试,而不是按宿主机容量平均分配。数据库、JVM和缓存服务需要预留页缓存、直接内存、连接缓冲区及突发空间。限制太低会形成重启循环,太高则无法保护宿主机。
可以在 萤光云 上建立与生产同规格的短期环境,也可以使用按小时计费的 LightNode 复现相同Compose配置。测试时固定镜像版本、请求模型和数据量,才有可比较的峰值。
重建后执行完整验收
运行 docker compose config 确认配置,再重建服务并用 docker inspect 核对字节值。覆盖一次正常峰值,观察 docker stats、应用延迟、Swap和OOM记录。
验收应同时满足限制值准确、容器在正常峰值下稳定、宿主机保有安全余量,并且重启或重新部署后配置仍然存在。 只看一次瞬时内存曲线不足以证明修复完成。
常见问题
容器内 free -h 比限制值大,是不是限制没生效?
不一定。容器共享宿主机内核,部分工具展示全局信息。应以 docker inspect、docker stats 和cgroup计数综合判断。
修改Compose后执行docker restart可以吗?
通常不够。内存限制属于容器创建配置,需要通过Compose更新并重建容器,再回读实际值。
内存限制越紧越安全吗?
不是。过紧会让应用在正常峰值下OOM或频繁重启,应根据压测和运行时内存模型留出余量。
温馨提示
重建生产容器前,先保存当前Compose解析结果、环境变量和卷挂载信息,并确认服务具备回滚版本。不要为了验证OOM直接在高峰期压满生产内存,应在隔离环境或受控窗口完成容量测试。


