优化Spring Boot应用Docker镜像,提高CI/CD效率


越来越多的项目容器化,Docker已经成为软件开发中的重要工具。通常我们可以通过如下的DockerfileSpring Boot应用的fat jar打包成Docker镜像:
FROM adoptopenjdk:8-jre-hotspot  
ARG JAR_FILE=target/*.jar  
COPY ${JAR_FILE} app.jar  
EXPOSE 8080  
ENTRYPOINT ["java","-jar","/app.jar"]   

看起来不错,但是你会发现如果我们修改了业务代码,镜像都会重新构建,哪怕你仅仅修改了一个字符串。如果你使用了CI/CD去构建部署Docker镜像,你会发现有时候CI管道中构建的时间会很长,甚至卡住不动,特别在镜像比较多的时候这种感觉非常明显。所以我们项目要优化这个地方。

Docker的分层机制

要优化就要了解Docker镜像的构建分层机制。Docker镜像由很多层组成,每个层代表Dockerfile中的一条指令。每一层都是基础层上变化的增量,而且自下而上的进行增量构建。
1.png

Docker镜像层

这种机制其实也是Docker名称的由来,就像码头工人在码货物一样。
2.jpg

另外当我们构建Docker镜像时,它会被分层提取并缓存在主机中,这些层可以被重用,这就给了我们优化的机会。

就像上面的集装箱,我们如果把容易变动的集装箱放在底层,每次变动我们就需要把它上面的移开;如果我们把它置于底层就可以减少工作量。对于Docker镜像的构建构建也是这样。

Spring Boot 镜像的优化

Spring Boot的fat jar如果能拆分成一层一层构建,把重复的层从主机缓存中复用起来就可以大大提高效率。所以按照变动的频率Spring Boot应用可以划分如下的层级:
  • dependencies (依赖项一般变化不大)
  • spring-boot-loader(Spring Boot加载器变化也不大)
  • snapshot-dependencies (快照依赖,为快照版本的依赖,更新迭代的会快一些)
  • application (业务层,也就是我们最频繁变动的)


Spring Boot 2.3起,Spring Boot提供了spring-boot-jarmode-layertools的jar包,该jar将作为依赖项添加到应用的jar中。通过layertoolsjar模式启动jar:
$ java -Djarmode=layertools -jar my-app.jar

就会生成上述四种层级的索引文件layers.idx
3.png

上面就是该模式下构建的Spring Boot 应用jar的信息,我们可以看到这两个东西。


这个功能依赖spring-boot-maven-plugin插件。
我们只需要将Dockerfile修改为:
# 第一阶段使用layertools的extract命令将应用程序拆分为多个层,本次构建标记为builder  
FROM adoptopenjdk:8-jre-hotspot as builder  
WORKDIR application  
ARG JAR_FILE=target/*.jar  
COPY ${JAR_FILE} app.jar  
RUN java -Djarmode=layertools -jar app.jar extract  

#  第二阶段从分层中复制并构建镜像  
FROM adoptopenjdk:8-jre-hotspot  
WORKDIR application  
# 从上面构建的builder中复制层,注意保证层的顺序  
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"] 

就可以复用主机中的Docker缓存层来加速构建效率。构建命令为:
docker build --build-arg JAR_FILE=path/to/myapp.jar . -tag demo

然后会输出:
Sending build context to Docker daemon  41.87MB  
Step 1/12 : adoptopenjdk:8-jre-hotspot as builder  
– -> 973c18dbf567  
Step 2/12 : WORKDIR application  
– -> Using cache  
– -> b6b89995bd66  
Step 3/12 : ARG JAR_FILE=target/*.jar  
– -> Using cache  
– -> 2065a4ad00d4  
Step 4/12 : COPY ${JAR_FILE} app.jar  
– -> c107bce376f9  
Step 5/12 : RUN java -Djarmode=layertools -jar app.jar extract  
– -> Running in 7a6dfd889b0e  
Removing intermediate container 7a6dfd889b0e  
– -> edb00225ad75  
Step 6/12 : FROM  adoptopenjdk:8-jre-hotspot  
– -> 973c18dbf567  
Step 7/12 : WORKDIR application  
– -> Using cache  
– -> b6b89995bd66  
Step 8/12 : COPY – from=builder application/dependencies/ ./  
– -> Using cache  
– -> c9a01ed348a9  
Step 9/12 : COPY – from=builder application/spring-boot-loader/ ./  
– -> Using cache  
– -> e3861c690a96  
Step 10/12 : COPY – from=builder application/snapshot-dependencies/ ./  
– -> Using cache  
– -> f928837acc47  
Step 11/12 : COPY – from=builder application/application/ ./  
– -> 3a5f60a9b204  
Step 12/12 : ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]  
– -> Running in f1eb4befc4e0  
Removing intermediate container f1eb4befc4e0  
– -> 8575cc3ac2e3  
Successfully built 8575cc3ac2e3  
Successfully tagged demo:latest 



java -Djarmode=layertools -jar 不建议在启动脚本中使用,仅推荐在构建中使用 。

补充

关于插件目前有版本上的差异,在Spring Boot 2.3中需要:
<build>  
<plugins>  
<plugin>  
  <groupId>org.springframework.boot</groupId>  
  <artifactId>spring-boot-maven-plugin</artifactId>  
  <configuration>  
      <layers>  
        <enabled>true</enabled>  
      </layers>  
  </configuration>  
</plugin>  
...  
</plugins>  
...  
</build>

Spring Boot 2.4.x以上版本默认:
<build>  
<plugins>  
    <plugin>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-maven-plugin</artifactId>  
        <!--当然你可以修改layers.enabled为false以关闭此功能。-->  
    </plugin>  
</plugins>  
</build>

通过启用分层构建Spring Boot应用镜像,我明显感觉到推送镜像的速度快了不少(当然这是依赖没有改动的情况下);另外从远程拉取镜像时只需要拉取变化的层,速度也明显加快了;对构建其实上是构建了两次,虽然配合缓存,效率其实变化不大。也就是说在镜像的网络传输上分层构建有明显的优势,值得一试。

原文链接:https://mp.weixin.qq.com/s/JMcBd6OULcVd9aRKyEGc0w

0 个评论

要回复文章请先登录注册