如何在Kubernetes中优雅而安全地最大化分配给Java应用程序的堆空间量?

如何在Kubernetes中优雅而安全地最大化分配给Java应用程序的堆空间量?,java,java-8,kubernetes,Java,Java 8,Kubernetes,我有一个Kubernetes部署,它基于映像部署Java应用程序。除了Java应用程序和容器开销之外,容器中没有其他运行内容 我想最大化Java进程在docker容器中可以使用的内存量,并最大限度地减少保留但从未使用的ram量 例如,我有: docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitFor

我有一个Kubernetes部署,它基于映像部署Java应用程序。除了Java应用程序和容器开销之外,容器中没有其他运行内容

我想最大化Java进程在docker容器中可以使用的内存量,并最大限度地减少保留但从未使用的ram量

例如,我有:

docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version
docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -XX:MaxRAMFraction=2 -version
  • 两个Kubernetes节点,每个节点有8 gig的ram,并且没有交换
  • 一种Kubernetes部署,它运行一个Java进程,最多消耗1 gig的堆来优化运行
  • 我如何安全地最大化两个节点上运行的pod数量,而不会因为内存限制而让Kubernetes终止我的pod?

    Java8Update131+有一个标志-XX:+UseCGroupMemoryLimitForHeap使用来自Kubernetes部署的Docker限制

    我的Docker实验告诉我Kubernetes发生了什么

    如果我在Docker中运行以下命令:

    docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version
    
    docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -XX:MaxRAMFraction=2 -version
    
    我得到:

    docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version
    
    docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -XX:MaxRAMFraction=2 -version
    
    这个低值是因为Java在默认情况下将-XX:MaxRAMFraction设置为4,我得到了分配的大约1/4的ram

    如果在Docker:

    docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version
    
    docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -XX:MaxRAMFraction=2 -version
    
    我得到:

    docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version
    
    docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -XX:MaxRAMFraction=2 -version
    
    最后,设置MaxRAMFraction=1会很快导致Kubernetes杀死我的容器

    docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -XX:MaxRAMFraction=1 -version
    
    我得到:

    docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version
    
    docker run -m 1024m anapsix/alpine-java:8_server-jre_unlimited java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -XX:MaxRAMFraction=2 -version
    

    在我们的案例中,我们所做的是在kubernetes上启动高内存限制,在负载下随时间观察,或者将内存使用调整到我们希望通过
    -Xmx
    达到的水平,或者调整内存限制(和请求)以适应实际内存消耗。说实话,我们通常混合使用这两种方法。这种方法的关键是在集群(我们的例子中是普罗米修斯)上启用适当的监视,如果您想要高水平的微调,您可能还需要添加类似JMX普罗米修斯导出器的东西,在优化设置时详细了解指标。

    我认为这里的问题是kubernetes内存限制用于容器,MaxRAMFraction用于jvm。因此,如果jvm堆与kubernetes限制相同,那么容器本身就没有足够的内存了

    你可以尝试的一件事是增加

    limits:
      memory: 2048Mi
    

    保持
    请求
    限制相同。请求和限制之间的根本区别在于,如果节点级别有可用内存,请求将允许您超过限制,而
    限制
    是一个硬限制。这可能不是一个理想的解决方案,您必须计算出您的pod在jvm上消耗了多少内存,但作为一个快速解决方案,增加
    限制应该是可行的。

    Kubernetes杀死您的pod的原因是。由于容器开销以及内存使用规范中十进制和二进制前缀之间的不匹配,计算起来很困难。我的解决方案是完全取消限制,只保留要求(这是你的pod在任何情况下都可以使用的,如果它是预定的)。依靠JVM通过静态规范限制其堆,让Kubernetes通过资源需求管理单个节点上调度的POD数量

    首先,在使用所需堆大小运行时,需要确定容器的实际内存使用情况。使用
    -Xmx1024m-Xms1024m
    运行一个pod,并连接到它预定的主机docker守护程序。运行
    docker ps
    查找您的pod和
    docker stats
    查看其当前内存使用情况,它是JVM堆、其他静态JVM使用情况(如直接内存和您的容器开销)的总和。这个值应该只在kibibytes内波动,因为某些网络使用是在JVM之外处理的。将此值作为内存需求添加到pod模板中

    计算或估计节点上的其他组件需要多少内存才能正常工作。至少会有Kubernetes kubelet,Linux内核,它的用户区,可能是一个SSH守护进程,在您的例子中是一个docker守护进程在它们上面运行。如果您可以节省额外的几个字节,那么可以选择一个慷慨的默认值,比如1 Gibibyte(不包括kubelet)。在kubelets标志中指定
    --system reserved=1Gi
    --kube reserved=100Mi
    ,然后重新启动。这将在确定一个节点上可以运行多少POD时,将这些保留资源添加到Kubernetes Scheduler的计算中。有关更多信息,请参阅

    通过这种方式,在一个具有8GB RAM的节点上可能会安排5到7个POD,具体取决于上面选择的和测量的值。它们将保证内存要求中指定的RAM,并且不会终止。通过
    已分配资源
    下的
    kubectl descripe节点
    验证内存使用情况。至于美观性/灵活性,如果希望增加应用程序可用的RAM,只需调整内存需求和JVM堆大小


    这种方法只在假设pods内存使用不会爆炸的情况下起作用,如果它不受JVM的限制,则胭脂盒可能会导致逐出,请参阅

    谢谢。我将看一看Prometheus JMX exporterif set MaxRAMFraction=1,然后它将消耗容器的所有可用内存,关于元空间(非堆内存),它也会消耗一些内存,因此也需要为它分配一些内存。同时运行kubectl描述吊舱并查看“最后状态”。@PawanKamboj。我想你的意思是说这个评论是对这个问题的评论。正确,那么我如何优雅地分配更多内存,而不仅仅是进行试错测试呢?是的,评论是有问题的,建议设置pods内存,将堆空间额外限制为几MB,并设置-XX:MaxMetaspaceSize,这在java8中是新的,并且