在 docker 容器中运行的 Java(JDK8 更新 131 之前)应用程序 CPU/内存问题?

标签 java docker jvm cgroups

在 docker 容器中运行的 JVM(更新 131 之前的 JDK 8)忽略了容器环境设置的 CGroup 限制。
而且,他们查询的是主机资源,而不是分配给容器的资源。
结果对 JVM 来说是灾难性的,即当 JVM 试图为自己分配比 CGroup 限制所允许的更多的资源(CPU 或内存)时,docker demo 会注意到这一点并杀死 JVM 进程或容器本身,如果 java 程序是以 pid 1 运行。
内存问题的解决方案 - (可能在 JDK 8 更新 131 中修复)
如上所述,JVM 为其自身分配的内存超出了容器允许的内存。这可以很容易地通过

  • 在启动 JVM 时显式设置最大堆内存限制(使用 -Xmx )。 (131更新前)
  • 或通过这些标志 - (131更新后)-XX:+UnlockExperimentalVMOptions-XX:+UseCGroupMemoryLimitForHeap

  • 解决 CPU 问题 (可能在 JDK 更新 212 中修复)
    同样如上所述,在 docker 中运行的 JVM 将直接查看主机硬件并获取可用的总 CPU。然后它会尝试根据这个 CPU 计数进行访问或优化。
  • 在 JDK 8 更新 212 之后,在 docker 容器中运行的任何 JVM 都将尊重分配给容器的 cpu 限制,而不是直接查看主机 cpu。
    如果一个具有 cpu 限制的容器如下启动,JVM 将遵守这个限制并将自己限制为 1 个 cpu。docker run -ti --cpus 1 -m 1G openjdk:8u212-jdk//在这个容器中运行的jvms被限制为1cpu。
  • 这是我的问题: CPU 问题可能已在 JDK8 更新 212 中修复,但是如果我无法更新我的 JVM 并且我正在运行更新 131 之前的版本,我该如何解决 cpu 问题。
  • 最佳答案

    Linux 容器支持最早出现在 JDK 10 中,然后移植到 8u191,见 JDK-8146115 .
    较早版本的 JVM 获取可用 CPU 的数量如下。

  • 在 8u121 之前,HotSpot JVM 依赖于 sysconf(_SC_NPROCESSORS_ONLN) libc 调用。反过来,glibc 读取系统文件/sys/devices/system/cpu/online .因此,为了伪造可用 CPU 的数量,可以使用 bind mount 替换此文件。 :
    echo 0-3 > /tmp/online
    docker run --cpus 4 -v /tmp/online:/sys/devices/system/cpu/online ...
    
    只设置一个 CPU,写 echo 0而不是 echo 0-3
  • 自 8u121 起,JVM 变为 taskset aware .而不是 sysconf ,它开始调用 sched_getaffinity查找进程的 CPU 关联掩码。
    这打破了绑定(bind)安装技巧。不幸的是,你不能伪造 sched_getaffinitysysconf .但是,可以替换 sched_getaffinity 的 libc 实现。使用 LD_PRELOAD .

  • 我写了一个小型共享库proccount替换sysconfsched_getaffinity .因此,该库可用于在 8u191 之前的所有 JDK 版本中设置正确的可用 CPU 数量。
    它是如何工作的
  • 首先,它显示 cpu.cfs_quota_uscpu.cfs_period_us查看容器是否使用 --cpus 启动选项。如果两者都大于零,则 CPU 数量估计为
    cpu.cfs_quota_us / cpu.cfs_period_us
    
  • 否则为 cpu.shares并将可用 CPU 的数量估计为
    cpu.shares / 1024
    
    这种 CPU 计算类似于它在现代容器感知 JDK 中的实际工作方式。
  • 库定义(覆盖)sysconfsched_getaffinity函数返回在 (1) 或 (2) 中获得的处理器数量。

  • 如何编译
    gcc -O2 -fPIC -shared -olibproccount.so proccount.c -ldl
    
    使用方法
    LD_PRELOAD=/path/to/libproccount.so java <args>
    

    关于在 docker 容器中运行的 Java(JDK8 更新 131 之前)应用程序 CPU/内存问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64262912/

    相关文章:

    java - 如何以新值退出 while 循环?

    java - 如何在 Fedora 28 中为 Docker 安装 Tomcat 容器?

    node.js - Prisma 创建数据库但随后表示无法连接到该数据库

    docker - 调用时将参数传递给docker-compose yml

    java - com.ibm.websphere.management.exception.AdminException :

    java - 尝试使用 Jackson 将 JSON 文件读入 Map

    java - 如何在 Kotlin 中创建匿名接口(interface)的实例?

    java - 如何在 vaadin 中定义 TextFields 的验证约束?

    java - Java中内存地址存放在哪里

    java - jvm可以配置得体贴一些,与其他进程共享计算资源(CPU和RAM)吗?