偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

在Docker中運(yùn)行Java:為了防止失敗,你應(yīng)該知道的

開發(fā)
如果你嘗試在容器中運(yùn)行Java程序,或者專注于Docker,你可能會(huì)遇到一些關(guān)于JVM和堆大小的問(wèn)題。本篇文章將介紹如何解決這些問(wèn)題。

如果你嘗試在容器中運(yùn)行Java程序,或者專注于Docker,你可能會(huì)遇到一些關(guān)于JVM和堆大小的問(wèn)題。本篇文章將介紹如何解決這些問(wèn)題。

很多開發(fā)者會(huì)(或者應(yīng)該)知道,當(dāng)我們?yōu)檫\(yùn)行在Linux容器(docker, rkt, runC, lxcfs, etc,)中的Java程序去設(shè)置JVM的GC、堆大小和運(yùn)行時(shí)編譯器的參數(shù)時(shí)并沒(méi)有得到預(yù)想的效果。當(dāng)我們通過(guò)“java -jar mypplication-fat.jar”的方式而不設(shè)置任何參數(shù)來(lái)運(yùn)行一個(gè)Java應(yīng)用時(shí),JVM會(huì)根據(jù)自身的許多參數(shù)進(jìn)行調(diào)整,以便在執(zhí)行環(huán)境中獲得最優(yōu)的性能。

本篇博客將通過(guò)簡(jiǎn)單的方式向開發(fā)人員展示在將Java應(yīng)用運(yùn)行在Linux容器內(nèi)時(shí)需要了解的內(nèi)容。

我們傾向于認(rèn)為容器可以像虛擬機(jī)一樣可以完整的定義虛擬機(jī)的CPU個(gè)數(shù)和虛擬機(jī)的內(nèi)存。容器更像是一個(gè)進(jìn)程級(jí)別的資源(CPU、內(nèi)存、文件系統(tǒng)、網(wǎng)絡(luò)等)隔離。這種隔離是依賴于Linux內(nèi)核中提供的一個(gè) cgroups 的功能。

然而,一些可以從運(yùn)行時(shí)環(huán)境中收集信息的應(yīng)用程序在cgroups功能出現(xiàn)之前已經(jīng)存在。在容器中執(zhí)行命令 ‘top‘, ‘free‘, ‘ps’,也包括沒(méi)有經(jīng)過(guò)優(yōu)化的JVM是一個(gè)會(huì)受到高限制的Linux進(jìn)程。讓我們來(lái)驗(yàn)證一下。

問(wèn)題

為了展示遇到的問(wèn)題,我使用命令“docker-machine create -d virtualbox –virtualbox-memory ‘1024’ docker1024”在虛擬機(jī)中創(chuàng)建了一個(gè)具有1GB內(nèi)存的Docker守護(hù)進(jìn)程,接下來(lái)在3個(gè)Linux容器中執(zhí)行命令“free -h”,使其只有100MB的內(nèi)存和Swap。結(jié)果顯示所有的容器總內(nèi)存是995MB。

在Docker中運(yùn)行Java:為了防止失敗,你應(yīng)該知道的

即使是在 Kubernetes/OpenShift集群中,結(jié)果也是類似的。我在一個(gè)內(nèi)存是15G的集群中也執(zhí)行了命令使得Kubernetes Pod有511MB的內(nèi)存限制(命令:“kubectl run mycentos –image=centos -it –limits=’memory=512Mi’”),總內(nèi)存顯示為14GB。

在Docker中運(yùn)行Java:為了防止失敗,你應(yīng)該知道的

想要知道為什么是這樣的結(jié)果,可以去閱讀此篇博客文章 “ Memory inside Linux containers – Or why don’t free and top work in a Linux container? ”

我們需要知道Docker參數(shù) (-m, –memory和–memory-swap)和Kubernetes參數(shù) (–limits)會(huì)讓Linux內(nèi)核在一個(gè)進(jìn)程的內(nèi)存超出限制時(shí)將其Kill掉,但是JVM根本不清楚這個(gè)限制的存在,當(dāng)超過(guò)這個(gè)限制時(shí),不好的事情發(fā)生了!

為了模擬當(dāng)一個(gè)進(jìn)程超出內(nèi)存限制時(shí)會(huì)被殺死的場(chǎng)景,我們可以通過(guò)命令“docker run -it –name mywildfly -m=50m jboss/wildfly”在一個(gè)容器中運(yùn)行WildFly Application Server并且為其限制內(nèi)存大小為50MB。在這個(gè)容器運(yùn)行期間,我們可以執(zhí)行命令“docker stats”來(lái)查看容器的限制。

在Docker中運(yùn)行Java:為了防止失敗,你應(yīng)該知道的

但是過(guò)了幾秒之后,容器Wildfly將會(huì)被中斷并且輸出信息:*** JBossAS process (55) received KILL signal ***

通過(guò)命令 “docker inspect mywildfly -f ‘{{json .State}}'”可以查看容器被殺死的原因是發(fā)生了OOM(內(nèi)存不足)。容器中的“state”被記錄為OOMKilled=true 。

在Docker中運(yùn)行Java:為了防止失敗,你應(yīng)該知道的

這將怎樣影響Java應(yīng)用

在Docker宿主機(jī)中創(chuàng)建一個(gè)具有1GB內(nèi)存的虛擬機(jī)(在之前使用命令已經(jīng)創(chuàng)建完畢 “docker-machine create -d virtualbox –virtualbox-memory ‘1024’ docker1024”) ,并且限制一個(gè)容器的內(nèi)存為150M,看起來(lái)已經(jīng)足夠運(yùn)行這個(gè)在 Dockerfile中設(shè)置過(guò)參數(shù)-XX: PrintFlagsFinal 和 -XX: PrintGCDetails的Spring Boot application了。這些參數(shù)使得我們可以讀取JVM的初始化參數(shù)并且獲得 Garbage Collection (GC)的運(yùn)行詳細(xì)情況。

嘗試一下:

  1. $ docker run -it --rm --name mycontainer150 -p 8080:8080 -m 150M rafabene/java-container:openjdk 

我也提供了一個(gè)訪問(wèn)接口“/api/memory/”來(lái)使用String對(duì)象加載JVM內(nèi)存,模擬大量的消耗內(nèi)存,可以調(diào)用試試:

  1. $ curl http://X 41X:8080/api/memory 

這個(gè)接口將會(huì)返回下面的信息 “Allocated more than 80% (219.8 MiB) of the max allowed JVM memory size (241.7 MiB)”

在這里我們至少有2個(gè)問(wèn)題:

  • 為什么JVM會(huì)允許241.7MiB的最大內(nèi)容?
  • 如果容器已經(jīng)限制了內(nèi)存為150MB,為什么允許Java分配內(nèi)存到220MB?

首先,我們應(yīng)該重新了解在 JVM ergonomic page 中所描述的 “maximum heap size”的定義,它將會(huì)使用1/4的物理內(nèi)存。JVM并不知道它運(yùn)行在一個(gè)容器中,所以它將被允許使用260MB的最大堆大小。通過(guò)添加容器初始化時(shí)的參數(shù)-XX: PrintFlagsFinal,我們可以檢查這個(gè)參數(shù)的值。

 

  1. $ docker logs mycontainer150|grep -i MaxHeapSize  
  2. uintx MaxHeapSize := 262144000 {product} 

其次,我們應(yīng)該理解當(dāng)在docker命令行中設(shè)置了 “-m 150M”參數(shù)時(shí),Docker守護(hù)進(jìn)程會(huì)限制RAM為150M并且Swap為150M。從結(jié)果上看,一個(gè)進(jìn)程可以分配300M的內(nèi)存,解釋了為什么我們的進(jìn)程沒(méi)有收到任何從Kernel中發(fā)出的退出信號(hào)。

更多的關(guān)于Docker命令中內(nèi)存限制 (–memory)和Swap (–memory-swap)的差別可以參考 這里 。

更多的內(nèi)存是解決方案嗎?

開發(fā)者如果不理解問(wèn)題可能會(huì)認(rèn)為運(yùn)行環(huán)境中沒(méi)有為JVM提供足夠的內(nèi)存。通常的解決對(duì)策就是為運(yùn)行環(huán)境提供更多的內(nèi)存,但是實(shí)際上,這是一個(gè)錯(cuò)誤的認(rèn)識(shí)。

假如我們將Docker Machine的內(nèi)存從1GB提高到8GB(使用命令 “docker-machine create -d virtualbox –virtualbox-memory ‘8192’ docker8192”),并且創(chuàng)建的容器從150M到800M:

  1. $ docker run -it --name mycontainer -p 8080:8080 -m 800M rafabene/java-container:openjdk 

此時(shí)使用命令 “curl http://X 58X:8080/api/memory” 還不能返回結(jié)果,因?yàn)樵谝粋€(gè)擁有8GB內(nèi)存的JVM環(huán)境中經(jīng)過(guò)計(jì)算的MaxHeapSize大小是2092957696(~ 2GB)。可以使用命令“docker logs mycontainer|grep -i MaxHeapSize”查看。

在Docker中運(yùn)行Java:為了防止失敗,你應(yīng)該知道的

應(yīng)用將會(huì)嘗試分配超過(guò)1.6GB的內(nèi)存,當(dāng)超過(guò)了容器的限制(800MB的RAM 800MB的Swap),進(jìn)程將會(huì)被kill掉。

很明顯當(dāng)在容器中運(yùn)行程序時(shí),通過(guò)增加內(nèi)存和設(shè)置JVM的參數(shù)不是一個(gè)好的方式。當(dāng)在一個(gè)容器中運(yùn)行Java應(yīng)用時(shí),我們應(yīng)該基于應(yīng)用的需要和容器的限制來(lái)設(shè)置最大堆大小(參數(shù):-Xmx)。

解決方案是什么?

在Dockerfile中稍作修改,為JVM指定擴(kuò)展的環(huán)境變量。修改內(nèi)容如下:

  1. CMD java -XX: PrintFlagsFinal -XX: PrintGCDetails $JAVA_OPTIONS -jar java-container.jar 

現(xiàn)在我們可以使用JAVA_OPTIONS的環(huán)境變量來(lái)設(shè)置JVM Heap的大小。300MB看起來(lái)對(duì)應(yīng)用足夠了。稍后你可以查看日志,看到Heap的值是 314572800 bytes ( 300MBi)。

Docker下,可以使用“-e”的參數(shù)來(lái)設(shè)置環(huán)境變量進(jìn)行切換。

 

  1. $ docker run -d --name mycontainer8g -p 8080:8080 -m 800M -e JAVA_OPTIONS='-Xmx300m' rafabene/java-container:openjdk-env  
  2. $ docker logs mycontainer8g|grep -i MaxHeapSize  
  3. uintx MaxHeapSize := 314572800 {product} 

在Kubernetes中,可以使用“–env=[key=value]”來(lái)設(shè)置環(huán)境變量進(jìn)行切換:

 

 

  1. $ kubectl run mycontainer --image=rafabene/java-container:openjdk-env --limits='memory=800Mi' --env="JAVA_OPTIONS='-Xmx300m'"   
  2. $ kubectl get pods   
  3. NAME READY STATUS RESTARTS AGE    
  4. mycontainer-2141389741-b1u0o 1/1 Running 0 6s    
  5. $ kubectl logs mycontainer-2141389741-b1u0o|grep MaxHeapSize    
  6. uintx MaxHeapSize := 314572800 {product}  

還能再改進(jìn)嗎?

有什么辦法可以根據(jù)容器的限制來(lái)自動(dòng)計(jì)算Heap的值?

事實(shí)上如果你的基礎(chǔ)Docker鏡像使用的是由Fabric8提供的,那么就可以實(shí)現(xiàn)。鏡像fabric8/java-jboss-openjdk8-jdk使用了腳本來(lái)計(jì)算容器的內(nèi)存限制,并且使用50%的內(nèi)存作為上限。也就是有50%的內(nèi)存可以寫入。你也可以使用這個(gè)鏡像來(lái)開/關(guān)調(diào)試、診斷或者其他更多的事情。讓我們看一下一個(gè)Spring Boot應(yīng)用的 Dockerfile :

 

  1. FROM fabric8/java-jboss-openjdk8-jdk:1.2.3  
  2. ENV JAVA_APP_JAR java-container.jar  
  3. ENV AB_OFF true  
  4. EXPOSE 8080  
  5. ADD target/$JAVA_APP_JAR /deployments/ 

就這樣!現(xiàn)在,不管容器的內(nèi)存限制如何,我們的Java應(yīng)用將在容器中自動(dòng)的調(diào)節(jié)Heap大小,而不是再根據(jù)宿主機(jī)來(lái)設(shè)置。

 

在Docker中運(yùn)行Java:為了防止失敗,你應(yīng)該知道的

總結(jié)到目前為止,Java JVM還不能意識(shí)到其是運(yùn)行在一個(gè)容器中 — 某些資源在內(nèi)存和CPU的使用上會(huì)受到限制。因此,你不能讓JVM自己來(lái)設(shè)置其認(rèn)為的最優(yōu)的最大Heap值。

一個(gè)解決對(duì)策是使用Fabric8作為基礎(chǔ)鏡像,它可以意識(shí)到應(yīng)用程序運(yùn)行在一個(gè)受限制的容器中,并且在你沒(méi)有做任何事情的情況下,可以自動(dòng)的調(diào)整最大Heap的值。

在JDK9中已經(jīng)開始進(jìn)行嘗試在容器 (i.e. Docker)環(huán)境中為JVM提供cgroup功能的內(nèi)存限制。

責(zé)任編輯:未麗燕 來(lái)源: DockOne
相關(guān)推薦

2017-06-06 11:59:26

Docker工具容器

2017-07-24 14:59:31

ERP軟件連續(xù)性

2024-11-28 08:54:19

GolangGo變量

2011-03-25 15:56:58

2019-06-03 08:04:43

Apache服務(wù)器命令

2010-08-09 13:20:36

Flex

2013-01-09 13:55:43

2020-04-29 14:30:35

HTTPHTTPS前端

2020-11-16 09:15:07

MYSQL

2022-11-04 08:22:14

編譯代碼C語(yǔ)言

2017-10-12 10:20:13

服務(wù)器運(yùn)行壽命

2021-06-07 12:40:34

Python代碼陷阱

2022-01-04 10:10:34

Garuda LinuArch LinuxLinux

2023-05-04 16:10:13

緩存前端

2013-06-28 14:09:33

PHP庫(kù)

2020-10-13 14:15:22

HTTPHTTP請(qǐng)求方法

2018-08-23 09:33:12

2015-05-07 10:23:19

Android學(xué)習(xí)資源

2018-04-02 14:33:58

區(qū)塊鏈投資存儲(chǔ)技術(shù)

2013-05-23 11:11:58

Sailfish OSJolla手機(jī)操作系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)