This is an original article. Welcome any form of reprint, but please be sure to note the source cold https://lltx.github.io.
background
In the actual production of containerized deployment, we often encounter the situation of large Docker images and slow deployment and release
Factors affecting the size of docker image mainly include the following three aspects:
-
- The size of the base image. Choose Alpine as the base image to minimize the built-in OS software
-
- Dockerfile number of instruction layers. This requires us to optimize dockerfiles to merge as much as possible on a single line
-
- The size of the jar applied. That’s what I want to share with you today
The helloworld mirror
Let’s start by building a simple Web HelloWorld based on Spring Boot 2.3.0, and then build the image.
FROM adoptopenjdk:11-jre-hotspot as builder
WORKDIR application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
ENTRYPOINT ["java", "-jar application.jar"]
Copy the code
Docker build --build-arg JAR_FILE=./demo-layer-0.0.1- snapshot.jar. -t demo:v1.0Copy the code
View hierarchical mirror information
Docker Inspect Demo :v1.0 to look at the hash value of each layer of this image
// Demo :v1.0: layerlayer: [ "sha256:b7f7d2967507ba709dbd1dd0426a5b0cdbe1ff936c131f8958c8d0f910eea19e", "sha256:a6ebef4a95c345c844c2bf43ffda8e36dd6e053887dd6e283ad616dcc2376be6", "sha256:838a37a24627f72df512926fc846dd97c93781cf145690516e23335cc0c27794", "sha256:28ba7458d04b8551ff45d2e17dc2abb768bf6ed1a46bb262f26a24d21d8d7233", "sha256:55c91231ac46fdd63c3cf84b88b11f8a04c1870482dcff033029a601bc50e1ab", "sha256:9816c2d488754509f6024a267738b1e5fe33a7cd33bd25c5a9cdf6d4d7bfed1d", "sha256:f5fb3f91797d57a92f3f7e033398b8edd094df664db849a4950eabf2f5474535", "sha256:b87d2ff74819f83038ea2f89736a19cfcf99bfa080b8017d191c900a09a7524f" ]Copy the code
Helloworld upgrade rebuild
We partially modify the HelloWorld program (simulating the development process) and rebuild the image
Docker build --build-arg JAR_FILE=./demo-layer-0.0.1- snapshot.jar. -t demo:v1.1Copy the code
Docker inspect Demo :v1.1
// Demo :v1.1 demo:v1.1 demo:v1.1 demo:v1.1 demo:v1.1 [ "sha256:b7f7d2967507ba709dbd1dd0426a5b0cdbe1ff936c131f8958c8d0f910eea19e", "sha256:a6ebef4a95c345c844c2bf43ffda8e36dd6e053887dd6e283ad616dcc2376be6", "sha256:838a37a24627f72df512926fc846dd97c93781cf145690516e23335cc0c27794", "sha256:28ba7458d04b8551ff45d2e17dc2abb768bf6ed1a46bb262f26a24d21d8d7233", "sha256:55c91231ac46fdd63c3cf84b88b11f8a04c1870482dcff033029a601bc50e1ab", "sha256:9816c2d488754509f6024a267738b1e5fe33a7cd33bd25c5a9cdf6d4d7bfed1d", "sha256:f5fb3f91797d57a92f3f7e033398b8edd094df664db849a4950eabf2f5474535", "sha256:c1b6350d545fea605e0605c4bfd7f4529cfeee3f6759750d6a5ddeb9c882fc8f" ]Copy the code
Compare v1.0 and V1.1 mirrors
By comparing the image summary information for v1.0 and V1.1, we can see that only the last layer is changed. Dive is a Docker image analysis tool written in Go to determine what the last layer is doing
Dive Demo :v1.0, “As you will see it is the difference in the last jar that causes the 16M content to be rebuilt. When your business jar is large, this is the performance bottleneck”
Spring Boot packages and decrypts by default
By default, spring Boot builds the JAR and unzips it to see the following directory structure. The default is to be treated as a whole, as a separate layer when building the image, “with no distinction between business classes and referenced third-party jars.”
META-INF/
MANIFEST.MF
org/
springframework/
boot/
loader/
BOOT-INF/
classes/
lib/
Copy the code
layer jar
As you can see from the above, the idea of hierarchical JAR is to subdivide the JAR according to the rules. The business class and the three-party JAR correspond to the different layers of the mirror. In this way, the business code can be changed with very few changes to improve the construction speed.
Enable hierarchical packaging
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
Copy the code
Write support for hierarchical Dockerfiles
The core is to split the JAR through the layerTools provided by Spring Boot and then load it separately through the COPY instruction
FROM adoptopenjdk:11-jre-hotspot as builder WORKDIR application ARG JAR_FILE=target/*.jar COPY ${JAR_FILE} application.jar RUN java -Djarmode=layertools -jar application.jar extract FROM adoptopenjdk:11-jre-hotspot 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/ ./ ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]Copy the code
Build a new image and view the hierarchical information
Docker build --build-arg JAR_FILE=./demo-layer-0.0.1- snapshot.jar. -t demo:v2.0Copy the code
"Layers": [
"sha256:b7f7d2967507ba709dbd1dd0426a5b0cdbe1ff936c131f8958c8d0f910eea19e",
"sha256:a6ebef4a95c345c844c2bf43ffda8e36dd6e053887dd6e283ad616dcc2376be6",
"sha256:838a37a24627f72df512926fc846dd97c93781cf145690516e23335cc0c27794",
"sha256:28ba7458d04b8551ff45d2e17dc2abb768bf6ed1a46bb262f26a24d21d8d7233",
"sha256:55c91231ac46fdd63c3cf84b88b11f8a04c1870482dcff033029a601bc50e1ab",
"sha256:9816c2d488754509f6024a267738b1e5fe33a7cd33bd25c5a9cdf6d4d7bfed1d",
"sha256:f5fb3f91797d57a92f3f7e033398b8edd094df664db849a4950eabf2f5474535",
"sha256:06fe18cf8ae7384f120f2c6a3a33b31999dd0460cf1edae45e8f13adeab35942",
"sha256:16cf814564b8a667fcc9f07314b6084cbef8dc8c0a6565c7a2d91d74faf7e7de",
"sha256:94be40f716016b68cdd6b99d2cb8154acf8475c3a170a898a22f95a8ef40ffd3",
"sha256:427d87d6a5fe6da13cb4233939c3a1ff920bc6b4d2f14b5d78af7aef98fda7de"
]
Copy the code
Modify the code part of the business code, rebuild
Docker build --build-arg JAR_FILE=./demo-layer-0.0.1- snapshot.jar. -t demo:v2.1Copy the code
"Layers": [
"sha256:b7f7d2967507ba709dbd1dd0426a5b0cdbe1ff936c131f8958c8d0f910eea19e",
"sha256:a6ebef4a95c345c844c2bf43ffda8e36dd6e053887dd6e283ad616dcc2376be6",
"sha256:838a37a24627f72df512926fc846dd97c93781cf145690516e23335cc0c27794",
"sha256:28ba7458d04b8551ff45d2e17dc2abb768bf6ed1a46bb262f26a24d21d8d7233",
"sha256:55c91231ac46fdd63c3cf84b88b11f8a04c1870482dcff033029a601bc50e1ab",
"sha256:9816c2d488754509f6024a267738b1e5fe33a7cd33bd25c5a9cdf6d4d7bfed1d",
"sha256:f5fb3f91797d57a92f3f7e033398b8edd094df664db849a4950eabf2f5474535",
"sha256:06fe18cf8ae7384f120f2c6a3a33b31999dd0460cf1edae45e8f13adeab35942",
"sha256:16cf814564b8a667fcc9f07314b6084cbef8dc8c0a6565c7a2d91d74faf7e7de",
"sha256:94be40f716016b68cdd6b99d2cb8154acf8475c3a170a898a22f95a8ef40ffd3",
"sha256:8a20c60d361696a4e480fb6fbe1daf8b88bc54c579a98e209da1fb76e25de5aa"
]
Copy the code
View images at different layers
The last layer changes in size to 5KB
conclusion
- Changes of 16MB -> 5KB are more obvious during actual development
- Layering logic can be specified using the Spring Boot Maven Plugin, as described in the official documentation
- IO /spring-boot…