java对docker_Java和Docker限制问题
問題一:內存
時至今日,絕大多數產品級應用仍然在使用Java 8(或者更舊的版本),而這可能會帶來問題。Java 8(update 131之前的版本)跟docker無法很好地一起工作。問題是在你的機器上,JVM的可用內存和CPU數量并不是Docker允許你使用的可用內存和CPU數量。
比如,如果你限制了你的Docker容器只能使用100MB內存,但是呢,舊版本的Java并不能識別這個限制。Java看不到這個限制。JVM會要求更多內存,而且遠超這個限制。如果使用太多內存,Docker將采取行動并殺死容器內的進程!JAVA進程被干掉了,很明顯,這并不是我們想要的。
為了解決這個問題,你需要給Java指定一個最大內存限制。在舊版本的Java(8u131之前),你需要在容器中通過設置-Xmx來限制堆大小。這感覺不太對,你可不想定義這些限制兩次,也不太想在你的容器中來定義。
幸運的是我們現在有了更好的方式來解決這個問題。從Java 9之后(8u131+),JVM增加了如下標志:-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
這些標志強制JVM檢查Linux的cgroup配置,Docker是通過cgroup來實現最大內存設置的。現在,如果你的應用到達了Docker設置的限制(比如500MB),JVM是可以看到這個限制的。JVM將會嘗試GC操作。如果仍然超過內存限制,JVM就會做它該做的事情,拋出OutOfMemoryException。也就是說,JVM能夠看到Docker的這些設置。
從Java 10之后(參考下面的測試),這些體驗標志位是默認開啟的,也可以使用-XX:+UseContainerSupport來使能(你可以通過設置-XX:-UseContainerSupport來禁止這些行為)。
問題二:CPU
第二個問題是類似的,但它與CPU有關。簡而言之,JVM將查看硬件并檢測CPU的數量。它會優化你的runtime以使用這些CPUs。但是同樣的情況,這里還有另一個不匹配,Docker可能不允許你使用所有這些CPUs。可惜的是,這在Java 8或Java 9中并沒有修復,但是在Java 10中得到了解決。
從Java 10開始,可用的CPUs的計算將采用以不同的方式(默認情況下)解決此問題(同樣是通過UseContainerSupport)。
Java和Docker的內存處理測試
作為一個有趣的練習,讓我們驗證并測試Docker如何使用幾個不同的JVM版本/標志甚至不同的JVM來處理內存不足。
首先,我們創建一個測試應用程序,它只是簡單地“吃”內存并且不釋放它。javaimport?java.util.ArrayList;
import?java.util.List;
public?class?MemEat?{
public?static?void?main(String[]?args)?{
List?l?=?new?ArrayList<>();
while?(true)?{
byte?b[]?=?new?byte[1048576];
l.add(b);
Runtime?rt?=?Runtime.getRuntime();
System.out.println(?"free?memory:?"?+?rt.freeMemory()?);
}
}
}
我們可以啟動Docker容器并運行這個應用程序來查看會發生什么。
測試一:Java 8u111
首先,我們將從具有舊版本Java 8的容器開始(update 111)。docker?run?-m?100m?-it?java:openjdk-8u111?/bin/bash
我們編譯并運行MemEat.java文件:javac?MemEat.java
java?MemEat...
free?memory:?67194416
free?memory:?66145824
free?memory:?65097232
Killed
正如所料,Docker已經殺死了我們的Java進程。不是我們想要的(!)。你也可以看到輸出,Java認為它仍然有大量的內存需要分配。
我們可以通過使用-Xmx標志為Java提供最大內存來解決此問題:javac?MemEat.java
java?-Xmx100m?MemEat...
free?memory:?1155664
free?memory:?1679936
free?memory:?2204208
free?memory:?1315752
Exception?in?thread?"main"
java.lang.OutOfMemoryError:?Java?heap?space
at?MemEat.main(MemEat.java:8)
在提供了我們自己的內存限制之后,進程正常停止,JVM理解它正在運行的限制。然而,問題在于你現在將這些內存限制設置了兩次,Docker一次,JVM一次。
測試二:OpenJ9
我最近也在試用OpenJ9,這個免費的替代JVM已經從IBM J9開源,現在由Eclipse維護。 它運行速度快,內存管理非常好,性能卓越,經常可以為我們的微服務節省多達30-50%的內存。這幾乎可以將Spring Boot應用程序定義為’micro’了,其運行時間只有100-200mb,而不是300mb+。我打算盡快就此寫一篇關于這方面的文章。 但令我驚訝的是,OpenJ9還沒有類似于Java 8/9/10+中針對cgroup內存限制的標志(backported)的選項。如果我們將以前的測試用例應用到最新的AdoptAJDK OpenJDK 9 + OpenJ9 build:docker?run?-m?100m?-it?adoptopenjdk/openjdk9-openj9?/bin/bash
我們添加OpenJDK標志(OpenJ9會忽略的標志):java?-XX:+UnlockExperimentalVMOptions?-XX:+UseCGroupMemoryLimitForHeap
MemEat...
free?memory:?83988984
free?memory:?82940400
free?memory:?81891816
Killed
Oops,JVM再次被Docker殺死。 我真的希望類似的選項將很快添加到OpenJ9中,因為我希望在生產環境中運行這個選項,而不必指定最大內存兩次。 Eclipse/IBM正在努力修復這個問題,已經提了issues,甚至已經針對issues提交了PR。
測試三:OpenJ9(Nightly)
有人建議使用OpenJ9的最新nightly版本。docker?run?-m?100m?-it?adoptopenjdk/openjdk9-openj9:nightly?/bin/bash
最新的OpenJ9夜間版本,它有兩個東西: 另一個有問題的PATH參數,需要先解決這個問題 JVM支持新標志UseContainerSupport(就像Java 10一樣)export?PATH=$PATH:/opt/java/openjdk/jdk-9.0.4+12/bin/javac?MemEat.java
java?-XX:+UseContainerSupport
MemEat...
free?memory:?5864464
free?memory:?4815880
free?memory:?3443712
free?memory:?2391032
JVMDUMP039I?Processing?dump?event?"systhrow",?detail?"java/lang/OutOfMemoryError"
at?2018/05/15?21:32:07?-?please?wait.JVMDUMP032I
JVM?requested?System?dump?using?'//core.20180515.213207.62.0001.dmp'?in?response?to?an?eventJVMDUMP010I
System?dump?written?to?//core.20180515.213207.62.0001.dmpJVMDUMP032I
JVM?requested?Heap?dump?using?'//heapdump.20180515.213207.62.0002.phd'?in?response?to?an?eventJVMDUMP010I
Heap?dump?written?to?//heapdump.20180515.213207.62.0002.phdJVMDUMP032I
JVM?requested?Java?dump?using?'//javacore.20180515.213207.62.0003.txt'?in?response?to?an?eventJVMDUMP010I
Java?dump?written?to?//javacore.20180515.213207.62.0003.txtJVMDUMP032I
JVM?requested?Snap?dump?using?'//Snap.20180515.213207.62.0004.trc'?in?response?to?an?eventJVMDUMP010I
Snap?dump?written?to?//Snap.20180515.213207.62.0004.trcJVMDUMP013I
Processed?dump?event?"systhrow",?detail?"java/lang/OutOfMemoryError".
Exception?in?thread?"main"?java.lang.OutOfMemoryError:?Java?heap?space
TADAAA,正在修復中! 奇怪的是,這個標志在OpenJ9中默認沒有啟用,就像它在Java 10中一樣。再說一次:確保你測試了這是你想在一個Docker容器中運行Java。 結論 簡言之:注意資源限制的不匹配。測試你的內存設置和JVM標志,不要假設任何東西。 如果您在Docker容器中運行Java,請確保你設置了Docker內存限制和在JVM中也做了限制,或者你的JVM能夠理解這些限制。 如果您無法升級您的Java版本,請使用-Xmx設置您自己的限制。 對于Java 8和Java 9,請更新到最新版本并使用:-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
對于Java 10,確保它支持’UseContainerSupport’(更新到最新版本)。
對于OpenJ9(我強烈建議使用,可以在生產環境中有效減少內存占用量),現在使用-Xmx設置限制,但很快會出現一個支持UseContainerSupport標志的版本。
總結
以上是生活随笔為你收集整理的java对docker_Java和Docker限制问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 事务操作_Python实现
- 下一篇: python海贼王logo_Python