Kubernetes为运行JVM的Pod抛出OOM

qmb5sa22  于 2022-11-07  发布在  Kubernetes
关注(0)|答案(4)|浏览(328)

我正在运行包含JVM(java8u31)的Docker容器。这些容器作为pod部署在Kubernetes集群中。通常我会收到pod的OOM,Kubernetes会终止pod并重新启动它。由于我是Kubernetes的新手,因此在查找这些OOM的根本原因时遇到了问题。
1.以下是JVM参数

-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Xms700M -Xmx1000M  -XX:MaxRAM=1536M  -XX:MaxMetaspaceSize=250M

1.这些容器被部署为有状态集,下面是资源分配

resources:
    requests:
        memory: "1.5G"
        cpu: 1
    limits:
        memory: "1.5G"
        cpu: 1

以便分配给容器的总内存与MaxRam匹配
1.如果我使用-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/etc/opt/jmx/java_pid%p.hprof,则不会有任何帮助,因为一旦出现OOM,pod就会被终止、重新创建和启动,因此pod中的所有内容都将丢失
获得线程或堆转储的唯一方法是SSH到pod,但我也无法使用,因为pod是在OOM之后重新创建的,因此我无法在OOM时获得内存占用。我在OOM之后使用SSH,这没有多大帮助。
1.我还使用visualVM、jHat分析了代码,但没有发现大量的内存占用,这可能导致JVM中运行的线程消耗了太多的内存或可能存在泄漏。
我们非常感谢您帮助我们解决Kubernetes抛出的OOM。

h4cxqtbf

h4cxqtbf1#

当单元中的应用程序达到您通过resources.limits.memory或namespace limit设置的内存限制时,Kubernetes会重新启动单元。
下面的文章介绍了限制资源的Kubernetes部分:

Java应用程序消耗的内存不受堆大小的限制,您可以通过指定以下选项来设置堆大小:

-Xmssize Specifies the initial heap size.
-Xmxsize Specifies the maximum heap size.

Java应用程序需要一些额外的内存用于元空间、类空间、栈大小,而JVM本身需要更多的内存来完成它的任务,如垃圾收集、JIT优化、堆外分配、JNI代码。很难以合理的精度预测JVM的总内存使用量,因此最好的方法是在正常负载的真实的部署上测量它。
我建议您将Kubernetes pod限制设置为两倍Xmx大小,检查是否不再出现OOM,然后逐渐减小到开始出现OOM的点。最终值应该在这些点之间的中间。
您可以从监视系统(如Prometheus)中的内存使用统计信息中获得更精确的值。
另一方面,您可以尝试通过指定可用选项的数量来限制java内存使用,如下所示:

-Xms<heap size>[g|m|k] -Xmx<heap size>[g|m|k]
-XX:MaxMetaspaceSize=<metaspace size>[g|m|k]
-Xmn<young size>[g|m|k]
-XX:SurvivorRatio=<ratio>

有关详细信息,请参阅以下文章:

第二种限制JVM内存使用的方法是根据RAM的数量(或MaxRAM)计算堆大小。
默认大小基于计算机上的内存量,可以使用-XX:MaxRAM=N标志设置。通常,JVM通过检查计算机上的内存量来计算该值。但是,JVM将客户端编译器的MaxRAM限制为1 GB,将32位服务器编译器的4 GB限制为4 GB,和128 GB。最大堆大小是MaxRAM的四分之一。这就是默认堆大小可以变化的原因:如果计算机上的物理内存小于MaxRAM,则默认堆大小为该值的四分之一。但是,即使有数百GB的RAM可用,JVM默认使用的最大内存也是32 GB128 GB的四分之一。默认的最大堆计算实际上是这样的:
Default Xmx = MaxRAM / MaxRAMFraction
因此,也可以通过调整-XX:MaxRAMFraction=N标志的值来设置默认的最大堆,该标志的默认值为4。最后,为了保持有趣,还可以将-XX:ErgoHeapSizeLimit=N标志设置为JVM应该使用的最大默认值。默认情况下,该值为0(表示忽略它);否则,如果它小于MaxRAM / MaxRAMFraction,则使用该限制器。
初始堆大小的选择与此类似,但复杂程度较低。初始堆大小值的确定方式如下:
Default Xms = MaxRAM / InitialRAMFraction
从默认的最小堆大小可以得出结论,InitialRAMFraction标志的默认值是64。如果该值小于5 MB,或者,严格地说,小于-XX:OldSize=N指定的值(默认值为4 MB)加上-XX:NewSize=N(默认值为1 MB)。在这种情况下,旧大小和新大小的总和将用作初始堆大小。
本文为您提供了一个很好的切入点,帮助您开始为面向Web的应用程序调整JVM:

q0qdq0h2

q0qdq0h22#

如果您能够在Java 11(或10)上运行,而不是在Java 8上运行,那么memory limit options已经得到了很大的改进(加上JVM支持cgroups-aware)。只需使用-XX:MaxRAMPercentage(范围为0.0,100.0):

$ docker run -m 1GB openjdk:11 java -XshowSettings:vm -XX:MaxRAMPercentage=80 -version
VM settings:
    Max. Heap Size (Estimated): 792.69M
    Using VM: OpenJDK 64-Bit Server VM

openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment (build 11.0.1+13-Debian-2)
OpenJDK 64-Bit Server VM (build 11.0.1+13-Debian-2, mixed mode, sharing)

这样,您就可以轻松地为堆指定80%的可用容器内存,而这在旧的选项中是不可能的。

9ceoxa92

9ceoxa923#

感谢@VAS的评论。感谢kubernetes链接。
经过几次测试后,我认为如果你使用-XX:+UseCGroupMemoryLimitForHeap,指定XMX不是一个好主意,因为XMX会覆盖它。我还在做更多的测试和分析。
由于我的要求是在docker容器中运行JVM。我做了一些测试,如@尤金的文章中提到的。考虑到在JVM中运行的每个应用程序都需要HEAP和一些本机内存,我认为我们需要指定-XX:+UnlockExperimentalVMOptions,XX:+UseCGroupMemoryLimitForHeap,-XX:MaxRAMFraction=1(仅考虑在容器中运行的JVM,同时这是有风险的)-XX:MaxRAM(我认为如果MaxRAMFraction为1,我们应该指定此值,以便为本机内存留出一些)
少量测试:
根据下面的Docker配置,考虑到容器内只运行JVM,Docker被分配了1 GB。考虑到Docker被分配了1G,并且我还想分配一些给进程/本机内存,我认为我应该使用MaxRam= 700 M,这样我就有300 MB的本机内存。
$ docker run -m 1GB openjdk:8 u131 java -XX:+解锁实验虚拟机选项-XX:+堆使用C组内存限制-XX:最大RAM分数=1 -XX:最大RAM = 700 M-X显示设置:虚拟机-版本虚拟机设置:最大堆大小(估计):622.50M人体工程学机器类别:使用VM的服务器:OpenJDK 64位服务器虚拟机
现在,指定XX:MaxRAMFraction=1可能会导致以下问题:
参照:https://twitter.com/csanchez/status/940228501222936576?lang=enIs -XX:MaxRAMFraction=1 safe for production in a containered environment?
以下是更好的,请注意,我已经删除了MaxRAM,因为MaxRAMFraction〉1:
$ docker run -m 1GB openjdk:8 u131 java -XX:+解锁实验虚拟机选项-XX:+堆使用C组内存限制-XX:最大RAM分数=2 -X显示设置:虚拟机-版本虚拟机设置:最大堆大小(估计):455.50M人机工程学机器类别:使用VM的服务器:OpenJDK 64位服务器虚拟机
这将为本机提供500 M的其余部分,例如,可通过指定-XX:MaxMetaspaceSize:
$ docker run -m 1GB openjdk:8 u131 java -XX:+解锁实验虚拟机选项-XX:+使用CGroupMemory堆限制-XX:最大RAM分数=2 -XX:最大元空间大小= 200 M-Xshow设置:虚拟机-版本虚拟机设置:最大堆大小(估计):455.50M人机工程学机器类别:使用VM的服务器:OpenJDK 64位服务器虚拟机
从逻辑上讲,也根据上面的参考,指定-XX:MaxRAMFraction〉1是有意义的。这也取决于应用程序分析的完成。
我还在做一些更多的测试,将更新这些结果或张贴。谢谢

xqkwcwgp

xqkwcwgp4#

最近我也遇到了类似问题
java 11.0.11+9 + kubernetes在pod中运行Docker容器
配置与op类似

resources:
    requests:
        memory: "1G"
        cpu: 400m
    limits:
        memory: "1G"

使用-XX:MaxRAMPercentage=60.0
我们的服务上传和下载了大量数据。因此使用了直接内存和in this问题。我发现MaxDirectMemorySize等于堆大小。因此,如果我们计算内存使用量,它可能会超出1G限制(1G * 0.6 * 2)。在本例中,我们将内存增加到了1.5G,并更改了-XX:MaxRAMPercentage=35.0,因此我们有足够的空间用于堆+直接内存,甚至用于一些与操作系统相关的任务。在容器环境中设置MaxRAMPercentageXmx时要小心。

相关问题