Kubernetes和JVM内存设置

15
在一个具有众多微服务的 Kubernetes 集群中,其中之一专门用于运行 Java 1.8 数据处理应用程序的 Java 虚拟机 (JVM)。直到最近,运行在 JVM pod 中的作业消耗的 RAM 少于 1 GB,因此 pod 的最大内存设置为 4 GB,没有为 JVM 设置任何显式的堆大小设置。现在一些新数据需要整个 pod(包括 JVM)约 2.5 GB 的内存,但使用 4 GB 限制启动后不久 pod 就会崩溃。使用类似于“-Xms256m -Xmx3072m”这样的堆大小范围并将限制设置为 4 GB 并不能解决问题。事实上,现在 pod 甚至无法启动。是否有任何方法可以对 JVM 进行参数化以适应所需的 2.5 GB,而不增加 pod 的最大内存为 4 GB?

2
JVM 的哪个版本?当前版本已知会查找 Pod 的资源限制并相应地调整堆大小,无需任何“-Xmx”选项。 - David Maze
Java 1.8(在问题中更新)。 - PNS
更具体地说?特别是Java 8u191可以自行解决 - David Maze
目前的问题措辞没有意义。如果一个进程占用了2.5GB的RAM,将其运行在4GB内存限制的容器中不会有问题。当谈到2.5GB时,您是指Java堆大小、进程驻留集大小(RSS)还是虚拟内存大小(VSZ)?这个答案可能会让您了解Java进程的内存占用情况。 - apangin
Pod的总RAM消耗达到了2.5 GB(问题已更新,包含更多细节)。基于此,4 GB的限制本应足够,但实际上不够,需要将其增加到8 GB,以便能够运行Pod并通过Kubernetes top命令进行内存测量。 - PNS
如果一个 pod 总共占用了 2.5 GB 的 RAM,它不会因为 4 GB 的限制而被杀掉。查看崩溃的原因 - 在内核日志中必须有来自 OOM killer 的消息,或者在 Kubernetes 日志中有一些线索。 - apangin
2个回答

28

如果您没有指定 -Xmx,默认情况下 "max heap" 的大小将是主机 RAM 的 1/4 (25%)。JDK 10 在容器支持方面有所改进,它使用容器的 RAM 限制而不是底层主机的 RAM。正如 @David Maze 所指出的那样,这已经被回退到 JDK 8 中。

假设您拥有足够新版本的JDK 8,可以使用 -XX:MaxRAMPercentage 修改用于 Max heap 的总 RAM 百分比的默认值。因此,您可以告诉它,例如 -XX:MaxRAMPercentage=75.0,而不是指定 -Xmx。另请参见 https://blog.arkey.fr/2020/10/27/maxrampercentage-is-not-what-i-wished-for/

以下是使用 Alpine JDK Docker 映像的示例:https://hub.docker.com/_/openjdk(特别是参见 "Make JVM respect CPU and RAM limits" 部分)。

# this is running on the host with 2 GB RAM
docker run --mount type=bind,source="$(pwd)",target=/pwd -it openjdk:8

# running with MaxRAMPercentage=50 => half of the available RAM is used as "max heap"
root@c9b0b4d9e85b:/# java -XX:+PrintFlagsFinal -XX:MaxRAMPercentage=50.0 -version | grep -i maxheap
    uintx MaxHeapFreeRatio                          = 100                                 {manageable}
    uintx MaxHeapSize                              := 1044381696                          {product}
openjdk version "1.8.0_265"
OpenJDK Runtime Environment (build 1.8.0_265-b01)
OpenJDK 64-Bit Server VM (build 25.265-b01, mixed mode)

# running without MaxRAMPercentage => default 25% of RAM is used
root@c9b0b4d9e85b:/# java -XX:+PrintFlagsFinal -version | grep -i maxheap
    uintx MaxHeapFreeRatio                          = 100                                 {manageable}
    uintx MaxHeapSize                              := 522190848                           {product}
openjdk version "1.8.0_265"

我们已经尝试了所有这些事实和配置选项,但它们都没有起作用。事实上,我们正在运行比8u200更新的OpenJDK 1.8版本,但某种方式选项“-XX:MaxRAMPercentage”仍然无法识别。 - PNS
1
至少最新的alpine JDK docker镜像似乎支持它 - 请参见我的编辑答案以及https://hub.docker.com/_/openjdk - Juraj Martinka
1
你是如何检查它“未被识别”的?你使用了-XX:+PrintFlagsFinal、某些监控工具还是仅仅根据应用程序的行为猜测的? - Juraj Martinka
1
@JurajMartinka 当您声称JVM使用容器的RAM限制而不是底层主机时,您是否特别指的是spec.containers[].resources.limits.memory?如果您例如设置了2GB的限制和1GB的请求,并且只能访问1GB,那么这不会导致问题吗?您将比限制少1GB... - Copy and Paste
是的,在 Kubernetes 中这应该与 .resources.limits.memory 有关 - 在此处更详细地讨论:https://blog.arkey.fr/2020/10/27/maxrampercentage-is-not-what-i-wished-for/。 - Juraj Martinka
1
博客链接似乎已经被移动到https://www.atamanroman.dev/development/2019/09/11/usecontainersupport-to-the-rescue.html。 - Jaap

0
在我的K8s设置中,我使用consul来管理pod配置。以下是一个命令,可以动态覆盖jvm设置。这主要取决于项目的具体情况,但如果您正在使用consul进行配置,则可能会给您一些提示。
kubectl -n <namespace> exec -it consul-server -- bash -c "export CONSUL_HTTP_ADDR=https://localhost:8500 && /opt/../home/bin/bootstrap-config --token-file /opt/../config/etc/SecurityCertificateFramework/tokens/consul/default/management.token kv write config/processFlow/jvm/java_option_xmx -Xmx8192m"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接