Spring Boot项目容器镜像优化实战

后端 潘老师 4周前 (03-25) 19 ℃ (0) 扫码查看

在开发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.idxclasspath.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应用的容器镜像进行优化,提高开发和部署的效率。大家在实际项目中可以根据自身需求灵活运用,希望这些内容对大家有所帮助!


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/back/16318.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】