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:

    1. The size of the base image. Choose Alpine as the base image to minimize the built-in OS software
    1. Dockerfile number of instruction layers. This requires us to optimize dockerfiles to merge as much as possible on a single line
    1. 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…
image