2

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

 3 years ago
source link: http://dockone.io/article/2277178
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

越来越多的项目容器化,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中的一条指令。每一层都是基础层上变化的增量,而且自下而上的进行增量构建。

Docker镜像层

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

另外当我们构建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

上面就是该模式下构建的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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK