Docker——Tomcat JVM 內存配置


 

前言

安裝再docker中的tomcat,在下載大文件或者某些情況下,會出現tomcat的內存溢出等情況,所以需要配置tomcat的內存大小,docker中的tomcat內存大小配置有四種方式。

 

 

一、修改catalina.sh

加入JVM:

JAVA_OPTS="-server -Dfile.encoding=UTF-8 -Xms4g -Xmx4g -Xmn1g -Xss512K -verbose:gc -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=10 -XX:PermSize=1g -XX:MaxPermSize=1g -XX:+ExplicitGCInvokesConcurrent -XX:GCTimeRatio=19 -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=10 -XX:+CMSClassUnloadingEnabled -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=50 -Xnoclassgc -XX:SoftRefLRUPolicyMSPerMB=0"

示例:

 

 

二、創建setenv.sh

如果你查看過catalina.sh里面的代碼你會發現下面2段說明:

進入到tomcat/bin目錄下,通過以下命令創建setenv.sh文件:

vi setenv.sh

添加環境變量到文件中並保存

JAVA_OPTS="-server -Dfile.encoding=UTF-8 -Xms4g -Xmx4g -Xmn1g -Xss512K -verbose:gc -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=10 -XX:PermSize=1g -XX:MaxPermSize=1g -XX:+ExplicitGCInvokesConcurrent -XX:GCTimeRatio=19 -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=10 -XX:+CMSClassUnloadingEnabled -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=50 -Xnoclassgc -XX:SoftRefLRUPolicyMSPerMB=0"

 

 

三、docker啟動設置環境變量

如果使用官方的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
docker run -d --name masl -e TZ="Asia/Shanghai" -e JAVA_OPTS='-server -Dfile.encoding=UTF-8 -Xms2g -Xmx2g -Xmn512m -Xss512K -verbose:gc -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=10 -XX:PermSize=512m -XX:MaxPermSize=512m -XX:+ExplicitGCInvokesConcurrent -XX:GCTimeRatio=19 -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=10 -XX:+CMSClassUnloadingEnabled -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=50 -Xnoclassgc -XX:SoftRefLRUPolicyMSPerMB=0' -p 8081:8080 172.16.99.2:40305/masl:dev-yc-36

 

 

四、自動適配容器內存

通過前面3種方法,我們知道在Docker集群上部署運行Java容器應用的時候,可以設置JVM的heap,然而僅僅對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

說明:

  • 為了JVM自身的Non-Heap內存,以及監控,故障排查等場景,我們預留了部分內存(缺省256M),其余容器內存我們都分配給JVM的堆。
  • 這里沒有對邊界情況做進一步處理。在生產系統中需要根據情況做相應的設定,比如最大的堆大小等等。

現在我們啟動一個tomcat運行在512兆的容器中

docker run -d --name test -m 512m registry.aliyuncs.com/denverdino/tomcat:8-autoheap

通過下列命令,從日志中我們可以檢測到相應的JVM參數已經被設置成 256MB (512-256)

docker logs test

...
02-Apr-2016 14:18:09.870 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx256m
...

我們也可以方便的調整Java應用的內存.

Docker 1.10提供了對容器資源限制的動態修改能力。但是由於JVM無法感知容器資源修改,我們依然需要重啟tomcat來變更JVM的內存設置,例如,我們可以通過下面命令把容器內存限制調整到1GB

docker update -m 1024m test
docker restart test

再次檢查日志,相應的JVM Heap Size最大值已被設置為768MB

docker logs test

...
02-Apr-2016 14:21:07.644 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx768MB
...

 

 

總結

第1和第2種方法,通過在Dockerfile中構建image的時候寫死了一個catalina.sh或setenv.sh到tomcat的bin目錄下(或者通過掛載文件的方式),這樣的話就限制死了我們的鏡像,不夠通用。

第3種方法,也容易引發問題,且不夠靈活。所以,推薦使用第4種方法。

 

參考:https://yq.aliyun.com/articles/18037


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM