当前位置: 首页 > 科技观察

Java服务Docker容器化最佳实践_0

时间:2023-03-20 15:50:09 科技观察

1.概述当我们在容器中运行Java应用程序时,我们可能希望调整参数以充分利用资源。在本教程中,我们将了解如何在运行Java进程的容器中设置JVM参数。本文将重点介绍常见的-Xmx和-Xms标志。此外,我们还将探讨与某些Java版本一起运行的容器化程序的常见问题,以及如何在对常见Java应用程序进行容器化时设置自定义标志。2、Java容器中的默认堆设置过去,JVM并不知道分配给容器的内存和CPU。Java10引入了一个新设置:+UseContainerSupport(默认启用)来解决此问题[3],并在8u191[4]中将此修复程序反向移植到Java8。现在JVM可以根据分配给容器的内存来计算它的内存。1、自动内存计算当-Xmx和-Xmx参数没有设置时,JVM会根据系统规格调整堆大小。查看堆大小:$java-XX:+PrintFlagsFinal-version|grep-Ei"maxheapsize|maxram"输出如下:openjdkversion"15"2020-09-15OpenJDKRuntimeEnvironmentAdoptOpenJDK(build15+36)OpenJDK64-BitServerVMAdoptOpenJDK(build15+36,mixedmode,sharing)size_tMaxHeapSize=4253024256{product}{ergonomic}uint64_tMaxRAM=137438953472{pdproduct}{default}uintxMaxRAMFraction=4{product}{default}doubleMaxRAMPercentage=25.000000{product}{default}size_tSoftMaxHeapSize=425302425}{ageable}ergonomic}我们看到JVM将其堆大小设置为可用RAM的大约25%。在此示例中,在16GB系统上分配了4GB。出于测试目的,创建一个名为PrintXmxXms.java的文件,内容是打印堆大小,单位MB,代码内容如下:importjava.lang.management.ManagementFactory;导入java.lang.management.MemoryMXBean;公共类PrintXmxXms{publicstaticvoidmain(String[]args){intmb=1024*1024;MemoryMXBeanmemoryBean=ManagementFactory.getMemoryMXBean();longxmx=memoryBean.getHeapMemoryUsage().getMax()/mb;longxms=memoryBean.getHeapMemoryUsage().getInit()/mb;System.out.println("初始内存(xms):"+xms+"mb");System.out.println("最大内存(xmx):"+xmx+"mb");}}假设已经安装了JDK,程序可以编译运行:$javac./PrintXmxXms.java$java-cp。PrintXmxXms在具有16GbRAM的主机上,输出为:INFO:InitialMemory(xms):254mbINFO:MaxMemory(xmx):Below4056mb,在容器中尝试。2.对于JDK8u191之前的版本,在包含PrintXmxXms.java文件的文件夹中添加如下Dockerfile:FROMopenjdk:8u92-jdk-alpineCOPY*.java/src/RUNmkdir/app\&&ls/src\&&javac/src/PrintXmxXms.java-d/appCMD["sh","-c",\"java-version\&&java-cp/appPrintXmxXms"]此处使用的容器使用旧版本的Java8,早于较新版本版本可用的容器支持。构建镜像:$sudodockerbuild-toldjava。Dockerfile中的CMD行是运行容器时默认执行的进程。由于未提供-Xmx或-XmsJVM标志,因此内存设置将是默认设置。运行容器:$sudodockerrun--rm-tioldjavaopenjdkversion"1.8.0_92-internal"OpenJDKRuntimeEnvironment(build1.8.0_92-...)OpenJDK64-BitServerVM(build25.92-b14,mixedmode)初始内存(xms):198mb最大内存(xmx):2814mb现在使用--memory=1g命令行标志将容器内存限制为1GB:$sudodockerrun--rm-ti--memory=1goldjavaopenjdkversion"1.8.0_92-internal"OpenJDKRuntimeEnvironment(build1.8.0_92-...)OpenJDK64-BitServerVM(build25.92-b14,mixedmode)InitialMemory(xms):198mbMaxMemory(xmx):2814mb输出完全一样.这证明旧的JVM不遵守容器内存限制。3.JDK8u130之后的版本使用相同的测试程序,将Dockerfile的第一行改成使用新版本的JVM8:FROMopenjdk:8-jdk-alpine然后再做测试:$sudodockerbuild-tnewjava.$sudodockerrun--rm-tinewjavaopenjdkversion"1.8.0_212"OpenJDKRuntimeEnvironment(IcedTea3.12.0)(Alpine8.212.04-r0)OpenJDK64-BitServerVM(build25.212-b04,mixedmode)Initial内存(xms):198mbMax内存(xmx):2814mb如上面的输出,使用整个docker主机内存来计算JVM堆大小。但是,如果您为容器分配1GB的RAM:$sudodockerrun--rm-ti--memory=1gnewjavaopenjdkversion"1.8.0_212"OpenJDKRuntimeEnvironment(IcedTea3.12.0)(Alpine8.212.04-r0)OpenJDK64位服务器VM(构建25.212-b04,混合模式)初始内存(xms):16mb最大内存(xmx):247mb这一次,JVM根据容器可用的1GBRAM计算堆大小。我们现在了解了JVM如何计算其默认值以及为什么您需要最新的JVM才能获得正确的默认值。3.常用基础镜像中的内存设置1.OpenJDK使用环境变量,而不是直接在容器命令上硬编码JVM标志。例如,如果你在Dockerfile中使用了JAVA_OPTS变量,你可以在启动容器时修改它:FROMopenjdk:8u92-jdk-alpineCOPY*.java/src/RUNmkdir/app\&&ls/src\&&javac/src/打印XmxXms。java-d/appENVJAVA_OPTS=""CMD["sh","-c",\"java-version\&&java$JAVA_OPTS-cp/appPrintXmxXms"]构建镜像:$sudodockerbuild-topenjdk-java。通过指定JAVA_OPTS环境变量在运行时选择内存设置:$sudodockerrun--rm-ti-eJAVA_OPTS="-Xms50M-Xmx50M"openjdk-javaopenjdkversion"1.8.0_92-internal"OpenJDKRuntimeEnvironment(build1.8.0_92-internal-alpine-r1-b14)OpenJDK64-BitServerVM(build25.92-b14,mixedmode)InitialMemory(xms):50mbMaxMemory(xmx):48mbNote:-XmxparameterandMaxmemoryreportedbyJVM之间存在细微差别。这是因为Xmx设置了内存分配池的最大大小,其中包括堆、垃圾收集器的幸存者空间和其他池。2.Tomcat9Tomcat9容器有自己的启动脚本,所以要设置JVM参数,需要用到这些脚本。bin/catalina.sh脚本需要在环境变量CATALINA_OPTS中设置内存参数。首先,您需要创建一个war包以部署到Tomcat。然后我们使用以下Dockerfile将其容器化,我们在其中声明CATALINA_OPTS环境变量:FROMtomcat:9.0COPY./target/*.war/usr/local/tomcat/webapps/ROOT.warENVCATALINA_OPTS="-Xms1G-Xmx1G"然后构建容器镜像并运行它:$sudodockerbuild-ttomcat.$sudodockerrun--nametomcat-d-p8080:8080\-eCATALINA_OPTS="-Xms512M-Xmx512M"tomcat注意:运行,通过CATALINA_OPTS的新值。如果未提供此值,将使用Dockerfile第3行给出的默认值。您可以检查应用程序的运行时参数并验证选项-Xmx和-Xms是否存在:$sudodockerexec-titomcatjps-lv1org.apache.catalina.startup.Bootstrap-Xms512M-Xmx512M4.使用构建插件Maven和Gradle提供的插件允许我们在没有Dockerfile的情况下创建容器镜像。生成的图像通常可以在运行时通过环境变量进行参数化。让我们看几个例子。1.使用SpringBoot从SpringBoot2.3开始,SpringBootMaven和Gradle插件可以在没有Dockerfile的情况下高效构建容器。使用Maven时,将它们添加到spring-boot-maven-plugin中的块中:com.baeldung.dockerheapsizing-demo0.0.1-SNAPSHOTorg.springframework.bootspring-boot-maven-pluginheapsizing-demo要构建项目,请运行以下命令:$./mvnwcleanspring-boot:build-image这将生成一个name本例中为:生成的镜像命名为:demo-app:0.0.1-SNAPSHOT。SpringBoot底层使用CloudNativeBuildpacks作为容器化技术。该插件对JVM的内存设置进行硬编码。但是,我们仍然可以通过设置环境变量JAVA_OPTS或JAVA_TOOL_OPTIONS来覆盖它:$sudodockerrun--rm-ti-p8080:8080\-eJAVA_TOOL_OPTIONS="-Xms20M-Xmx20M"\--memory=1024Mheapsizing-demo:0.0.1-SNAPSHOT输出类似于:SettingActiveProcessorCountto8CalculatedJVMMemoryConfiguration:[...][...]PickedupJAVA_TOOL_OPTIONS:-Xms20M-Xmx20M[...]2.使用GoogleJIB就像SpringBootmaven插件一样,GoogleJIB可以在没有Dockerfile的情况下高效地创建Docker镜像。Maven和Gradle插件的配置方式类似。GoogleJIB还使用环境变量JAVA_TOOL_OPTIONS作为JVM参数的覆盖机制。我们可以将GoogleJIBMaven插件与任何能够生成可执行jar文件的Java框架一起使用。例如,它可以在SpringBoot应用程序中代替spring-boot-maven插件来生成容器镜像:com.google.cloud.toolsjib-maven-plugin2.7.1heapsizing-demo-jib图像使用mvnjib:由dockerBuild命令构建:$mvncleaninstall&&mvnjib:dockerBuild尝试运行:$sudodockerrun--rm-ti-p8080:8080\-eJAVA_TOOL_OPTIONS="-Xms50M-Xmx50M"heapsizing-demo-jibPickedupJAVA_TOOL_OPTIONS:-Xms50M-Xmx50M[...]2021-01-2517:46:44.070INFO1---[main]c.baeldung.docker.XmxXmsDemoApplication:在1.666秒内启动XmxXmsDemoApplication(JVM运行2.104)2021-01-2517:46:44.075INFO1---[main]c.baeldung.docker.XmxXmsDemoApplication:初始内存(xms):50mb2021-01-2517:46:44.075INFO1---[main]c.baeldung.docker.XmxXmsDemoApplication:MaxMemory(xmx):50mb5.结论在这篇文章中,我们介绍了需要使用最新的JVM来获取容器中的默认内存设置,然后研究了在自定义容器镜像中设置-Xms和-BestXmx的实践以及如何使用现有的Java应用程序容器在其中设置JVM选项。最后,我们了解了如何使用构建工具来管理Java应用程序的容器化。GitHub上提供了上面使用的示例源代码。