1. 程式人生 > >加強Docker容器與Java 10整合

加強Docker容器與Java 10整合

很多執行在Java虛擬機器(JVM)中的應用,包括資料服務如Apache Spark和Kafka以及傳統企業應用,都執行在容器中。最近,執行在容器裡的JVM出現了由於記憶體和CPU資源限制和使用率導致效能損失問題。這是因為Java意識不到自己執行在容器中。隨著Java 10的釋出,JVM終於可以通過設定cgroup來實現這些約束。記憶體和CPU約束都可以直接在容器裡被用來管理Java應用,包括:

  • 在容器裡面設定記憶體約束
  • 在容器裡設定可用的CPUs個數
  • 在容器裡設定CPU約束


Java 10的這些優化在Docker for mac/windows和Docker企業版均可正常執行。

容器記憶體限制

一直到Java 9,JVM依然不能夠通過在容器中使用標識來識別記憶體或CPU限制。在Java 10中,記憶體限制可以被自動識別,而且這個特性是預設支援的。

Java對伺服器進行分級定義,如果一臺伺服器有2CPUs和2GB記憶體,那麼預設的堆疊大小是1/4的實體記憶體。假設一個安裝了Docker企業版的伺服器有4CPUs和2GB記憶體。我們來比較分別比較執行Java 8和Java 10的容器的區別。先看Java 8:

shell
docker container run -it -m512 --entrypoint bash openjdk:latest

$ docker-java-home/bin/java -XX: PrintFlagsFinal -version | grep MaxHeapSize
uintx MaxHeapSize                              := 524288000                          {product}
openjdk version "1.8.0_162"


我們可以看到最大的堆疊大小是512MB,剛好(1/4)X2GB,只是通過Docker EE自動設定的,而不是通過設定容器來實現的。做個比較,我們看下在Java 10環境下運行同樣的命令是什麼結果。

shell
docker container run -it -m512M --entrypoint bash openjdk:10-jdk

$ docker-java-home/bin/java -XX: PrintFlagsFinal -version | grep MaxHeapSize
size_t MaxHeapSize                              = 134217728                                {product} {ergonomic}
openjdk version "10" 2018-03-20


上面的結果顯示,容器裡的記憶體限制非常接近我們期望的128MB。

設定可用CPUs個數

預設情況下,每個容器可以獲取到主機的CPU時鐘週期是沒有限制的。各種限制可以被設定來約束某個指定的容器可以獲取的主機CPU時鐘週期。

Java 10能夠識別這些限制:

shell
docker container run -it --cpus 2 openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2


所有分配給Docker EE的CPUs均獲得同樣比例的CPU時鐘週期。這個比例可以通過調整CPU共享比重(相對於其他所有執行的容器的比重)來進行修改。

這個比例只在執行CPU敏感的程序才生效。當一個容器中的任務是空閒狀態,那麼其他容器可以使用剩餘的全部CPU時間。然後真實的CPU時間統計則相當依賴系統上執行的容器的個數。這些在Java 10中都可以設定:

shell
docker container run -it --cpu-shares 2048 openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2


Java 10中也可以設定CPU集合約束(允許哪些CPU執行)。

shell
docker run -it --cpuset-cpus="1,2,3" openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 3

分配記憶體和CPU

使用Java 10,可以使用容器設定來估計部署應用程式所需的記憶體和CPU的分配。我們假設已經確定了容器中執行的每個程序的記憶體堆和CPU要求,並設定了JAVA_OPTS。例如,如果您有一個跨10個節點分佈的應用程式,五個節點需要512Mb的記憶體,每個需要1024個CPU-shares,另外五個節點需要256Mb,每個512個CPU-shares。請注意,1個CPU份額比例由1024表示。

對於記憶體,應用程式需要至少分配5Gb。
512Mb x 5 = 2.56 Gb
256Mb x 5 = 1.28 Gb

該應用程式需要8個CPU才能高效執行。
1024 x 5 = 5 CPUs
512 x 5 = 3 CPUs

最佳實踐建議分析應用程式以確定執行在JVM中的每個程序的記憶體和CPU分配。但是,在調整容器大小以防止Java應用程式中的記憶體不足錯誤以及分配足夠的CPU來處理工作負載時,Java 10消除了這些猜測。

在此我向大家推薦一個架構學習交流QQ群:725633148 裡面會分享一些資深架構師錄製的視訊錄影:有Spring,MyBatis,Netty原始碼分析,高併發、高效能、分散式、微服務架構的原理,JVM效能優化、分散式架構等這些成為架構師必備的知識體系。還能領取免費的學習資源,目前受益良多!