51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Spring Boot 重用 Docker 层

1、简介 {#1简介}

Docker 是创建独立应用的事实标准。从 2.3.0 版开始,Spring Boot 包含了多项增强功能,可帮助我们创建高效的 Docker 镜像。例如:它允许将应用分解成不同的层。

换句话说,源代码位于自己的层中。因此,它可以独立重建,从而提高效率并缩短启动时间。本文将带你了解如何利用 Spring Boot 重用 Docker 层。

2、Docker 中的分层 jar {#2docker-中的分层-jar}

Docker 容器由基础镜像和额外的层组成。一旦层构建完成,它们将保持缓存状态。因此,后续的生成速度会更快:

docker 的层

对底层层级的更改也会重新构建上层层级。因此,不经常更改的层级应保持在底部,而经常更改的层级应放在顶部。

同样,Spring Boot 允许将工件(构建产物)内容映射到层中。默认的层映射如下:

spring boot 分层

你可以看到,应用有自己的层。修改源代码时,只会重新构建独立的层。loader 和依赖保持缓存,从而减少了 Docker 镜像的创建和启动时间。接下来看看如何使用 Spring Boot 实现这一点!

3、Spring Boot 创建高效的 Docker 镜像 {#3spring-boot-创建高效的-docker-镜像}

在传统的构建 Docker 镜像的方式中,Spring Boot使用的是 "fat jar" 方法。一个工件就包含了所有依赖项和应用源代码。因此,源代码的任何变化都会迫使我们重建整个层。

3.1、Spring Boot 分层配置 {#31spring-boot-分层配置}

Spring Boot 2.3.0 版引入了两个新功能来改进 Docker 镜像的生成:

  • Buildpack 支持提供了应用的 Java 运行时环境,因此现在可以跳过 Dockerfile,并自动构建 Docker 镜像。
  • 分层 JAR 可以帮助我们最大限度地利用 Docker 层的生成

在本文中,我们将对分层 JAR 方法进行扩展。

首先,在 Maven 中设置分层 JAR 。在打包工件时,生成层。

们检查一下JAR文件:

jar tf target/spring-boot-docker-0.0.1-SNAPSHOT.jar

你可以看到,在 fat jar 内的 BOOT-INF 文件夹中创建了新的 layers.idx 文件。当然,它将依赖、资源和应用源代码映射到独立的层:

BOOT-INF/layers.idx

同样,文件内容也细分为不同的存储层:

- "dependencies":
  - "BOOT-INF/lib/"
- "spring-boot-loader":
  - "org/"
- "snapshot-dependencies":
- "application":
  - "BOOT-INF/classes/"
  - "BOOT-INF/classpath.idx"
  - "BOOT-INF/layers.idx"
  - "META-INF/"

3.2、与层互动 {#32与层互动}

列出工件内部的层:

java -Djarmode=layertools -jar target/docker-spring-boot-0.0.1.jar list

结果是 layers.idx 文件内容的简单视图:

dependencies
spring-boot-loader
snapshot-dependencies
application

还可以将层提取到文件夹中:

java -Djarmode=layertools -jar target/docker-spring-boot-0.0.1.jar extract

接下来,可以在 Dockerfile 中重用文件夹:

$ ls
application/
snapshot-dependencies/
dependencies/
spring-boot-loader/

3.3、Dockerfile 配置 {#33dockerfile-配置}

要充分利用 Docker 的功能,需要在镜像中添加层。

首先,将 Fat Jar 添加到基础镜像中:

FROM adoptopenjdk:11-jre-hotspot as builder
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar

其次,提取工件的层:

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

最后,复制提取的文件夹,添加相应的 Docker 层:

FROM adoptopenjdk:11-jre-hotspot
COPY --from=builder dependencies/ ./
COPY --from=builder snapshot-dependencies/ ./
COPY --from=builder spring-boot-loader/ ./
COPY --from=builder application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

通过这种配置,当更改源代码时,只会重建应用层。其他部分将保持缓存状态。

4、自定义层 {#4自定义层}

看起来一切都运行得很顺利。但是,如果仔细观察,就会发现依赖层并没有在构建之间共享。也就是说,它们都在一个单独的层级中,甚至包括内部依赖。因此,如果我们更改了内部库的类,将会重新构建所有的依赖层级。

4.1、Spring Boot 自定义层配置 {#41spring-boot-自定义层配置}

在 Spring Boot 中,可以通过单独的配置文件来调整自定义层:

<layers xmlns="http://www.springframework.org/schema/boot/layers"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/boot/layers
                     https://www.springframework.org/schema/boot/layers/layers-2.3.xsd">
    <application>
        <into layer="spring-boot-loader">
            <include>org/springframework/boot/loader/**</include>
        </into>
        <into layer="application" />
    </application>
    <dependencies>
        <into layer="snapshot-dependencies">
            <include>*:*:*SNAPSHOT</include>
        </into>
        <into layer="dependencies" />
    </dependencies>
    <layerOrder>
        <layer>dependencies</layer>
        <layer>spring-boot-loader</layer>
        <layer>snapshot-dependencies</layer>
        <layer>application</layer>
    </layerOrder>
</layers>

如上,将依赖和资源映射并排序到层中。此外,还可以添加任意数量的自定义层。

将文件命名为 layers.xml。然后,在 Maven 中,配置该文件以自定义层:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <layers>
            <enabled>true</enabled>
            <configuration>${project.basedir}/src/layers.xml</configuration>
        </layers>
    </configuration>
</plugin>

打包工件,结果类似于默认行为

4.2、添加新的层 {#42添加新的层}

创建一个内部依赖(internal-dependencies),添加应用类:

<into layer="internal-dependencies">
    <include>com.baeldung.docker:*:*</include>
</into>

排序新的层:

<layerOrder>
    <layer>internal-dependencies</layer>
</layerOrder>

如果列出 Fat Jar 内的层,就会出现新的 internal-dependencies

dependencies
spring-boot-loader
internal-dependencies
snapshot-dependencies
application

4.3、Dockerfile 配置 {#43dockerfile-配置}

提取后,就可以将新的 internal 层添加到 Docker 镜像中:

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

如果生成镜像,就会看到 Docker 是如何将 internal-dependencies 构建为一个新层的:

$ mvn package
$ docker build -f src/main/docker/Dockerfile . --tag spring-docker-demo
....
Step 8/11 : COPY --from=builder internal-dependencies/ ./
 ---> 0e138e074118
.....

然后,就可以在历史记录中查看 Docker 镜像中各层的组成:

$ docker history --format "{{.ID}} {{.CreatedBy}} {{.Size}}" spring-docker-demo
c0d77f6af917 /bin/sh -c #(nop)  ENTRYPOINT ["java" "org.s... 0B
762598a32eb7 /bin/sh -c #(nop) COPY dir:a87b8823d5125bcc4... 7.42kB
80a00930350f /bin/sh -c #(nop) COPY dir:3875f37b8a0ed7494... 0B
0e138e074118 /bin/sh -c #(nop) COPY dir:db6f791338cb4f209... 2.35kB
e079ad66e67b /bin/sh -c #(nop) COPY dir:92a8a991992e9a488... 235kB
77a9401bd813 /bin/sh -c #(nop) COPY dir:f0bcb2a510eef53a7... 16.4MB
2eb37d403188 /bin/sh -c #(nop)  ENV JAVA_HOME=/opt/java/o... 0B

可以看到,该层现在包含了项目的 internal dependencies。

5、总结 {#5总结}

本文介绍了如何使用 Spring Boot 创建分层 Jar,以高效地生成 Docker 镜像。


Ref:https://www.baeldung.com/docker-layers-spring-boot

赞(0)
未经允许不得转载:工具盒子 » Spring Boot 重用 Docker 层