K8S(18)容器環境下資源限制與jvm記憶體回收

發表於2021-04-30

K8S(18)容器環境下資源限制與jvm記憶體回收

一、k8s中的java資源限制與可能的問題

與以前單機跑單服務的情況相比,在k8s、docker容器化環境下的宿主機記憶體、cpu相對更大,所以當執行java類程式的時候,就必然有必要對容器進行記憶體限制,否則以java預設引數啟動,一個程式就可能吃掉你四分之一的記憶體

但是怎麼限制,就是一個值得考慮的問題了。

方案1:通過JVM的Xms和Xmx引數限制

java -Xms2048m -Xmx2048m -jar register.jar

在非容器化時,最常規的方案;在容器化中,也經常使用向容器傳遞環境變數的方式,通過控制這兩個引數來控制服務的記憶體使用量

在網上通常的jvm調優方案中,也通常會要求把這兩個引數設定為相等(可認為屬於調優共識),來較小FGC的頻率和提高效能

但是在容器化情況下(假設xmx=xms=4G),存在的問題是:

A 記憶體不釋放問題:

  1. java容器執行後,會慢慢的用到設定的記憶體上限4G
  2. 由於還有其他的開銷,一般會達到6G左右,並且不會釋放
  3. 你會發現你宿主機或叢集的記憶體,只升高不降低,除非你重啟容器

B 引數設定多少的問題:

  1. 由於給它設定的記憶體,它總會用完,不好確定到底給它多少記憶體
  2. 給多了不夠用,給少了GC頻繁
  3. 有些人要說GC調優確定最合適記憶體,但這個因素不可控

所以這樣,宿主機或叢集的總記憶體,必須得大於你設定的所有容器的記憶體上限總和才保險

方案2:通過容器的requests和limits引數控制

docker容器和k8s都可以通過引數限制分配給容器的記憶體大小,同樣假設分配了4G,問題會是

  1. 容器資源隔離的原因,容器中看到的記憶體大小=宿主機的記憶體大小
  2. 容器中的java服務,不知道自己被限制了記憶體,會按預設的1/4申請記憶體
  3. 由於對容器進行了記憶體限制,所以極大概率該容器執行一段時間後會被重啟

方案3:容器引數和JVM引數共用

較可行的方案是兩邊的引數一起使用,只是要注意容器限制的記憶體要比jvm設定的大(1-2)G,因為前面說了JVM設定的4G,實際可能會用到6G。

但是共用也沒有徹底解決第一個方案中的問題,即:

  1. 給java服務到底分配多少記憶體
  2. 申請的記憶體不釋放,導致宿主機記憶體總量上限問題
  3. 多個地方設定記憶體引數,麻煩

二、解決問題三板斧

主要參考的三個部落格
參考1:https://www.cnblogs.com/xiaoqi/p/container-jvm.html
參考2:https://www.imooc.com/article/292785?block_id=tuijian_wz
參考3:https://blog.csdn.net/qq_40378034/article/details/110677269

1)採用JDK8-191以上的jdk版本

由於jdk8-191以前的jdk版本,是不能感知到自己是在容器中執行的,所以對容器設定的資源限制,java程式不能感知,因此會按宿主機的總記憶體來申請資源

2)使用JVM新的資源限制引數且不相等

java -XX:MaxRAMPercentage=90.0 -XX:MinRAMPercentage=60.0 -jar register.jar

[MAX|MIN]RAMPercentage引數,是191版本後新增加的引數,該引數可以感知容器的資源限制,並以此限制為申請的最大最小資源量,即Xms和Xmx

由參考文章3中的結論:Xms和Xmx不相等時,在可能的情況下,GC過程會慢慢釋放記憶體回作業系統,而我們 設定的[MAX|MIN]RAMPercentage這兩個引數不等,達到的結果就是Xms和Xmx不相等

# 此命令可以在容器中驗證,java服務按此規則申請時,會申請的記憶體大小
java -XX:+PrintFlagsFinal -XX:MaxRAMPercentage=90.0 -XX:MinRAMPercentage=60.0 -version | grep -Ei "heapsize|rampercentage"

3)配置容器的資源限制引數

在前兩條的基礎上,最後在配合容器的資源限制功能後,就能做到

  1. 限制容器的記憶體上限
  2. 容器內的jvm服務,能根據資源限制來申請記憶體
  3. 由於jvm會釋放記憶體,所以可先設一個較大值,經過長期觀察,可以確定該服務的常規記憶體水平
  4. 最終可以實現合理設定了容器、宿主機、java程式三者的記憶體的目的

相關文章