# springboot-layers
**Repository Path**: wxdfun/springboot-layers
## Basic Information
- **Project Name**: springboot-layers
- **Description**: springboot docker分层打包
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 6
- **Forks**: 1
- **Created**: 2020-06-15
- **Last Updated**: 2024-04-03
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 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打包插件
```xml
org.springframework.boot
spring-boot-maven-plugin
```
根目录的DockerFile文件
```dockerfile
FROM openjdk:8-jre
MAINTAINER ttzommed@foxmail.com
WORKDIR application
EXPOSE 36665
ADD ./target/*.jar ./app.jar
CMD java -jar app.jar
```
然后我们执行相关命令打包
```shell
mvn clean package
docker build . -t hc.registry.docker.com:5000/springboot-layers:1.0.0
```

先来看看这个docker镜像的分层情况,分别用`docker inspect`和`dive`来看看。
`docker inspect hc.registry.docker.com:5000/springboot-layers:1.0.0`

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

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

```shell
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`

docker镜像分层文件中只有最后一处与上次编译不一样。
`dive hc.registry.docker.com:5000/springboot-layers:1.0.1`

可以看出我们只改动了几个字符,docker打包的时候依然给我们重新上传了springboot的全量jar包。
## 改造
最近springboot2.3.0发布,支持springboot分层打包,我们看看新特性怎么结合docker使用。升级springboot依赖到2.3.0.RELEASE。
```xml
org.springframework.boot
spring-boot-starter-parent
2.3.0.RELEASE
```
在SpringBoot-maven插件中加入配置支持分层打包
```xml
org.springframework.boot
spring-boot-maven-plugin
true
```
再次进行打包分析操作`mvn clean package`,现在我们可以用下面命令来看我们springboot2.3.0分层打包编译的jar包结构
`java -Djarmode=layertools -jar target/springboot-layers-1.0.0.jar list`

可以看到layertools识别出jar包内将依赖打包到不同文件夹中,接下来我们改造下原有的dockerfile
```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 ttzommed@foxmail.com
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`

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

```shell
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`

仍然只有最后一个文件层有变动,用dive来分析看看
`dive hc.registry.docker.com:5000/springboot-layers:2.0.1`

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


可以发现跟我们想的一样,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`,然后看看下图的里面的文件内容就真相大白了。
