我的Java微服务正在AWS EC2实例上托管的k8s集群中运行。
我在K8s集群中运行着大约30个微服务(nodejs和Java 8的很好的结合)。我面临一个挑战,我的Java应用程序pod意外重启,导致应用程序5xx计数增加。
要调试此功能,我在Pod中与应用程序一起启动了一个newrelic代理,并找到了下图:
我可以看到的是,我的Xmx值为6GB,最大使用量为5.2GB。
这显然表明JVM没有超过Xmx值。
但是当我描述吊舱并寻找最后的状态时,它会说“原因:错误”和“退出代码:137”
然后,在进一步调查中,我发现我的Pod平均内存使用一直都接近其限制。(分配的9Gib,使用〜9Gib)。我什至无法理解为什么Pod中的内存使用如此之高,即使我只运行一个进程((JVM)),也受6Gib Xmx限制。
登录到工作节点并检查docker容器的状态时,我可以看到该退出的最后一个容器具有“退出”状态,并显示“容器退出,退出代码为非零137”
我可以看到wokernode内核日志为:
这表明内核正在终止我在容器内运行的进程。
我可以看到我的工作程序节点中有很多可用内存。
我不确定为什么我的Pod一次又一次重启是k8s的行为还是我的基础架构中存在某种欺骗行为。这迫使我再次将应用程序从Container移到VM,因为这导致5xx计数增加。
编辑:将内存增加到12GB后,我得到了OOM。
我不确定为什么OOM会导致POD被杀死 JVM xmx仅为6 GB。
需要帮助!
答案 0 :(得分:1)
由于您已将Pod的最大内存使用量限制为9Gi,因此当内存使用量达到9Gi时,它将自动终止。
答案 1 :(得分:1)
一些较旧的Java版本(Java 8 u131发行版之前)无法识别它们正在容器中运行。因此,即使您使用-Xmx为JVM指定最大堆大小,JVM也会基于主机的总内存而不是容器可用的内存来设置最大堆大小,然后当进程尝试分配超出其限制的内存时(在pod / deployment规范中定义),您的容器已被OOMKilled。
在本地K8集群中运行Java应用程序时,这些问题可能不会弹出,因为Pod内存限制与本地本地计算机总内存之间的差异并不大。但是,当您在具有更多可用内存的节点上在生产环境中运行它时,JVM可能会超出您的容器内存限制,并将被OOMKilled。
从Java 8(u131版本)开始,可以使JVM具有“容器意识”,以便它可以识别由容器控制组(cgroup)设置的约束。
对于 Java 8 (从U131版本开始)和Java9 ,您可以将此实验性标志设置为JVM:
-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
它将根据您的容器cgroup内存限制设置堆大小,该限制在pod / deployment规范的容器定义部分中定义为“资源:限制”。 在Java 8中,仍有可能出现JVM堆外内存增加的情况,因此您可以进行监视,但总体而言,这些实验性标志也必须对此进行处理。
在 Java 10 中,这些实验性标志是新的默认标志,可以通过使用以下标志来启用/禁用:
-XX:+UseContainerSupport
-XX:-UseContainerSupport
答案 2 :(得分:0)
在GCloud App Engine中,您可以指定最大CPU使用率阈值,例如0.6。这意味着如果CPU达到100%的0.6-60%-将会产生一个新实例。
我没有遇到这样的设置,但是也许:Kubernetes POD / Deployment具有类似的配置参数。这意味着,如果POD的RAM达到100%的0.6,则终止POD。在您的情况下,这将是9GB的60%=〜5GB。只是一些值得深思的东西。