DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

Docker 设置 --memory 后,free 仍显示宿主机总内存

区分 free 的系统内存视图与容器 cgroup 限额,检查 Docker 保存的配置和 cgroup v2 控制文件,避免把显示总量当成限制失效。

容器启动时已经设置 --memory=512m,进入容器执行 free -m,总内存却仍接近宿主机的数 GiB。这个读数本身不能证明 Docker 忽略了限制。普通 Linux 容器里的 procps free 读取 /proc/meminfo,其中的系统总量与容器所属 cgroup 的内存上限是不同对象。

下面针对 Docker Engine 的 Linux 容器。Docker Desktop 中的“宿主系统”通常是运行容器的 Linux 虚拟机,不是桌面操作系统的全部物理内存。若环境专门替换了容器看到的 /proc/meminfo,或者使用其他实现的 free,显示还可能不同,应先确认工具与挂载环境。

先核对限制是否真的写到了这个容器

在连接目标 Engine 的终端进行只读检查,myapp 是需要替换的容器名:

docker inspect --type container --format \
  'ID={{.Id}} Memory={{.HostConfig.Memory}} MemoryReservation={{.HostConfig.MemoryReservation}} MemorySwap={{.HostConfig.MemorySwap}}' \
  myapp
docker stats --no-stream myapp

Memory 是保存的硬内存限制,单位为字节。以 --memory=512m 创建的容器,对应值应为 536870912。Memory=0 表示这个字段没有设置容器自己的硬限制,不能把它解释成只允许使用零字节;上级 cgroup 或虚拟机仍可能限制实际可用资源。

MemoryReservation 是软限制,不能当作 Memory 的等价替代。也要确认检查的是修改后的容器 ID,而不是旧容器或另一个 Docker context 下的同名对象。部署文件已经修改,并不自动证明当前容器采用了它。

docker stats 的 MEM USAGE / LIMIT 可用于观察容器的用量和显示限额,仍需区别采样时间、单位及缓存统计口径。配置字段和一次统计查询不是完整的负载测试,但比拿 free 的系统总量推断容器限额更接近要检查的对象。

在 cgroup v2 中核对实际控制文件

Docker 从 20.10 起支持 cgroup v2。先确认当前 Engine 的模式:

docker info --format '{{.CgroupVersion}} {{.CgroupDriver}}'
docker inspect --type container --format '{{.HostConfig.CgroupnsMode}}' myapp

对 cgroup v2、使用私有 cgroup namespace、且容器内 /sys/fs/cgroup 对应本容器 cgroup 的普通场景,可以读取:

docker exec myapp cat /sys/fs/cgroup/memory.max
docker exec myapp cat /sys/fs/cgroup/memory.current

memory.max 是该 cgroup 的硬限制,memory.current 是该组及其后代当前使用的内存。它们不是一对“总物理内存和空闲内存”,也不能把两者简单相减后当作应用堆的可分配空间。

数字以字节表达;memory.max 为 max 表示该层没有设置这个上限,并不表示机器具有无限内存。祖先 cgroup 的约束、同机负载和实际物理资源依然有效。接近上限时,内核可能回收内存或触发该 cgroup 内的 OOM 处理;部分分配行为和瞬时统计仍有边界,不能把它当成精确不变的显示值。

容器没有 cat、控制文件不存在或权限不足时,不要把失败当成没有限制,也不要为读取指标给业务容器加特权。应在实际 Engine 主机上,由现有运维权限定位该容器的 cgroup 再读对应文件。Docker Desktop 的该主机是其虚拟机,远程 Engine 则是远端服务器。

cgroup v1 的文件名和层级不同,硬限制通常查看 memory.limit_in_bytes。路径还受 systemd/cgroupfs 驱动影响,不能复制一个旧的 /lxc/容器ID/ 路径就认定找到了当前容器。本文的 v2 两条路径也不能原样当作 v1 操作步骤。

限制有效时,修正监控与应用预算

若 Docker 保存的限制、实际 cgroup 控制文件与预期一致,而只有 free 总量看起来太大,应修改监控取数位置或应用内存预算,不必修改 /proc/meminfo,也不要为让数字看起来一致重启全部容器。

按系统总内存自动决定堆、缓存或 worker 数的应用,可能需要启用其容器感知能力,或配置明确的应用预算。预算应为程序其他内存、库和并发开销留出空间,不能把容器的全部硬限制直接填成堆上限。具体参数取决于运行时及版本,不能用一套设置覆盖所有语言。

若限制根本没有应用,则回到原部署配置修正相应容器的资源参数,再按维护流程更新。先确认应用正常工作所需内存和宿主机容量;降低限制可能导致正在运行的工作失败,不要在线制造内存耗尽来“测试限制是否有效”。

另外,--memory-swap 与物理内存限制相关但不是同一字段。它配置的是允许使用的内存与 swap 合计,free 显示的宿主 swap 也不能用来证明容器可使用多少 swap。这里不需要为了纠正总量显示而禁用 OOM 保护或增加 swap。

验收看同一容器的限制与正常负载

保存容器 ID、原资源配置和监控定义,核对配置字段、对应 cgroup 文件及正常代表性负载下的指标。没有进行压力验证时,只记录已确认的配置和控制文件,不把一次查询写成“所有情况下都不会超限”。

如果只修正了监控的取数口径,标明变更时间并检查告警阈值,避免把图表突然下降解释成应用节省了内存。若实际调整了部署限额,回退时恢复原资源参数并按同一部署流程应用;无须删除镜像或数据卷。