Kubernetes_pod_javajdk_動態JVM堆內存大小限制

Kubernetes_pod_javajdk_動態JVM堆內存大小限制java

前言shell

       在Kubernetes集羣中,使用容器鏡像封裝一個建議java jdk做爲應用運行環境,而且不少時候經過yaml資源聲明,如但願經過Kubernetes調度一個擁有12核CPU,96G內存的宿主機節點上,yaml資源聲明對內存或CPU進行java 容器資源限制(容器運行在Kubernetes集羣中時,最低能擁有0.5核CPU,500M內存,最高不超過1核CPU,1024M內存)。爲何要這麼設置呢?bash

示例yaml片斷:網絡

             - name: testapp

        image: test.com/test/test:test01curl

        resources:jvm

          limits:ide

            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參數進化史(圖片來自網絡):

image.png

       JDK8版本低於131,在容器啓動JAVA程序時,自行定義好與資源配額一致的-Xmx等參數。隨着JDK版本的升級,容器感知的功能就內置於Java應用中了,java 8u131+和java 9+版本,須要開啓2個參數:-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap ;若是JDK8的版本高於191,可使用MaxRAMPercentage這種更智能化的參數來解決。如下介紹2個通用方法!

 

1、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 大小。

 

2、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 跟預期結果一致。

第二方法比第一種方法更加靈活!

相關文章
相關標籤/搜索