在日誌中,我們可以清楚地發現設定已經生效 “Command line argument: -Xmx512m”
02-Apr-2016 12:46:26.970 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version: Apache Tomcat/8.0.32
02-Apr-2016 12:46:26.974 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built: Feb 2 2016 19:34:53 UTC
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server number: 8.0.32.0
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name: Linux
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version: 4.1.19-boot2docker
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture: amd64
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home: /usr/lib/jvm/java-7-openjdk-amd64/jre
02-Apr-2016 12:46:26.976 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version: 1.7.0_95-b00
02-Apr-2016 12:46:26.976 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor: Oracle Corporation
02-Apr-2016 12:46:26.977 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE: /usr/local/tomcat
02-Apr-2016 12:46:26.977 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME: /usr/local/tomcat
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx512m
如果使用官方的Java映象,或者基於Java映象構建的Docker映象,都可以透過傳遞 JAVA_OPTS 環境變數來輕鬆地設定JVM的記憶體引數。比如,對於官方Tomcat 映象,我們可以執行下面命令來啟動一個最大記憶體為512M的tomcat例項
docker run --rm -e JAVA_OPTS="-Xmx512m" tomcat:8
在日誌中,我們可以清楚地發現設定已經生效 “Command line argument: -Xmx512m”
02-Apr-2016 12:46:26.970 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version: Apache Tomcat/8.0.32
02-Apr-2016 12:46:26.974 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built: Feb 2 2016 19:34:53 UTC
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server number: 8.0.32.0
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name: Linux
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version: 4.1.19-boot2docker
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture: amd64
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home: /usr/lib/jvm/java-7-openjdk-amd64/jre
02-Apr-2016 12:46:26.976 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version: 1.7.0_95-b00
02-Apr-2016 12:46:26.976 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor: Oracle Corporation
02-Apr-2016 12:46:26.977 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE: /usr/local/tomcat
02-Apr-2016 12:46:26.977 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME: /usr/local/tomcat
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx512m
...
然而在Docker叢集上部署執行Java容器應用的時候,僅僅對JVM的heap引數設定是不夠的,我們還需要對Docker容器的記憶體資源進行限制:
1. 限制容器使用的記憶體的最大量,防止對系統或其他應用造成傷害
2. 能夠將Docker容器排程到擁有足夠空餘的記憶體的節點,從而保證應用的所需執行資源
關於容器的資源分配約束,Docker提供了相應的啟動引數
對記憶體而言,最基本的就是透過 -m引數來約束容器使用記憶體的大小
-m, --memory=""
Memory limit (format: <number>[<unit>]). Number is a positive integer. Unit can be one of b, k, m, or g. Minimum is 4M.
那麼問題就來了,為了正確設定Docker容器記憶體的大小,難道我們需要同時傳遞容器的記憶體限制和JAVA_OPTS環境變數嗎? 如下所示:
docker run --rm -m 512m -e JAVA_OPTS="-Xmx512m" tomcat:8
這個方法有兩個問題
1. 需要管理員保證容器記憶體和JVM記憶體設定匹配,否則可能引發錯誤
2. 當對容器記憶體限制調整時,環境變數也需要重新設定,這就需要重建一個新的容器
是否有一個方法,可以讓容器內部的JVM自動適配容器的記憶體限制?這樣可以採用更加統一的方法來進行資源管理,簡化配置工作。
大家知道Docker是透過CGroup來實現資源約束的,自從1.7版本之後,Docker把容器的local cgroups以只讀方式掛載到容器內部的檔案系統上,這樣我們就可以在容器內部,透過cgroups資訊來獲取系統對當前容器的資源限制了。
我建立了一個示例映象 registry.aliyuncs.com/denverdino/tomcat:8-autoheap
,其原始碼可以從Github 獲得。它基於Docker官方Tomcat映象建立,它的啟動指令碼會檢查CGroup中記憶體限置,並計算JVM最大Heap size來傳遞給Tomcat。其程式碼如下
#!/bin/bash
limit_in_bytes=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)
# If not default limit_in_bytes in cgroup
if [ "$limit_in_bytes" -ne "9223372036854771712" ]
then
limit_in_megabytes=$(expr $limit_in_bytes \/ 1048576)
heap_size=$(expr $limit_in_megabytes - $RESERVED_MEGABYTES)
export JAVA_OPTS="-Xmx${heap_size}m $JAVA_OPTS"
echo JAVA_OPTS=$JAVA_OPTS
fi
exec catalina.sh run
說明:
為了監控,故障排查等場景,我們預留了部分記憶體(預設64M),其餘容器記憶體我們都分配給JVM的堆。
這裡沒有對邊界情況做進一步處理。在生產系統中需要根據情況做相應的設定,比如最大的堆大小等等。
現在我們啟動一個tomcat執行在512兆的容器中
docker run -d --name test -m 512m registry.aliyuncs.com/denverdino/tomcat:8-autoheap
透過下列命令,從日誌中我們可以檢測到相應的JVM引數已經被設定成 448MB (512-64)
docker logs test
...
02-Apr-2016 14:18:09.870 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx448m
...
我們也可以方便的調整Java應用的記憶體.
Docker 1.10提供了對容器資源限制的動態修改能力。但是由於JVM無法感知容器資源修改,我們依然需要重啟tomcat來變更JVM的記憶體設定,例如,我們可以透過下面命令把容器記憶體限制調整到1GB
docker update -m 1024m test
docker restart test
再次檢查日誌,相應的JVM Heap Size最大值已被設定為960MB
docker logs test
...
02-Apr-2016 14:21:07.644 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx960m