SpringBoot + Docker layered packaging
background
SpringBoot uses the org.springframework.boot:spring-boot-maven-plugin
Maven plugin to compile the project into a jar package by default . The jar package compiled by default is a whole, which java -jar
can be started directly by command. After combining with docker, we can package it into a Docker image by DockerFile
or Docker Compose
. But every time Maven compiles the SpringBoot project file into a full jar package in the target folder, the jar package contains our own code and dependent third-party jar packages, often a jar package is above 100M, which leads to When combined with docker packaging, the docker push
full jar package will be uploaded each time . Recently, SpringBoot 2.3.0 was released, and the update includes support for layered packaging. Let's take a look at how SpringBoot combined with Docker can achieve layered packaging.
Demo project warehouse: https://gitee.com/wxdfun/springboot-layers.git
analysis
How to compile the SpringBoot project into a jar package and how to use Docker will not be introduced. You can go to Baidu and enter today's topic directly. A docker image analysis tool will be used in this article dive
. For installation and use, please refer to the document https://github.com/wagoodman/dive
Let's take a look at the original SpringBoot packaging and DockerFile compilation image. Let's create a simple demo project for testing.
Pom configuration SpringBoot packaging plugin
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
DockerFile file in the root directory
FROM openjdk:8-jre
MAINTAINER [email protected]
WORKDIR application
EXPOSE 36665
ADD ./target/*.jar ./app.jar
CMD java -jar app.jar
Then we execute the relevant command package
mvn clean package
docker build . -t hc.registry.docker.com:5000/springboot-layers:1.0.0
Let's take a look at the layering of this docker image first, use docker inspect
and dive
take a look respectively .
docker inspect hc.registry.docker.com:5000/springboot-layers:1.0.0
[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-WUNntqU3-1592193616881)(https://imgb.hnhcyy.com/ctr_cloud/doc/md/20200615104414.jpg )]
dive hc.registry.docker.com:5000/springboot-layers:1.0.0
Next, we change a little java code and repeat the above steps to see the problem.
[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (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
Only the last part of the docker image layered file is different from the last compilation.
dive hc.registry.docker.com:5000/springboot-layers:1.0.1
[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-aCNRsyij-1592193616885)(https://imgb.hnhcyy.com/ctr_cloud/doc/md/20200615105947.jpg )]
It can be seen that we only changed a few characters, and docker still uploaded the full jar package of springboot to us when packaging it.
Retrofit
Recently, springboot 2.3.0 was released, supporting springboot hierarchical packaging. Let's see how the new features can be combined with docker. Upgrade springboot to depend on 2.3.0.RELEASE.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/>
</parent>
Add configuration to support layered packaging in the SpringBoot-maven plugin
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
</plugins>
</build>
Perform the packaging analysis operation again mvn clean package
, and now we can use the following command to see the jar package structure of our springboot2.3.0 hierarchical packaging and compilation
java -Djarmode=layertools -jar target/springboot-layers-1.0.0.jar list
You can see that layertools recognizes that the dependencies in the jar package are packaged into different folders, and then we modify the original 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"]
This dockerfile means that a temporary image build is marked as builder, and the full jar package is loaded once, and then the java -Djarmode=layertools -jar app.jar extract
command is executed to decompose the jar package into hierarchical packaging directories, and a new image is built again, and the hierarchical directories are divided into batches according to the list of directories. Load into the docker image.
Build the docker image againdocker build . -t hc.registry.docker.com:5000/springboot-layers:2.0.0
docker inspect hc.registry.docker.com:5000/springboot-layers:2.0.0
You can see that in the file layering of the docker image, there are more levels. Let's change the java code and build it again.
mvn clean package
docker build . -t hc.registry.docker.com:5000/springboot-layers:2.0.1
Analyze the docker image level again
docker inspect hc.registry.docker.com:5000/springboot-layers:2.0.1
Still only the last file layer has changed, use dive to analyze it
dive hc.registry.docker.com:5000/springboot-layers:2.0.1
A magical thing happened, this time the changes in docker only have 4.6k file changes. Push the 2.0.0 and 2.0.1 docker images separately to verify whether only these two changed layered files are re-uploaded
It can be found that, as we thought, the 2.0.1 docker image push change file is the only 4.6k file marked above, and the uploaded hierarchical file is also greatly reduced. Finally, we can start our mirror to verify the success.
docker run -p 36665:36665 hc.registry.docker.com:5000/springboot-layers:2.0.1
result
After configuring the springboot 2.3.0 version to support hierarchical packaging, and the dockerfile file is also changed to the corresponding format, we can indeed achieve the desired effect. What we re-upload each time is only the code we wrote, third-party dependencies, SpringBoot internal configuration, snapshot dependencies, these SpringBoot are packaged for us in different folders, and then rely on the docker's hierarchical characteristics to add files in stages. The effect of layered packaging can be achieved. Friends who want to find out can do it java -Djarmode=layertools -jar target/springboot-layers-2.0.1.jar extract
, and then look at the contents of the file in the figure below to make the truth clear.
SpringBoot internal configuration, snapshot dependency, these SpringBoot are packaged in different folders for us, and then relying on the layered characteristics of docker, adding files in stages can achieve the effect of layered packaging. Friends who want to find out can do it java -Djarmode=layertools -jar target/springboot-layers-2.0.1.jar extract
, and then look at the contents of the file in the figure below to make the truth clear.