SpringBoot 构建高效的容器镜像
前言
使用普通的 Dockerfile 构建会有哪些缺点?通常情况下构建一个 Spring Boot 的 Docker 镜像,一般会写一个下面这样的Dockerfile:
FROM eclipse-temurin:21-jre
ARG JAR_FILE=build/libs/application.jar # 这里使用Gradle构建,若是 Maven 的话,使用target/application.jar
ADD ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
确实很简单,然而复制并运行docker镜像中的 fat jar有以下几个缺点:
解压 Jar 包运行需要额外的开销:在不解压的情况下运行fat jar,总会有一定的开销,而在容器化环境中,这种开销是很明显的。
变更程序代码后重新构建镜像效率低:Docker镜像是分层构建的,而上面例子将依赖和程序代码都放在一个层中。在实际场景下,修改程序代码的频率将大大高于依赖的变化,所以最好将它们分在不同的层,这样不变的层在docker中可以直接使用缓存。
SpringBoot Layered JAR
从 Spring Boot 2.3.0 开始,使用 Spring Boot Maven 或 Gradle 插件构建的 JAR 文件包含下图的分层信息。
默认情况下,存在以下分层:
dependencies:适用于常规发布的依赖项。
spring-boot-loader:Spring Boot Jar 包加载类,在
org/springframework/boot/loader
下的所有内容。snapshot-dependencies:用于快照依赖项。
application:应用程序的类和资源。
在Spring Boot 2.3之后编译的jar包多了一个文件layers.idx
,通过这个文件来提供层被添加的顺序。从 layer.idx
中可以看到默认的顺序是:dependencies
, spring-boot-loader
,snapshot-dependencies
, application
。
在控制台运行解压命令查看:
java -Djarmode=layertools -jar application.jar extract
推荐的 Dockerfile 文件
假设上述 Dockerfile
在当前目录下,你的docker镜像可以用 docker build .
来构建,也可以选择指定 应用程序jar的路径,比如:你是使用 Maven 进行构建的。
$ docker build --build-arg JAR_FILE=target/application.jar .
这是一个多阶段的docker文件。 构建者阶段提取了以后需要的目录。 每个 COPY
命令都与jarmode提取的层有关。
构建镜像的命令展示如下:
可以通过修改程序代码,重新构建来观察各层的缓存情况。