繁体   English   中英

Kube.netes 为运行 JVM 的 pod 抛出 OOM

[英]Kubernetes throwing OOM for pods running a JVM

我正在运行 Docker 个容器,其中包含 JVM (java8u31)。 这些容器作为 pod 部署在 kube.netes 集群中。 我经常遇到 pod 的 OOM,而 Kube.netes 会杀死 pod 并重新启动它。 由于我是 Kube.netes 的新手,因此我在寻找这些 OOM 的根本原因时遇到了问题。

  1. 下面是JVM参数

    -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Xms700M -Xmx1000M -XX:MaxRAM=1536M -XX:MaxMetaspaceSize=250M
  2. 这些容器部署为有状态集,以下是资源分配

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

    所以分配给容器的总数 memory 与 MaxRam 匹配

  3. 如果我使用-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/etc/opt/jmx/java_pid%p.hprof这无济于事,因为一旦出现 OOM,pod 就会被杀死并重新创建并启动,因此其中的所有内容豆荚丢了

    获取线程或 HEAP 转储的唯一方法是将 SSH 放入 pod 中,这也是我无法接受的,因为在 OOM 后重新创建了 pod,所以在 OOM 时我没有得到 memory 足迹。 我SSH后OOM这也没什么用。

  4. 我还使用 visualVM、jHat 分析了代码,但找不到大量的 memory 足迹,这可能导致 JVM 内运行的线程消耗过多 memory 或可能泄漏的结论。

感谢任何帮助解决 Kube.netes 抛出的 OOM。

当您在pod中的应用程序达到您通过resources.limits.memory或命名空间限制设置的内存限制时,Kubernetes将重新启动该pod。

以下文章描述了限制资源的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用于客户端编译器, 4 GB用于32位服务器编译器, 128 GB用于64位编译器。 最大堆大小是MaxRAM的四分之一。 这就是默认堆大小可能变化的原因:如果计算机上的物理内存小于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指定的值-XX:OldSize=N (默认为4 MB )加上 - XX:NewSize=N (默认为1 MB )。 在这种情况下,旧大小和新大小的总和将用作初始堆大小。

本文为您提供了一个很好的方法来开始为面向Web的应用程序调整JVM:

感谢@VAS的意见。 感谢kubernetes链接。

经过几次测试后,我认为如果你使用-XX指定XMX不是一个好主意:+ UseCGroupMemoryLimitForHeap,因为XMX会覆盖它。 我还在做一些测试和分析。

因为我的要求是在docker容器中运行JVM。 我在@Eugene的帖子中提到了很少的测试。 考虑到在JVM中运行的每个应用程序都需要HEAP和一些本机内存,我认为我们需要指定-XX:+ UnlockExperimentalVMOptions,XX:+ UseCGroupMemoryLimitForHeap,-XX:MaxRAMFraction = 1(仅考虑容器内运行的JVM,在同时它的风险)-XX:MaxRAM(我想我们应该指定这个,如果MaxRAMFraction为1,那么你留下一些本机内存)

几个测试:

根据以下docker配置,如果您只在容器内运行JVM,则docker会分配1 GB。 考虑到docker对1G的分配,我也想为进程/本机内存分配一些,我想我应该使用MaxRam = 700M,以便我有300 MB的本机。

$ docker run -m 1GB openjdk:8u131 java -XX:+ UnlockExperimentalVMOptions -XX:+ UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction = 1 -XX:MaxRAM = 700M -XshowSettings:vm -version VM settings:Max。 堆大小(估计):622.50M人体工程学机器类:服务器使用VM:OpenJDK 64位服务器VM

现在指定XX:MaxRAMFraction = 1可能会导致:

参考: https ://twitter.com/csanchez/status/940228501222936576?lang = en -XX:MaxRAMFraction = 1在集装箱环境中生产是否安全?

以下会更好,请注意我已删除MaxRAM,因为MaxRAMFraction> 1:

$ docker run -m 1GB openjdk:8u131 java -XX:+ UnlockExperimentalVMOptions -XX:+ UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction = 2 -XshowSettings:vm -version VM settings:Max。 堆大小(估计):455.50M人体工程学机器类:服务器使用VM:OpenJDK 64位服务器VM

这给了本机500M的其余部分,例如可以通过指定-XX:MaxMetaspaceSize用于MetaSpace:

$ docker run -m 1GB openjdk:8u131 java -XX:+ UnlockExperimentalVMOptions -XX:+ UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction = 2 -XX:MaxMetaspaceSize = 200M -XshowSettings:vm -version VM settings:Max。 堆大小(估计):455.50M人体工程学机器类:服务器使用VM:OpenJDK 64位服务器VM

逻辑上以及根据上述参考,指定-XX:MaxRAMFraction> 1是有意义的。 这还取决于完成的应用程序分析。

我还在做一些测试,会更新这些结果或发布。 谢谢

如果您能够在Java 11(或10)而不是8上运行,则内存限制选项已得到很大改进(加上JVM可识别cgroups)。 只需使用-XX:MaxRAMPercentage (范围-XX:MaxRAMPercentage ):

$ 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%的可用容器内存,这对于旧选项是不可能的。

最近我也遇到过类似的问题

java 11.0.11+9 + kube.netes 在 pod 中运行 docker 个容器

与op类似的配置

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

-XX:MaxRAMPercentage=60.0

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM