距离上一次更新该文章已经过了 607 天,文章所描述的內容可能已经发生变化,请留意。
简介 在Spring Boot 2.3
以前,我们创建Spring Boot - docker image最通用的办法就是将Spring boot的应用程序打包成一个fat jar,然后写一个Dockerfile,将这个fat jar制作成为一个docker image然后运行。
在Spring Boot 2.3
发布后,附带了快速创建docker image的功能
传统做法和它的缺点 首先创建一个非常简单的Spring Boot程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 @SpringBootApplication @RestController public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); } @GetMapping("/getInfo") public String getInfo () { return "www.flydean.com" ; } }
默认情况下,我们build出来的是一个fat jar:springboot-with-docker-0.0.1-SNAPSHOT.jar
以下是这个fat jar解压后的内容:
Spring boot的fat jar分为三个部分:
第一部分就是BOOT-INF, 里面的class目录放的是我们自己编写的class文件。而lib目录存放的是项目依赖的其他jar包。
第二部分是META-INF,里面定义了jar包的属性信息。
第三部分是Spring Boot的类加载器,fat jar包的启动是通过Spring Boot的jarLauncher来创建LaunchedURLClassLoader,通过它来加载lib下面的jar包,最后以一个新线程启动应用的Main函数。
如果想要用这个fat jar来创建docker image,可以使用如下Dockerfile
1 2 3 4 5 FROM openjdk:8-jdk-alpine EXPOSE 8080 ARG JAR_FILE=target/springboot-with-docker-0.0.1-SNAPSHOT.jar ADD ${JAR_FILE} app.jar ENTRYPOINT ["java","-jar","/app.jar"]
缺点 这样写有两个问题:
但是如果使用的是fat jar包,即使我们只修改了我们自己的代码,也会导致整个fat jar重新更新,从而影响docker image的构建速度。
使用Buildpacks Spring Boot在2.3.0之后,引入了Cloud Native 的buildpacks,通过这个工具,我们可以非常非常方便的创建docker image。
在Maven和Gradle中,Spring Boot引入了新的phase:spring-boot:build-image
只需要引入此plugin:spring-boot-maven-plugin
,就可以直接运行以下指令来构建镜像:
在早些版本时,会默认从gcr.io
中拉取镜像,但目前3.x
则是从docker.io
拉取
1 mvn spring-boot:build-image
构建过程中会看到类似如下信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [INFO ] --- spring-boot-maven-plugin:3.1.2:build-image (default-cli) @ behappy-common-core --- [INFO ] Building image 'docker.io/wangxiaowu950330/behappy-gulimall/behappy-member:latest' [INFO ] [INFO ] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 16 % [INFO ] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 28 % [INFO ] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 30 % [INFO ] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 30 % [INFO ] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 35 % [INFO ] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 47 % [INFO ] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 56 % [INFO ] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 68 % [INFO ] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 100 % [INFO ] > Pulled builder image 'paketobuildpacks/builder@sha256:1a59354925fcb7ba54744b8017630c97c2b035e1a9e19309330557b9c66bfc2c' [INFO ] > Pulling run image 'docker.io/paketobuildpacks/run:tiny-cnb' 100 % [INFO ] > Pulled run image 'paketobuildpacks/run@sha256:adf913cf28031f2090aeaedac65edb36f2987d81a23a8dffab5ea18ca216c94c'
Layered Jars 如果你不想使用Cloud Native Buildpacks,还是想使用传统的Dockerfile的话,SpringBoot也为我们提供了独特的分层jar包系统。
我们需要在POM文件中加上下面的配置,即可开启:
1 2 3 4 5 6 7 8 9 10 11 12 13 <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <layers > <enabled > true</enabled > </layers > </configuration > </plugin > </plugins > </build >
再次打包,可以看到jar包内的内容和之前的jar包大致相同,但多了一个layers.idx 这个index文件:
layers分层 1 2 3 4 5 6 7 8 9 10 - "dependencies": - "BOOT-INF/lib/" - "spring-boot-loader": - "org/" - "snapshot-dependencies": - "application": - "BOOT-INF/classes/" - "BOOT-INF/classpath.idx" - "BOOT-INF/layers.idx" - "META-INF/"
index文件主要分为4个部分:
dependencies - 非SNAPSHOT的依赖jar包 snapshot-dependencies - SNAPSHOT的依赖jar包 spring-boot-loader - Spring boot的class loader文件 application - 应用程序的class和resources文件 注意,这里的index文件是有顺序的,它和我们将要添加到docker image中的layer顺序是一致的。
最少变化的将会最先添加到layer中,变动最大的放在最后面的layer。
我们可以使用layertools jarmode来对生成的fat jar进行校验或者解压缩:
1 2 3 4 5 6 7 8 java -Djarmode=layertools -jar springboot-with-docker-0.0.1-SNAPSHOT.jar Usage: java -Djarmode=layertools -jar springboot-with-docker-0.0.1-SNAPSHOT.jar Available commands: list List layers from the jar that can be extracted extract Extracts layers from the jar for image creation help Help about any command
使用list
命令,可列出jar包中的layer信息。使用extract
我们可以解压出不同的layer。
我们执行下extract命令,看下结果:
可以看到,我们根据layers.idx解压出了不同的文件夹。
所以开启了layer的DockerFile可以这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 FROM adoptopenjdk:11 -jre-hotspot as builderWORKDIR application ARG JAR_FILE=target/*.jarCOPY ${JAR_FILE} application.jar RUN java -Djarmode=layertools -jar application.jar extract FROM adoptopenjdk:11 -jre-hotspotWORKDIR application COPY --from=builder application/dependencies/ ./ COPY --from=builder application/spring-boot-loader/ ./ COPY --from=builder application/snapshot-dependencies/ ./ COPY --from=builder application/application/ ./ ENTRYPOINT ["java" , "org.springframework.boot.loader.JarLauncher" ]
这样我们的一个分层的DockerImage就创建完成了。
自定义Layer 如果我们需要自定义Layer,可以创建一个独立的layers.xml文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <layers xmlns ="http://www.springframework.org/schema/boot/layers" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/boot/layers https://www.springframework.org/schema/boot/layers/layers-2.3.xsd" > <application > <into layer ="spring-boot-loader" > <include > org/springframework/boot/loader/**</include > </into > <into layer ="application" /> </application > <dependencies > <into layer ="snapshot-dependencies" > <include > *:*:*SNAPSHOT</include > </into > <into layer ="company-dependencies" > <include > com.flydean:*</include > </into > <into layer ="dependencies" /> </dependencies > <layerOrder > <layer > dependencies</layer > <layer > spring-boot-loader</layer > <layer > snapshot-dependencies</layer > <layer > company-dependencies</layer > <layer > application</layer > </layerOrder > </layers >
然后添加到build plugin中就可以了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <layers > <enabled > true</enabled > <configuration > ${project.basedir}/src/main/resources/layers.xml</configuration > </layers > </configuration > </plugin > </plugins > </build >