Java(JDK8 更新 131 之前)docker 容器 CPU 中的应用程序 运行/内存问题?
Java (prior to JDK8 update 131) applications running in docker container CPU / Memory issues?
docker 容器中的
JVM(更新 131 之前的JDK 8)运行 忽略了容器环境设置的 CGroup 限制。
而且,他们查询的是主机资源,而不是分配给容器的资源。
结果对 JVM 来说是灾难性的,即当 JVM 试图为自己分配比 CGroup 限制允许的更多的资源(CPU 或内存)时,docker 恶魔会注意到这一点并杀死 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容器支持首先出现在JDK10,然后移植到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。它开始调用 sched_getaffinity
以查找进程的 CPU 关联掩码,而不是 sysconf
。
这破坏了绑定挂载技巧。不幸的是,你不能像 sysconf
一样伪造 sched_getaffinity
。但是,可以使用 LD_PRELOAD.
替换 sched_getaffinity
的 libc 实现
我写了一个小型共享库 proccount 来替代 sysconf
和 sched_getaffinity
。因此,此库可用于在 8u191.
之前的所有 JDK 版本中设置正确数量的可用 CPU
工作原理
首先,它读取 cpu.cfs_quota_us
和 cpu.cfs_period_us
以查找容器是否使用 --cpus
选项启动。如果两者都大于零,则 CPU 的数量估计为
cpu.cfs_quota_us / cpu.cfs_period_us
否则它读取cpu.shares
并估计可用CPU的数量为
cpu.shares / 1024
这样的 CPU 计算类似于它在现代 container-aware JDK.
中的实际工作方式
库将 sysconf
和 sched_getaffinity
函数定义(覆盖)为 return 在 (1) 或 (2) 中获得的处理器数量。
如何编译
gcc -O2 -fPIC -shared -olibproccount.so proccount.c -ldl
使用方法
LD_PRELOAD=/path/to/libproccount.so java <args>
docker 容器中的
JVM(更新 131 之前的JDK 8)运行 忽略了容器环境设置的 CGroup 限制。 而且,他们查询的是主机资源,而不是分配给容器的资源。 结果对 JVM 来说是灾难性的,即当 JVM 试图为自己分配比 CGroup 限制允许的更多的资源(CPU 或内存)时,docker 恶魔会注意到这一点并杀死 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容器支持首先出现在JDK10,然后移植到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。它开始调用
sched_getaffinity
以查找进程的 CPU 关联掩码,而不是sysconf
。这破坏了绑定挂载技巧。不幸的是,你不能像
替换sysconf
一样伪造sched_getaffinity
。但是,可以使用 LD_PRELOAD.sched_getaffinity
的 libc 实现
我写了一个小型共享库 proccount 来替代 sysconf
和 sched_getaffinity
。因此,此库可用于在 8u191.
工作原理
首先,它读取
cpu.cfs_quota_us
和cpu.cfs_period_us
以查找容器是否使用--cpus
选项启动。如果两者都大于零,则 CPU 的数量估计为cpu.cfs_quota_us / cpu.cfs_period_us
否则它读取
cpu.shares
并估计可用CPU的数量为cpu.shares / 1024
这样的 CPU 计算类似于它在现代 container-aware JDK.
中的实际工作方式库将
sysconf
和sched_getaffinity
函数定义(覆盖)为 return 在 (1) 或 (2) 中获得的处理器数量。
如何编译
gcc -O2 -fPIC -shared -olibproccount.so proccount.c -ldl
使用方法
LD_PRELOAD=/path/to/libproccount.so java <args>