SpringBoot2.3.0 + Docker实现分层打包

SpringBoot + Docker分层打包

背景

SpringBoot默认使用org.springframework.boot:spring-boot-maven-plugin Maven插件把项目编译成jar包。默认编译的jar包是一个整体,通过java -jar命令可直接启动。结合docker后,我们可以通过DockerFile或者Docker Compose方式打包成Docker镜像。但每次Maven会将SpringBoot项目文件编译出一个全量jar包在target文件夹下,其jar包内包含我们自己写的代码和依赖的第三方jar包,常常一个jar包在100M上下,这导致在结合docker打包的情况下,每次docker push都会上传全量的jar包。最近SpringBoot2.3.0发布,更新包含了支持分层打包,下面我们看看SpringBoot结合Docker如何实现分层打包。

demo项目仓库:https://gitee.com/wxdfun/springboot-layers.git

分析

SpringBoot项目如何编译成jar包以及Docker使用方式就不介绍了,可自行百度,直接步入今天的正题。文章中会用到一款docker镜像分析工具dive,安装和使用可以参考文档https://github.com/wagoodman/dive

先来看看原来的SpringBoot打包和DockerFile编译镜像。我们新建一个简单的demo项目来做下测试。

pom配置SpringBoot打包插件

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

根目录的DockerFile文件

FROM openjdk:8-jre

MAINTAINER [email protected]

WORKDIR application

EXPOSE 36665

ADD ./target/*.jar ./app.jar

CMD java  -jar app.jar

然后我们执行相关命令打包

mvn clean package
docker build . -t hc.registry.docker.com:5000/springboot-layers:1.0.0

docker编译镜像

先来看看这个docker镜像的分层情况,分别用docker inspectdive来看看。

docker inspect hc.registry.docker.com:5000/springboot-layers:1.0.0

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WUNntqU3-1592193616881)(https://imgb.hnhcyy.com/ctr_cloud/doc/md/20200615104414.jpg)]

dive hc.registry.docker.com:5000/springboot-layers:1.0.0

dive_1.0.0

接下来我们更改一点点java代码,重新进行上面步骤看看问题。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1iaRTW16-1592193616883)(https://imgb.hnhcyy.com/ctr_cloud/doc/md/20200615105447.jpg)]

mvn clean package
docker build . -t hc.registry.docker.com:5000/springboot-layers:1.0.1

docker inspect hc.registry.docker.com:5000/springboot-layers:1.0.1

inspect_1.0.1

docker镜像分层文件中只有最后一处与上次编译不一样。

dive hc.registry.docker.com:5000/springboot-layers:1.0.1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aCNRsyij-1592193616885)(https://imgb.hnhcyy.com/ctr_cloud/doc/md/20200615105947.jpg)]

可以看出我们只改动了几个字符,docker打包的时候依然给我们重新上传了springboot的全量jar包。

改造

最近springboot2.3.0发布,支持springboot分层打包,我们看看新特性怎么结合docker使用。升级springboot依赖到2.3.0.RELEASE。

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/>
    </parent>

在SpringBoot-maven插件中加入配置支持分层打包

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <layers>
                        <enabled>true</enabled>
                    </layers>
                </configuration>
            </plugin>
        </plugins>
    </build>

再次进行打包分析操作mvn clean package,现在我们可以用下面命令来看我们springboot2.3.0分层打包编译的jar包结构
java -Djarmode=layertools -jar target/springboot-layers-1.0.0.jar list

layertools_list

可以看到layertools识别出jar包内将依赖打包到不同文件夹中,接下来我们改造下原有的dockerfile

FROM openjdk:8-jre as builder

WORKDIR application

ADD ./target/*.jar ./app.jar

RUN java -Djarmode=layertools -jar app.jar extract

FROM openjdk:8-jre

MAINTAINER [email protected]

WORKDIR application

COPY --from=builder application/dependencies/ ./

COPY --from=builder application/spring-boot-loader/ ./

COPY --from=builder application/snapshot-dependencies/ ./

COPY --from=builder application/application/ ./

EXPOSE 36665

ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

这个dockerfile表示先进行一次临时镜像构建标记为builder,并加载一次全量jar包,然后执行java -Djarmode=layertools -jar app.jar extract命令将jar包分解为分层打包目录,再次构建一个新镜像,按照list的目录顺序分批将分层目录加载到docker镜像中。

再次构建docker镜像docker build . -t hc.registry.docker.com:5000/springboot-layers:2.0.0

docker inspect hc.registry.docker.com:5000/springboot-layers:2.0.0

inspect_2.0.0

可以看到在docker镜像的文件分层中,层级变多了。我们改动一点java代码再次构建看看。

改动2.0.1

mvn clean package
docker build . -t hc.registry.docker.com:5000/springboot-layers:2.0.1

再次分析docker镜像层级

docker inspect hc.registry.docker.com:5000/springboot-layers:2.0.1

inspect_2.0.1

仍然只有最后一个文件层有变动,用dive来分析看看

dive hc.registry.docker.com:5000/springboot-layers:2.0.1

dive_2.0.1

神奇的事情发生了,这次docker中的改动只有4.6k的文件变动。单独push 2.0.0和2.0.1 docker镜像验证下是否只重新上传这两处改动分层文件

push_2.0.0

push_2.0.1

可以发现跟我们想的一样,2.0.1 docker镜像push的改动文件正是上面标注的仅有4.6k的文件,而上传的分层文件也大大缩小,最后启动我们的镜像即可验证成功了。

docker run -p 36665:36665 hc.registry.docker.com:5000/springboot-layers:2.0.1

结果

结果

在配置了springboot2.3.0版本支持分层打包,并且dockerfile文件也改成相应的格式后,确实能达到我们预期的效果。我们每次重新上传的只是我们自己写的代码,第三方依赖、SpringBoot内部配置、快照依赖 ,这些SpringBoot都为我们打包到不同的文件夹下,再依靠docker的分层特征,分次加入文件即可达到分层打包的效果。有想一探究竟的朋友可以执行java -Djarmode=layertools -jar target/springboot-layers-2.0.1.jar extract,然后看看下图的里面的文件内容就真相大白了。

SpringBoot内部配置、快照依赖 ,这些SpringBoot都为我们打包到不同的文件夹下,再依靠docker的分层特征,分次加入文件即可达到分层打包的效果。有想一探究竟的朋友可以执行java -Djarmode=layertools -jar target/springboot-layers-2.0.1.jar extract,然后看看下图的里面的文件内容就真相大白了。

image-20200615115315550

猜你喜欢

转载自blog.csdn.net/ttzommed/article/details/106759670