Linux 容器资源限制与性能调优

1次阅读

容器启动时–memory未生效,因docker默认cgroup v1而新系统多启用v2,需配置daemon.json启用cgroup v2支持;java应用oom因jvm不自动感知容器内存限制,须显式设置-xmx并开启-xx:+usecontainersupport。

Linux 容器资源限制与性能调优

容器启动时加 --memory 为什么没生效?

Docker 默认使用 cgroup v1,但很多新系统(如 ubuntu 22.04+、centos 8+)默认启用了 cgroup v2。如果内核启用了 cgroup v2,而 Docker daemon 没显式配置支持,--memory--cpus 这类资源限制会被静默忽略——容器跑起来完全不受控,docker stats 显示的内存值也可能是宿主机总内存。

  • 确认当前 cgroup 版本:cat /proc/1/cgroup,如果路径含 unified 或第一行是 0::/,说明是 v2
  • 检查 Docker 是否启用 cgroup v2 支持:docker info | grep "Cgroup Driver",输出应为 systemdcgroupfs,且版本需 ≥ 20.10
  • 若不支持,需在 /etc/docker/daemon.json 中添加:
    {"exec-opts": ["native.cgroupdriver=systemd"]}

    ,然后重启 dockerd

docker run --memory=512m 后,Java 应用还是 OOM

JVM 不会自动感知容器内存限制,它读的是宿主机的 /proc/meminfo。即使你设了 --memory=512m,JVM 默认可能按宿主机内存的 1/4 设(比如宿主机 64G → 堆 16G),远超容器限额,触发内核 OOM killer 杀进程。

  • Java 8u191+ / Java 10+ 开始支持容器感知,但需显式开启:-XX:+UseContainerSupport(默认已开,但别假设)
  • 强制指定堆上限,而不是靠比例:-Xmx384m,且建议 ≤ 容器限制的 75%,留空间给元空间、直接内存等
  • 验证 JVM 实际识别的内存:在容器内运行 java -XX:+PrintFlagsFinal -version | grep MaxHeapSize,看输出是否接近你设的 -Xmx

限制 CPU 用 --cpus=1.5--cpu-period/--cpu-quota 有什么区别?

--cpus=1.5 是 Docker 封装后的语义糖,底层仍转成 --cpu-period--cpu-quota,但它的行为在不同内核版本下有差异:

  • 内核 ≥ 4.13:--cpus=1.5 等价于 --cpu-period=100000 --cpu-quota=150000,能真正实现“平均 1.5 核”

  • 内核 –cpu-shares(权重机制),无法硬限,高负载时可能吃到 2 核甚至更多

  • 如果你在 kubernetes 里用 resources.limits.cpu: "1500m",底层也是走 period/quota,但 kubelet 会做归一化,和裸 Docker 表现未必一致

  • 生产环境建议统一用 --cpu-period + --cpu-quota 显式控制,避免内核版本导致行为漂移

  • 注意:--cpu-quota 必须配合 --cpu-period 使用,单独设 quota 无效

容器内 top 看到的 CPU% 超过 100%,但 docker stats 显示正常

top 默认以单核为 100% 计算(即 4 核机器满载是 400%),而 docker stats 显示的是“实际占用的 CPU 时间占所有可用 CPU 时间的比例”,单位是百分比(100% = 一个完整 CPU 核的全部时间)。两者参照系不同。

  • docker stats 的 CPU% 是 cgroup 的 cpu.statusage_usec / total_usec 计算而来,受 --cpus 限制直接影响
  • top 的 %CPU 是采样周期内该进程在所有 CPU 上运行时间的总和占比,不感知容器限制
  • 更可靠的监控方式是看 docker stats --no-stream <container></container> 输出的 CPU % 列,或直接读容器 cgroup 路径:cat /sys/fs/cgroup/cpu,cpuacct/docker/<id>/cpu.stat</id>

cgroup 对资源的约束不是“闸门”,而是“配额调度器”,它不阻止进程申请资源,只决定是否给它执行机会。这点在调试延迟敏感型服务时最容易被忽略。

text=ZqhQzanResources