日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Java和Docker限制的那些事兒

Java和Docker不是天然的朋友。 Docker可以設(shè)置內(nèi)存和CPU限制,而Java不能自動檢測到。使用Java的Xmx標識(繁瑣/重復)或新的實驗性JVM標識,我們可以解決這個問題。

虛擬化中的不匹配

Java和Docker的結(jié)合并不是完美匹配的,最初的時候離完美匹配有相當大的距離。對于初學者來說,JVM的全部設(shè)想就是,虛擬機可以讓程序與底層硬件無關(guān)。

那么,把我們的Java應(yīng)用打包到JVM中,然后整個再塞進Docker容器中,能給我們帶來什么好處呢?大多數(shù)情況下,你只是在復制JVMs和Linux容器,除了浪費更多的內(nèi)存,沒任何好處。感覺這樣子挺傻的。

不過,Docker可以把你的程序,設(shè)置,特定的JDK,Linux設(shè)置和應(yīng)用服務(wù)器,還有其他工具打包在一起,當做一個東西。站在DevOps/Cloud的角度來看,這樣一個完整的容器有著更高層次的封裝。

問題一:內(nèi)存

時至今日,絕大多數(shù)產(chǎn)品級應(yīng)用仍然在使用Java 8(或者更舊的版本),而這可能會帶來問題。Java 8(update 131之前的版本)跟Docker無法很好地一起工作。問題是在你的機器上,JVM的可用內(nèi)存和CPU數(shù)量并不是Docker允許你使用的可用內(nèi)存和CPU數(shù)量。

比如,如果你限制了你的Docker容器只能使用100MB內(nèi)存,但是呢,舊版本的Java并不能識別這個限制。Java看不到這個限制。JVM會要求更多內(nèi)存,而且遠超這個限制。如果使用太多內(nèi)存,Docker將采取行動并殺死容器內(nèi)的進程!JAVA進程被干掉了,很明顯,這并不是我們想要的。

為了解決這個問題,你需要給Java指定一個最大內(nèi)存限制。在舊版本的Java(8u131之前),你需要在容器中通過設(shè)置-Xmx來限制堆大小。這感覺不太對,你可不想定義這些限制兩次,也不太想在你的容器中來定義。

幸運的是我們現(xiàn)在有了更好的方式來解決這個問題。從Java 9之后(8u131+),JVM增加了如下標志:

 
 
 
  1. -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap 

這些標志強制JVM檢查Linux的cgroup配置,Docker是通過cgroup來實現(xiàn)最大內(nèi)存設(shè)置的。現(xiàn)在,如果你的應(yīng)用到達了Docker設(shè)置的限制(比如500MB),JVM是可以看到這個限制的。JVM將會嘗試GC操作。如果仍然超過內(nèi)存限制,JVM就會做它該做的事情,拋出OutOfMemoryException。也就是說,JVM能夠看到Docker的這些設(shè)置。

從Java 10之后(參考下面的測試),這些體驗標志位是默認開啟的,也可以使用-XX:+UseContainerSupport來使能(你可以通過設(shè)置-XX:-UseContainerSupport來禁止這些行為)。

問題二:CPU

第二個問題是類似的,但它與CPU有關(guān)。簡而言之,JVM將查看硬件并檢測CPU的數(shù)量。它會優(yōu)化你的runtime以使用這些CPUs。但是同樣的情況,這里還有另一個不匹配,Docker可能不允許你使用所有這些CPUs??上У氖?,這在Java 8或Java 9中并沒有修復,但是在Java 10中得到了解決。

從Java 10開始,可用的CPUs的計算將采用以不同的方式(默認情況下)解決此問題(同樣是通過UseContainerSupport)。

Java和Docker的內(nèi)存處理測試

作為一個有趣的練習,讓我們驗證并測試Docker如何使用幾個不同的JVM版本/標志甚至不同的JVM來處理內(nèi)存不足。

首先,我們創(chuàng)建一個測試應(yīng)用程序,它只是簡單地“吃”內(nèi)存并且不釋放它。

 
 
 
  1. java 
  2. import java.util.ArrayList; 
  3. import java.util.List; 
  4. public class MemEat { 
  5.     public static void main(String[] args) { 
  6.         List l = new ArrayList<>(); 
  7.         while (true) { 
  8.             byte b[] = new byte[1048576]; 
  9.             l.add(b); 
  10.             Runtime rt = Runtime.getRuntime(); 
  11.             System.out.println( "free memory: " + rt.freeMemory() ); 
  12.         } 
  13.     } 

我們可以啟動Docker容器并運行這個應(yīng)用程序來查看會發(fā)生什么。

測試一:Java 8u111

首先,我們將從具有舊版本Java 8的容器開始(update 111)。

 
 
 
  1. shell 
  2. docker run -m 100m -it java:openjdk-8u111 /bin/bash 

我們編譯并運行MemEat.java文件:

 
 
 
  1. shell 
  2. javac MemEat.java 
  3. java MemEat 
  4. ... 
  5. free memory: 67194416 
  6. free memory: 66145824 
  7. free memory: 65097232 
  8. Killed 

正如所料,Docker已經(jīng)殺死了我們的Java進程。不是我們想要的(?。?。你也可以看到輸出,Java認為它仍然有大量的內(nèi)存需要分配。

我們可以通過使用-Xmx標志為Java提供最大內(nèi)存來解決此問題:

 
 
 
  1. shell 
  2. javac MemEat.java 
  3. java -Xmx100m MemEat 
  4. ... 
  5. free memory: 1155664 
  6. free memory: 1679936 
  7. free memory: 2204208 
  8. free memory: 1315752 
  9. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
  10.     at MemEat.main(MemEat.java:8) 

在提供了我們自己的內(nèi)存限制之后,進程正常停止,JVM理解它正在運行的限制。然而,問題在于你現(xiàn)在將這些內(nèi)存限制設(shè)置了兩次,Docker一次,JVM一次。

測試二:Java 8u144

如前所述,隨著增加新標志來修復問題,JVM現(xiàn)在可以遵循Docker所提供的設(shè)置。我們可以使用版本新一點的JVM來測試它。

 
 
 
  1. shell 
  2. docker run -m 100m -it adoptopenjdk/openjdk8 /bin/bash 

(在撰寫本文時,此OpenJDK Java鏡像的版本是Java 8u144)

接下來,我們再次編譯并運行MemEat.java文件,不帶任何標志:

 
 
 
  1. shell 
  2. javac MemEat.java 
  3. java MemEat 
  4. ... 
  5. free memory: 67194416 
  6. free memory: 66145824 
  7. free memory: 65097232 
  8. Killed 

依然存在同樣的問題。但是我們現(xiàn)在可以提供上面提到的實驗性標志來試試看:

 
 
 
  1. shell 
  2. javac MemEat.java 
  3. java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap MemEat 
  4. ... 
  5. free memory: 1679936 
  6. free memory: 2204208 
  7. free memory: 1155616 
  8. free memory: 1155600 
  9. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
  10. at MemEat.main(MemEat.java:8) 

這一次我們沒有告訴JVM限制的是什么,我們只是告訴JVM去檢查正確的限制設(shè)置!現(xiàn)在感覺好多了。

測試三:Java 10u23

有些人在評論和Reddit上提到Java 10通過使實驗標志成為新的默認值來解決所有問題。這種行為可以通過禁用此標志來關(guān)閉:-XX:-UseContainerSupport。

當我測試它時,它最初不起作用。在撰寫本文時,AdoptAJDK OpenJDK10鏡像與jdk-10+23一起打包。這個JVM顯然還是不理解UseContainerSupport標志,該進程仍然被Docker殺死。

 
 
 
  1. shell 
  2. javac MemEat.java 
  3. java MemEat 
  4. ... 
  5. free memory: 96262112 
  6. free memory: 94164960 
  7. free memory: 92067808 
  8. free memory: 89970656 
  9. Killed 
  10. java -XX:+UseContainerSupport MemEat 
  11. Unrecognized VM option 'UseContainerSupport' 
  12. Error: Could not create the Java Virtual Machine. 
  13. Error: A fatal exception has occurred. Program will exit. 

測試四:Java 10u46(Nightly)

我決定嘗試AdoptAJDK OpenJDK 10的最新nightly構(gòu)建。它包含的版本是Java 10+46,而不是Java 10+23。

 
 
 
  1. shell 
  2. docker run -m 100m -it adoptopenjdk/openjdk10:nightly /bin/bash 

然而,在這個ngithly構(gòu)建中有一個問題,導出的PATH指向舊的Java 10+23目錄,而不是10+46,我們需要修復這個問題。

 
 
 
  1. shell 
  2. export PATH=$PATH:/opt/java/openjdk/jdk-10+46/bin/ 
  3. javac MemEat.java 
  4. java MemEat 
  5. ... 
  6. free memory: 3566824 
  7. free memory: 2796008 
  8. free memory: 1480320 
  9. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
  10.   at MemEat.main(MemEat.java:8) 

成功!不提供任何標志,Java 10依然可以正確檢測到Dockers內(nèi)存限制。

測試五:OpenJ9

我最近也在試用OpenJ9,這個免費的替代JVM已經(jīng)從IBM J9開源,現(xiàn)在由Eclipse維護。

請在我的下一篇博文(http://royvanrijn.com/blog/2018/05/openj9-jvm-shootout/)中閱讀關(guān)于OpenJ9的更多信息。

它運行速度快,內(nèi)存管理非常好,性能卓越,經(jīng)??梢詾槲覀兊奈⒎?wù)節(jié)省多達30-50%的內(nèi)存。這幾乎可以將Spring Boot應(yīng)用程序定義為’micro’了,其運行時間只有100-200mb,而不是300mb+。我打算盡快就此寫一篇關(guān)于這方面的文章。

但令我驚訝的是,OpenJ9還沒有類似于Java 8/9/10+中針對cgroup內(nèi)存限制的標志(backported)的選項。如果我們將以前的測試用例應(yīng)用到最新的AdoptAJDK OpenJDK 9 + OpenJ9 build:

 
 
 
  1. shell 
  2. docker run -m 100m -it adoptopenjdk/openjdk9-openj9 /bin/bash 

我們添加OpenJDK標志(OpenJ9會忽略的標志):

 
 
 
  1. shell 
  2. java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap MemEat 
  3. ... 
  4. free memory: 83988984 
  5. free memory: 82940400 
  6. free memory: 81891816 
  7. Killed 

Oops,JVM再次被Docker殺死。

我真的希望類似的選項將很快添加到OpenJ9中,因為我希望在生產(chǎn)環(huán)境中運行這個選項,而不必指定最大內(nèi)存兩次。 Eclipse/IBM正在努力修復這個問題,已經(jīng)提了issues,甚至已經(jīng)針對issues提交了PR。

更新:(不推薦Hack)

一個稍微丑陋/hacky的方式來解決這個問題是使用下面的組合標志:

 
 
 
  1. shell 
  2. java -Xmx`cat /sys/fs/cgroup/memory/memory.limit_in_bytes` MemEat 
  3. ... 
  4. free memory: 3171536 
  5. free memory: 2127048 
  6. free memory: 2397632 
  7. free memory: 1344952 
  8. JVMDUMP039I Processing dump event "systhrow", detail "java/lang/OutOfMemoryError" at 2018/05/15 14:04:26 - please wait. 
  9. JVMDUMP032I JVM requested System dump using '//core.20180515.140426.125.0001.dmp' in response to an event 
  10. JVMDUMP010I System dump written to //core.20180515.140426.125.0001.dmp 
  11. JVMDUMP032I JVM requested Heap dump using '//heapdump.20180515.140426.125.0002.phd' in response to an event 
  12. JVMDUMP010I Heap dump written to //heapdump.20180515.140426.125.0002.phd 
  13. JVMDUMP032I JVM requested Java dump using '//javacore.20180515.140426.125.0003.txt' in response to an event 
  14. JVMDUMP010I Java dump written to //javacore.20180515.140426.125.0003.txt 
  15. JVMDUMP032I JVM requested Snap dump using '//Snap.20180515.140426.125.0004.trc' in response to an event 
  16. JVMDUMP010I Snap dump written to //Snap.20180515.140426.125.0004.trc 
  17. JVMDUMP013I Processed dump event "systhrow", detail "java/lang/OutOfMemoryError". 
  18. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
  19.   at MemEat.main(MemEat.java:8) 

在這種情況下,堆大小受限于分配給Docker實例的內(nèi)存,這適用于較舊的JVM和OpenJ9。這當然是錯誤的,因為容器本身和堆外的JVM的其他部分也使用內(nèi)存。但它似乎工作,顯然Docker在這種情況下是寬松的。也許某些bash大神會做出更好的版本,從其他進程的字節(jié)中減去一部分。

無論如何,不要這樣做,它可能無法正常工作。

測試六:OpenJ9(Nightly)

有人建議使用OpenJ9的最新nightly版本。

 
 
 
  1. shell 
  2. docker run -m 100m -it adoptopenjdk/openjdk9-openj9:nightly /bin/bash 

最新的OpenJ9夜間版本,它有兩個東西:

另一個有問題的PATH參數(shù),需要先解決這個問題

JVM支持新標志UseContainerSupport(就像Java 10一樣)

 
 
 
  1. shell 
  2. export PATH=$PATH:/opt/java/openjdk/jdk-9.0.4+12/bin/ 
  3. javac MemEat.java 
  4. java -XX:+UseContainerSupport MemEat 
  5. ... 
  6. free memory: 5864464 
  7. free memory: 4815880 
  8. free memory: 3443712 
  9. free memory: 2391032 
  10. JVMDUMP039I Processing dump event "systhrow", detail "java/lang/OutOfMemoryError" at 2018/05/15 21:32:07 - please wait. 
  11. JVMDUMP032I JVM requested System dump using '//core.20180515.213207.62.0001.dmp' in response to an event 
  12. JVMDUMP010I System dump written to //core.20180515.213207.62.0001.dmp 
  13. JVMDUMP032I JVM requested Heap dump using '//heapdump.20180515.213207.62.0002.phd' in response to an event 
  14. JVMDUMP010I Heap dump written to //heapdump.20180515.213207.62.0002.phd 
  15. JVMDUMP032I JVM requested Java dump using '//javacore.20180515.213207.62.0003.txt' in response to an event 
  16. JVMDUMP010I Java dump written to //javacore.20180515.213207.62.0003.txt 
  17. JVMDUMP032I JVM requested Snap dump using '//Snap.20180515.213207.62.0004.trc' in response to an event 
  18. JVMDUMP010I Snap dump written to //Snap.20180515.213207.62.0004.trc 
  19. JVMDUMP013I Processed dump event "systhrow", detail "java/lang/OutOfMemoryError". 
  20. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 

TADAAA,正在修復中!

奇怪的是,這個標志在OpenJ9中默認沒有啟用,就像它在Java 10中一樣。再說一次:確保你測試了這是你想在一個Docker容器中運行Java。

結(jié)論

簡言之:注意資源限制的不匹配。測試你的內(nèi)存設(shè)置和JVM標志,不要假設(shè)任何東西。

如果您在Docker容器中運行Java,請確保你設(shè)置了Docker內(nèi)存限制和在JVM中也做了限制,或者你的JVM能夠理解這些限制。

如果您無法升級您的Java版本,請使用-Xmx設(shè)置您自己的限制。

對于Java 8和Java 9,請更新到最新版本并使用:

 
 
 
  1. -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap 

對于Java 10,確保它支持’UseContainerSupport’(更新到最新版本)。

對于OpenJ9(我強烈建議使用,可以在生產(chǎn)環(huán)境中有效減少內(nèi)存占用量),現(xiàn)在使用-Xmx設(shè)置限制,但很快會出現(xiàn)一個支持UseContainerSupport標志的版本。


本文名稱:Java和Docker限制的那些事兒
標題網(wǎng)址:http://www.5511xx.com/article/djijspp.html