容器中的Java與內存限制:LXC、Docker與OpenVZ
譯文開發者在使用JVM、內存與Docker時是否遭遇到難題?今天我們將一同利用Jelastic將其解決。最近Matt Willaims提起的Docker內Java及其內存限制的議題得到了廣泛關注,而這些在容器使用當中無法避免的狀況也在Twitter上引發一系列討論。
那么在今天的文章中,我們就來共同分析這一問題并嘗試找出解決辦法。
問題
Matt講述了他在Docker容器內使用JVM heap時的探索之旅。他指出,容器對于內存的限制未能正確顯示,這導致任意Java或其它應用都可分配給該主機設備的全部內存資源,而JVM卻無法指示其中有多少資源應該專門用于其運行所在的父容器。如此一來,OutOfMemoryError開始出現。
事實上,大多數Linux工具所提供的系統資源指標在創建時間上要早于cgroups(例如free與top,二者皆源自procps)。其通常會從proc文件系統:/proc/meminfo,/proc/vmstat, /proc/PID/smaps或者其它位置處讀取內存指標。
遺憾的是,/proc/meminfo, /proc/vmstat及其它位置無法實現容器化,也就是其無法為cgroup所識別。其只能顯示主機系統(物理或虛擬機)的整體內存容量,而現代Linux容器技術無法對其加以利用。容器中的流程無法依靠free、top及其它指標確定所能使用的內存容量;其必須遵循cgroups提供的約束條件,且無法使用主機系統中的全部內存。
事實上,實際內存限制條件的可見性非常重要,我們需要利用其實現應用優化及故障排查,例如內存泄露、交換空間使用、性能下降等等。另外,某些用例還需要利用垂直伸縮以實現容器內的資源使用優化,包括自動變更工作程序、進程或線程數量。垂直伸縮往往取決于特定容器的實際可用內存量,因此必須配合容器中的限制可見性方能起效。
解決方案
開放容器倡議配合runC能夠利用文件系統用戶空間覆蓋/proc文件。LXC創建的lxcfs文件系統允許容器擁有虛擬cgroup文件系統及虛擬/proc文件查看方式,這樣上述問題的實質就成了如何對容器進行維護。
我們也遇到過類似的問題,但在Jelastic的幫助下,我們順利為客戶解決了這一障礙。下面來看具體辦法。
首先打開Jelastic向導,為測試賬戶選定服務供應商并利用預定義內存限制創建一套Java Docker容器——例如8 cloudlets,相當于1 GB內存。
接下來前往Jelastic SSH gate(1),選定之前創建的測試環境(2),而后選定該容器(3)。現在我們已經可以利用free工具檢查可用內存了(4)。
As we can see, the memory limit equals 1GB defined before. Let’s check the top tool.
一切運行正常。為了進一步檢查,我們重復Matt之前提到的Java啟發式行為問題。
現在的MaxHeapSize = 268435546 (~256 MiB),意味著當前容器的內存容量為默認JVM heap的四分之一。
其中的秘訣是什么?當然,這是各種“成分”的***結合。在本示例中,我們將OpenVZ與Docker技術結合以提供更出色的安全與隔離控制效果,同時保證其不會影響到容器實時遷移與容器休眠等必要功能。下面,大家可以看到Docker容器在Jelastic當中的高級應用思路。
在OpenVZ中,每套容器都擁有一套/proc pseudo-filesystem虛擬化視圖。需要強調的是,容器中的/proc/meminfo為“定制”版本,能夠顯示每容器信息而非直接提取自主機。如此一來,top與free等工具在運行于容器內時,就能顯示特定容器中遵循限制條件時的內存與swap使用情況。
值得注意的是,容器中的swap并非真正的swap,而只是一種虛擬機制(因此其整體技術被稱為VSwap)。其核心思路在于,一旦啟用了VSwap的某容器超出配置內存限制,則其部分內存即會轉換為所謂swap cache。期間并不會發生真正的空間交換,意味著不存在I/O——除非整體環境下出現了全局(而非每容器)內存短缺。另外,使用VSwap的容器會通過降低運行速度的方式解決內存超標,這種方式從容器內部角度來看類似于執行真正的空間交換。這項技術能夠對每容器內存與swap使用情況加以有效控制。
當然,除了Jelastic之外,大家也可以根據Matt的提示始終為JVM指定heap大小而非依賴于啟發機制。如果各位有其它更好的思路,也請在評論中與我們分享。
原文標題:Java and Memory Limits in Containers: LXC, Docker and OpenVZ
【51CTO.com獨家譯文,合作站點轉載請注明來源。】