您的位置:首页 > 编程语言 > Java开发

Kubernetes_pod_javajdk_动态JVM堆内存大小限制

2021-04-10 15:05 811 查看

Kubernetes_pod_javajdk_动态JVM堆内存大小限制

前言

       在Kubernetes集群中,使用容器镜像封装一个建议java jdk作为应用运行环境,并且很多时候通过yaml资源声明,如希望通过Kubernetes调度一个拥有12核CPU,96G内存的宿主机节点上,yaml资源声明对内存或CPU进行java 容器资源限制(容器运行在Kubernetes集群中时,最低能拥有0.5核CPU,500M内存,最高不超过1核CPU,1024M内存)。为什么要这么设置呢?

示例yaml片段:

             - name: test

        image: test.com/test/test:test01

        resources:

          limits:

            cpu: 1000m

            memory: 1024Mi

          requests:

            cpu: 500m

            memory: 500Mi

由于Docker容器本质是是宿主机上的一个进程,它与宿主机共享一个/proc目录,也就是说我们在容器内看到的/proc/meminfo,/proc/cpuinfo与直接在宿主机上看到的一致。也即,此容器认为自己的系统资源同样是12核CPU,96G内存。默认情况下,JVM的Max Heap Size是系统内存的1/4,也就是24G作为最大JVM堆内存出现了运行的容器感知的资源,与Kubernetes分配给它的资源的偏差,这种偏差可能会导致Java应用不断的重启。

如何解决呢?随着JAVA版本不同,有不同处理方法,如下图JVM参数进化史(图片来自网络):

       JDK8版本低于131,在容器启动JAVA程序时,自行定义好与资源配额一致的-Xmx等参数。随着JDK版本的升级,容器感知的功能就内置于Java应用中了,java 8u131+和java 9+版本,需要开启2个参数:-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap ;如果JDK8的版本高于191,可以使用MaxRAMPercentage这种更智能化的参数来解决。以下介绍2个通用方法!

 

一、JDK 版本java 8u131+和java 9+

在容器设置jvm 环境变量 ,同时启用-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 参数。

在Dokerfile文件,设定环境变量,示例片段:

ENV JAVA_OPTS -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1

 

注:-XX:MaxRAMFraction默认值为4,JVM会分配大约25%的最大RAM,对于仅由单个JVM进程组成的部署,仅使用配额的25%似乎是浪费。因此,现在人们开始设置-XX:MaxRAMFraction=1,因此从理论上讲,JVM可以使用MaxRAM的100%。但是JVM或其他东西(如远程Shell或进程外任务)没有太多可用空间。所以一般结合k8s 内存或CPU的限制(获取k8s yaml资源声明系统资源文件 /sys/fs/cgroup/memory/memory.limit_in_bytes)来更灵活调整。使用shell启动脚本,通过计算,如:获取memory.limit_in_bytes指乘以百分比(一般为75%或80%较合适,冗余20%-25%容器其他),作为-xms,-xmx jvm 堆内存启动参数。

参考示例start.sh如下:

#!/bin/sh

# 无限制内存大小(字节/MB)

unlimitM=9223372036854771712/1048576

                                                                                                                                    

# 获取k8s 限制内存设置值.                                                                                     

limitM=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)

if [ $limitM -eq $unlimitM ]

then

  #设置默认大小 512M

  limitM=536870912/1048576  

fi

 

# limitM限制内存大小乘以0.75,作为jvm 启动参数                                                                                  

limitUM= $limitM *(75/100)                                                                                                 

test_cmd="xjava -Xms${ limitUM }m -Xmx${ limitUM }m"  

                                                                               

for v in $@                                                                                                                                             

do

  #判断参数大于4个的情况

  if [ ${#v} -ge 4 ]

  then

    if [ "${v:0:4}" == "-Xmx" || "${v:0:4}" == "-Xms" ]

    then

      continue

    else

      test_cmd ="$ test_cmd $v"

    fi

  else                                                                                                                                                   

      test_cmd ="$ test_cmd $v"

  fi

done

exec $ test_cmd

注:通过获取k8s 限制容器内存或CPU大小,通过脚本计算替换xms,xmx 参数,较灵活设定JVM 大小。

 

二、JDK 版本java 8u191+

jdk1.8.191 +JVM内存参数新增了MaxRAMPercentage、InitialRAMPercentage、MinRAMPercentage,为适配Docker容器并标记为deprecated。Docker容器模式下,我们可以给每个JVM实例所属的POD分配任意大小的内存上限。如上POD为1G内存,通用的启动脚本中指定80%(-XX:MaxRAMPercentage=80.0 -XX:InitialRAMPercentage=80.0 -XX:MinRAMPercentage=80.0)。那么服务就相当于设置了-Xmx819m -Xms819m。


示例制作Dockerfile集成阿里云arthas java分析工具、测试app.jar应用jar:

FROM openjdk:8-jre-alpine

 

RUN echo "https://mirrors.aliyun.com/alpine/v3.10/main/" > /etc/apk/repositories \

    && echo "https://mirrors.aliyun.com/alpine/v3.10/community/" >> /etc/apk/repositories \

    && apk update upgrade \

    && apk add --no-cache procps unzip curl bash tzdata \

    && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \

    && echo "Asia/Shanghai" > /etc/timezone

ENV TERM=xterm

ENV.UTF-8

RUN mkdir -p /usr/local/apps

ADD ./app.jar /usr/local/apps/

WORKDIR /usr/local

RUN apk add --update ttf-dejavu && rm -rf /var/cache/apk/*

WORKDIR /usr/local

EXPOSE 80

 

ENTRYPOINT ["/sbin/tini", "--"]

CMD ["sh","-c","java -XX:MaxRAMPercentage=80.0 -XX:InitialRAMPercentage=80.0 -XX:MinRAMPercentage=80.0 -jar /usr/local/apps/app.jar"]

 

制作成容器镜像后,通过k8s 发布部署,依然POD限制资源为1核、1G,启动进入容器通过java 命令查看,设定是否生效:

#Kubectl exec -it test-apps sh -n default

##java 版本

/usr/local # java -version

openjdk version "1.8.0_212"

OpenJDK Runtime Environment (IcedTea 3.12.0) (Alpine 8.212.04-r0)

OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)

 

# jinfo 查看参数值

/usr/local #jinfo -flag MaxRAMPercentage 7

-XX:MaxRAMPercentage=80.000000

 

#启动 java -jar arthas-boot.jar xx(pid)

[arthas@7]$ jvm

 RUNTIME                                                                       

 MEMORY                                                                         

--------------------------------------------------------------------------------

 HEAP-MEMORY-USAGE      init : 859832320(820.0 MiB)                            

 [memory in bytes]      used : 95071040(90.7 MiB)                               

                        committed : 831193088(792.7 MiB)                       

                        max : 831193088(792.7 MiB)                              

                                                                               

 NO-HEAP-MEMORY-USAGE   init : 2555904(2.4 MiB)                                

 [memory in bytes]      used : 145747616(139.0 MiB)                            

                        committed : 150364160(143.4 MiB)                       

                        max : -1(-1 B)                                         

                                                                               

 PENDING-FINALIZE-COUN  0    

 

注:从arthas工具,可以看到内存 max 792.7MiB 跟预期结果一致。

第二方法比第一种方法更加灵活!


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: