知識庫 / Spring / Spring Boot RSS 訂閱

利用 Docker 層級與 Spring Boot

Docker,Spring Boot
HongKong
5
12:44 PM · Dec 06 ,2025

1. 引言

Docker 是創建自包含應用程序的行業標準。從 2.3.0 版本開始,Spring Boot 包含了多個增強功能,以幫助我們創建高效的 Docker 鏡像。因此,它 允許將應用程序分解為不同的層

換句話説,源代碼位於其自身的層中。因此,它可以獨立重建,從而提高效率並縮短啓動時間。在本教程中,我們將學習如何利用 Spring Boot 的新功能來重用 Docker 層。

2. Docker 中的分層罐

Docker 容器由基礎鏡像和附加層組成。一旦層構建完成,它們將保持緩存狀態。因此,後續生成將更加快速:

對底層層中的更改也會重新構建頂層層。因此,變化頻率較低的層應位於底部,而變化頻率較高的層應位於頂部。

同樣地,Spring Boot 允許將 Artifact 的內容映射到層中。以下是默認層映射:

如我們所見,應用程序擁有自己的層。修改源代碼時,僅重新構建獨立的層。加載器和依賴項保持緩存,從而減少 Docker 鏡像的創建和啓動時間。

3. 使用 Spring Boot 構建高效 Docker 鏡像

在傳統的 Docker 鏡像構建方式中,Spring Boot 採用“胖 JAR”方法。因此,單個 Artifact 包含了所有依賴項和應用程序的源代碼。任何源代碼的修改都迫使整個層重新構建。

3.1. 使用 Spring Boot 配置分層

Spring Boot 2.3.0 版本引入了兩個新功能以改進 Docker 鏡像生成:

  • 構建包支持 提供應用程序的 Java 運行時,因此現在可以跳過 Dockerfile 並自動構建 Docker 鏡像。
  • 分層 JAR 有助於我們充分利用 Docker 鏡像分層功能。

在本教程中,我們將擴展分層 JAR 方法。

最初,我們將分層 JAR 設置在 Maven 中。在打包 Artifact 時,我們將生成層。讓我們檢查 JAR 文件:

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

我們可以看到,在胖 JAR 包內的 BOOT-INF 文件夾中創建了一個新的 .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. 與圖層交互

讓我們列出該 Artifact 中的圖層:

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 配置文件

為了充分發揮 Docker 的功能,我們需要將層添加到鏡像中。

首先,我們將“胖 JAR”文件添加到基礎鏡像中:

FROM openjdk:17-jdk-alpine as builder
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar

第二,我們來提取該文物中的各層:

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

最後,讓我們複製提取的文件夾以添加相應的 Docker 層:

FROM openjdk:17-jdk-alpine
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.1. 使用 Spring 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. 添加新層

讓我們創建一個內部依賴,將我們的應用程序類添加進來:

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

此外,我們還將添加新的層:

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

結果,如果我們列出脂肪罐內部的層級,新的內部依賴關係就會出現:

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

4.3. Dockerfile 配置文件

提取完成後,我們可以將新的內部層添加到我們的 Docker 鏡像中:

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

因此,當我們生成圖像時,我們會看到 Docker 如何將內部依賴項構建為新的層:

$ 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

如我們所見,該層現在包含項目的內部依賴關係。

5. 結論

在本教程中,我們演示瞭如何生成高效的 Docker 鏡像。簡而言之,我們利用 Spring Boot 的新特性創建了分層 JAR 包。對於簡單的項目,我們可以使用默認配置。我們還展示了一種更高級的配置,用於重用層。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.