章
目
录
在开发Spring Boot应用的时候,大家应该都知道,使用spring-boot-maven-plugin
打包后得到的是uber jar,也就是我们常说的fat jar。在日常开发中,多数情况下只有应用程序对应的jar包会发生变化。但对于Docker来说,只要复制jar包的这一层有变动,就需要推送或拉取整个层,这无疑增加了很多不必要的操作。
那有没有办法优化呢?如果能把应用的jar包单独放在一层,是不是就能减少镜像变动的大小了?接下来,就给大家分享一个优化Spring Boot容器镜像的方法,还会详细讲解如何操作。
一、优化思路及Dockerfile配置
1.1 构建分层提取
下面是一个通用的Dockerfile示例,主要分为两部分。先看前半部分,它的作用是在一个单独的构建容器里,把构建好的uber jar按照分层分别提取到对应的目录。
# 基于bellsoft/liberica-openjre-debian:17-cds镜像创建一个名为builder的构建容器
FROM bellsoft/liberica-openjre-debian:17-cds AS builder
WORKDIR /builder
# 定义一个变量JAR_FILE,指向项目target文件夹下的jar包,如果使用Gradle构建,需调整为'build/libs/*.jar'
ARG JAR_FILE=target/*.jar
# 将指定的jar包复制到工作目录,并命名为application.jar
COPY ${JAR_FILE} application.jar
# 使用高效的布局提取jar文件,提取结果存放在extracted目录
RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted
1.2 复制分层目录到镜像
后半部分则是把提取出来的目录复制到镜像里。因为是分别复制各个目录,所以只要对应的层没有变化,Docker就不会重新拉取,这就大大提高了效率。而且,这部分还预生成了CDS缓存文件,能加快应用的启动速度。
# 基于bellsoft/liberica-openjre-debian:17-cds镜像创建运行时容器
FROM bellsoft/liberica-openjre-debian:17-cds
WORKDIR /application
# 从构建容器builder中复制extracted目录下的dependencies文件夹到运行时容器的当前工作目录
COPY --from=builder /builder/extracted/dependencies/ ./
# 复制spring-boot-loader相关文件夹
COPY --from=builder /builder/extracted/spring-boot-loader/ ./
# 复制snapshot-dependencies文件夹
COPY --from=builder /builder/extracted/snapshot-dependencies/ ./
# 复制application相关文件夹
COPY --from=builder /builder/extracted/application/ ./
# 执行CDS训练运行
RUN java -XX:ArchiveClassesAtExit=application.jsa -Dspring.context.exit=onRefresh -jar application.jar
# 启动应用,使用预生成的CDS缓存文件,提高启动速度
ENTRYPOINT ["java", "-XX:SharedArchiveFile=application.jsa", "-jar", "application.jar"]
二、提取目录及分层配置解析
2.1 提取目录结构
以之前的WebSocket示例项目为例,在本地执行提取命令java -Djarmode=tools -jar application.jar extract --layers --destination extracted
后,可以看到提取出了几个目录:
dependencies
:存放常规发布的依赖。spring-boot-loader
:包含org/springframework/boot/loader
下的所有内容。snapshot-dependencies
:用于存放快照依赖。application
:存放应用程序的类和资源。
每个目录都对应一个Docker层,这种分层方式让镜像管理更加灵活。
2.2 分层配置文件
解压缩jar文件后,在BOOT-INF
目录下会看到两个.idx
后缀的文件,layers.idx
和classpath.idx
,它们是用来配置分层的。如果需要修改分层配置,可以参考官方文档“Custom Layers Configuration”。这里简单看一下layers.idx
的配置内容:
- "dependencies":
- "BOOT-INF/lib/"
- "spring-boot-loader":
- "org/"
- "snapshot-dependencies":
- "application":
- "BOOT-INF/classes/"
- "BOOT-INF/classpath.idx"
- "BOOT-INF/layers.idx"
- "META-INF/"
从这里能清晰地看到各个分层对应的文件路径配置。
三、实际操作步骤
3.1 拉取基础镜像(可选)
如果拉取bellsoft/liberica-openjre-debian:17-cds
基础镜像比较困难,可以使用阿里云的镜像地址,拉取后再修改镜像tag。
# 从阿里云镜像仓库拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/pusher/liberica-openjre-debian:17-cds
# 修改镜像tag,使其符合项目使用要求
docker tag registry.cn-hangzhou.aliyuncs.com/pusher/liberica-openjre-debian:17-cds bellsoft/liberica-openjre-debian:17-cds
3.2 项目打包
使用Maven进行项目打包,跳过测试阶段。
# 执行Maven打包命令,清理之前的构建结果并打包项目,跳过测试
mvn clean package -DskipTests
3.3 构建镜像
根据Dockerfile构建镜像,并指定镜像名称和标签。
# 构建镜像,-t参数指定镜像名称为liujiajia/websocket,标签为1.0,最后的.表示Dockerfile所在目录
docker build -t liujiajia/websocket:1.0 .
3.4 运行镜像
将容器的8080端口映射到本地的8080端口,运行构建好的镜像。
# 运行镜像,-p参数进行端口映射,将容器的8080端口映射到本地8080端口
docker run -p 8080:8080 liujiajia/websocket:1.0
3.5 查看及进入容器
运行下面的命令可以查看正在运行的容器。
# 查看正在运行的容器列表
docker ps
如果需要进入容器进行查看或操作,可以使用以下命令,其中{container-id}
需要替换为实际的容器ID。
# 进入指定容器,-it参数表示以交互模式进入,bash为进入容器后执行的命令
docker exec -it {container-id} bash
进入容器后,查看application
目录下的文件,可以看到application.jsa
文件,这就是预生成的CDS缓存文件。
root@c9d989a4a0e3:/application# ls -l
total 32304
-rw-r--r-- 1 root root 9552 Mar 25 05:46 application.jar
-r--r--r-- 1 root root 33062912 Mar 25 05:46 application.jsa
drwxr-xr-x 2 root root 4096 Mar 25 05:46 lib
这个文件虽然会使镜像总大小变大,但能显著提升应用的启动速度。以本地WebSocket示例项目为例,正常启动大约需要3.7秒,而使用CDS缓存文件启动只需约2.4秒。如果不需要这个加速功能,可以从Dockerfile中移除生成该文件的步骤以及最后的-XX:SharedArchiveFile=application.jsa
参数。
通过以上这些方法,我们就能对Spring Boot应用的容器镜像进行优化,提高开发和部署的效率。大家在实际项目中可以根据自身需求灵活运用,希望这些内容对大家有所帮助!