如何在 Docker 中建置多模組 Maven 項目
1. 概述
在本教程中,我們將學習如何為多模組 Maven 專案高效建置 Docker 映像。我們將首先探索多階段 Docker 構建,以充分利用 Docker 的快取機制。
然後,我們將研究使用 Google 的 Jib Maven 插件的替代方法。這個工具允許我們建立優化的 Docker 映像,而不需要Dockerfile
或 Docker 守護程式。
2.多模組Maven項目
多模組 Maven 應用程式由用於不同功能的單獨模組組成。 Maven 透過管理依賴關係並將這些模組組裝成單一可部署單元來建置應用程式。
對於本文中的程式碼範例,我們將使用一個帶有兩個 Maven 模組的基本 Spring Boot 專案 - 代表我們應用程式的domain
和API
。讓我們來視覺化這個 Maven 專案的結構:
+-- parent
+-- api
| `-- src
| `-- pom.xml
+-- domain
| `-- src
| `-- pom.xml
`-- pom.xml
如果我們看一下父模組的pom.xml
文件,我們可以預期它會擴展spring-boot-starter-parent
並包含domain
和api
模組:
<project>
<groupId>com.baeldung.docker-multi-module-maven</groupId>
<artifactId>parent</artifactId>
<packaging>pom</packaging>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
<relativePath />
</parent>
<modules>
<module>api</module>
<module>domain</module>
</modules>
<!-- other configuration -->
</project>
此外,我們將遵循乾淨的架構原則,確保所有原始碼依賴項都指向正確的方向。簡而言之,我們將確保api
模組依賴於domain
模組,而不是相反。
3. 多階段 Docker 構建
Docker 中的多階段建置允許我們在單一Dockerfile
中使用多個FROM
指令來建立更小、更有效率的映像。每個階段都可以用於不同的目的,例如編譯程式碼或打包應用程序,並且只有最後一個階段包含在最終映像中。
例如,我們的範例可以使用三個階段:拉取依賴項、建置應用程式和準備執行時間環境。讓我們使用這三個不同的部分來建立Dockerfile
:
# pre-fetch dependencies
FROM maven:3.8.5-openjdk-17 AS DEPENDENCIES
# build the jar
FROM maven:3.8.5-openjdk-17 AS BUILDER
# prepeare runtime env
FROM openjdk:17-slim
3.1.預獲取依賴項
DEPENDENCIES
階段將為我們的應用程式預取並快取 Maven 依賴項。讓我們先選擇我們首選的Maven 映像,然後複製三個pom.xml
檔:
FROM maven:3.8.5-openjdk-17 AS DEPENDENCIES
WORKDIR /opt/app
COPY api/pom.xml api/pom.xml
COPY domain/pom.xml domain/pom.xml
COPY pom.xml .
之後,我們需要使用maven-dependency-plugin
及其go-offline
目標來解析並下載pom.xml
檔案中指定的所有相依性。此外,我們將透過指定“-B”
選項以非互動模式運行該命令,並透過“-e”
提示所有錯誤:
RUN mvn -B -e org.apache.maven.plugins:maven-dependency-plugin:3.1.2:go-offline -DexcludeArtifactIds=domain
最後,我們新增了excludeArtifactIds
屬性以防止 Maven 下載特定工件。在這種情況下,它排除了domain
工件。因此, domain
JAR 將在本地構建,而不是從存儲庫獲取。
該命令確保當我們在下一階段運行建置過程時,所有依賴項都將在本地可用,並且不需要再次下載。
3.2.建立形象
為了建立鏡像,我們首先需要確保預取所有必需的依賴項並且原始程式碼可用。在BUILDER
階段,我們首先從DEPENDENCIES
階段複製必要的資源:
FROM maven:3.8.5-openjdk-17 AS BUILDER
WORKDIR /opt/app
COPY --from=DEPENDENCIES /root/.m2 /root/.m2
COPY --from=DEPENDENCIES /opt/app/ /opt/app
COPY api/src /opt/app/api/src
COPY domain/src /opt/app/domain/src
接下來,讓我們執行mvn clean install
來編譯程式碼並建立domain
和api
JAR 檔案。由於測試可能已提前運行,我們可以使用-DskipTests
來加快建置流程:
RUN mvn -B -e clean install -DskipTests
3.3.準備運作環境
在Dockerfile
的最後階段,我們將為應用程式設定最小的執行環境。我們將選擇應用程式將在其上運行的基礎映像,複製上一階段的 JAR 文件,並定義啟動它的入口點:
FROM openjdk:17-slim
WORKDIR /opt/app
COPY --from=BUILDER /opt/app/api/target/*.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
3.4.運行應用程式
最後,我們可以建置並運行圖像。我們也加入from-dockerfile
標籤來區分該映像:
docker build -t baeldung-demo:from-dockerfile .
docker run -p 8080:8080 baeldung-demo:from-dockerfile
不用說,如果我們向localhost:8080/api/countries
發送GET
請求,我們會注意到我們的應用程式已啟動並正在運行。
正如我們所看到的,多階段Dockerfile
透過將建置依賴項與最終執行時間環境隔離來簡化依賴項管理。此外,它還可以透過僅複製建置階段所需的工件來幫助我們縮小最終映像的大小,從而提高效率和安全性。
4. 使用 Jib 建置項目
我們也可以使用Jib等專用工具建構Docker映像。 Jib Maven 外掛程式是一個工具,可以直接從我們的 Maven 建置中為 Java 應用程式建置最佳化的 Docker 映像,而不需要Dockerfile
或 Docker 守護程式。
Jib 要求我們配置幾個關鍵屬性:
- Java 基礎鏡像
- 產生的 Docker 映像的名稱
- 我們應用程式的入口點
- 暴露的端口
讓我們將maven-jib-plugin
加入到 API 模組的pom.xml
中:
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<from>
<image>openjdk:17-slim</image>
</from>
<to>
<image>baeldung-demo:from-jib</image>
</to>
<container>
<mainClass>com.baeldung.api.Application</mainClass>
<ports>
<port>8080</port>
</ports>
</container>
</configuration>
</plugin>
之後我們就可以使用Maven來建構鏡像了:
mvn compile jib:dockerBuild
結果,Jib 建置了 Docker 映像,我們現在可以使用docker run
命令來執行應用程式:
docker run -p 8080:8080 baeldung-demo:from-jib
5. 結論
在本文中,我們學習如何為具有多個模組的 Maven 專案建立 Docker 映像。最初,我們手動建立了一個Dockerfile
,在其中複製了所有pom.xml
檔案、解析了所有依賴項並建置了映像。此外,我們也利用了Docker的多階段特性來充分利用其快取機制。
之後,我們探索了 Jib Maven 插件,並使用它來建立 Docker 映像,而無需Dockerfile
。 Jib 外掛程式有效地管理依賴關係並建立映像,而無需手動定義每個建置步驟的開銷。
與往常一樣,本文中使用的完整程式碼可以 在 GitHub 上找到。