如果你很急着了解,可以直接跳到最后,看最终的Dockerfile,前面的介绍是为了大家理解原理。1、普通Dockerfile的缺点 我们通常情况下要编译SpringBoot的Docker镜像,一般会写一个下面这样的DockerfileFROMopenjdk:17EXPOSE8080ARGJARFILEtargetmyapplication。jarMaven的位置,Gradle为buildlibsmyapplication。jarADD{JARFILE}app。jarENTRYPOINT〔java,jar,app。jar〕 这样做很简单,运行起来也没什么问题,但是它有以下的缺点:镜像中的jar包是压缩文件,在容器环境中运行需要解压,这需要相当的开销,我们的jar通常要以解压后的形式运行更好。更新程序后编译新的镜像效率低。Docker镜像的编译时分层构建的,而上面将依赖和程序都放在一个层中。在实际应用中,修改程序编译代码的频率将大大高于依赖的变化,所以我们最好将它们分在不同的层,这样不变的层在docker中可以直接使用缓存。2、一个简单的演示程序 我们创建一个简单的演示应用,添加SpringWeb。 添加简单的演示代码:SpringBootApplicationRestControllerpublicclassLayeredJarsApplication{GetMappingpublicStringhello(){returnHelloLayeredJars!;}publicstaticvoidmain(String〔〕args){SpringApplication。run(LayeredJarsApplication。class,args);}} 自行编译SpringBoot的jar包。3、探索官方最佳方案分层jar包 开始之前,如果你想对SpringBoot的jar包有更深入的了解,可参考我的《SpringBoot独立运行的jar包是如何工作的》。 我们打开SpringBoot生成的jar包发现,从SpringBoot2。3之后,SpringBoot提供了一种新的布局类型叫做LAYEREDJAR即分层jar包。 在SpringBoot2。3之后编译的jar包多了一个文件layers。idx,通过这个文件来提供层被添加的顺序。 默认情况下,SpringBoot定义了下面的层:dependencies:非程序的依赖,但版本号不包含SNAPSHOT。springbootloader:SpringBootJar包加载类。snapshotdependencies:非程序的依赖,但版本号包含SNAPSHOT。application:程序的依赖,应用类和资源。 从layer。idx中可以看到默认的顺序是:dependencies,springbootloader,snapshotdependencies,application。最少修改的层先添加,修改多的后添加,我们修改最多的应该是应用程序类和资源。dependencies:BOOTINFlibspringbootloader:orgsnapshotdependencies:application:BOOTINFclassesBOOTINFclasspath。idxBOOTINFlayers。idxMETAINF 一旦一个分层的jar包被创建后,springbootjarmodelayertools的jar包会被添加到应用程序的依赖中。一旦程序类路径中有了这个jar包,你就可以在特殊模式下启动应用程序,该模式允许引导代码运行与你的应用程序完全不同的东西,例如,提取jar包中层。 4、如何提取层 接上面,我们可以通过jarmode系统属性来提取jar包中的层。通过设置jarmode为layertools来启动jar包从而提取jar包中的层。 控制台运行下面命令:javaDjarmodelayertoolsjarmyapp。jar 控制台会提示下面的内容:Usage:javaDjarmodelayertoolsjarlayeredjars0。0。1SNAPSHOT。jarAvailablecommands:listListlayersfromthejarthatcanbeextractedextractExtractslayersfromthejarforimagecreationhelpHelpaboutanycommand 我们可以使用list命令列出jar中可提取的层,或者使用extract来提取jar包的层来进行镜像的创建。 5、推荐的Dockerfile 在程序根目录新建Dockerfile,内容如下:FROMopenjdk:17asbuilderWORKDIRapplicationARGJARFILEbuildlibs。jarCOPY{JARFILE}application。jarRUNjavaDjarmodelayertoolsjarapplication。jarextractFROMopenjdk:17WORKDIRapplicationCOPYfrombuilderapplicationdependencies。COPYfrombuilderapplicationspringbootloader。COPYfrombuilderapplicationsnapshotdependencies。COPYfrombuilderapplicationapplication。ENTRYPOINT〔java,org。springframework。boot。loader。JarLauncher〕 这是一个多阶段Dockerfile,builder阶段提取下面阶段需要的文件夹,每一句COPY命令都是我们上面提到的层。 org。springframework。boot。loader。JarLauncher,是SpringBoot的Main类的入口,想更多了解,可参考《SpringBoot独立运行的jar包是如何工作的》。 在Dockerfile下同目录,执行下面命令:dockerbuild。taglayeredjars 用下面的命令运行docker镜像:dockerrunitp8080:8080layeredjars:latest 当我们修改程序代码:GetMappingpublicStringhello(){returnHellochangedLayeredJars!;} 重新编译jar包,执行:dockerbuild。taglayeredjars 这时我们发现没有被修改过的层都使用缓存了。 再次运行新的镜像:dockerrunitp8080:8080layeredjars:latest