使用Docker緩存Maven依賴項
- Docker
- Maven
一、簡介
在本教程中,我們將展示如何在 Docker 中構建 Maven 項目。首先,我們將從一個簡單的單模塊 Java 項目開始,並展示如何利用 Docker 中的多階段構建來對構建過程進行 docker 化。接下來,我們將展示如何使用 Buildkit 來緩存多個構建之間的依賴關係。最後,我們將介紹如何在多模塊應用程序中利用層緩存。
2.多階段分層構建
在本文中,我們將使用 Guava 作為依賴項創建一個簡單的 Java 應用程序。我們將使用 maven-assembly 插件創建一個胖 JAR。代碼和 Maven 配置將從本文中省略,因為它們不是主要主題。
多階段構建是優化 Docker 構建過程的好方法。它們使我們能夠將整個過程保存在一個文件中,還可以幫助我們保持 Docker 映像盡可能小。在第一階段,我們將運行 Maven 構建並創建我們的胖 JAR,在第二階段,我們將復制 JAR 並定義一個入口點:
FROM maven:alpine as build
ENV HOME=/usr/app
RUN mkdir -p $HOME
WORKDIR $HOME
ADD . $HOME
RUN mvn package
FROM openjdk:8-jdk-alpine
COPY --from=build /usr/app/target/single-module-caching-1.0-SNAPSHOT-jar-with-dependencies.jar /app/runner.jar
ENTRYPOINT java -jar /app/runner.jar
這種方法可以讓我們保持最終的 Docker 鏡像更小,因為它不包含 Maven 可執行文件或我們的源代碼。
讓我們創建 Docker 鏡像:
docker build -t maven-caching .
接下來,讓我們從鏡像中啟動一個容器:
docker run maven-caching
當我們更改代碼中的某些內容並重新運行構建時,我們會注意到 Maven package
任務之前的所有命令都被緩存並立即執行。由於我們的代碼更改比項目依賴項更頻繁,我們可以使用 Docker 層緩存將依賴項下載和代碼編譯分開來縮短構建時間:
FROM maven:alpine as build
ENV HOME=/usr/app
RUN mkdir -p $HOME
WORKDIR $HOME
ADD pom.xml $HOME
RUN mvn verify --fail-never
ADD . $HOME
RUN mvn package
FROM openjdk:8-jdk-alpine
COPY --from=build /usr/app/target/single-module-caching-1.0-SNAPSHOT-jar-with-dependencies.jar /app/runner.jar
ENTRYPOINT java -jar /app/runner.jar
當我們只更改代碼時運行後續構建會快得多,因為 Docker 將從緩存中獲取層。
3. 使用 BuildKit 進行緩存
Docker 版本 18.09 引入了 BuildKit 作為對現有構建系統的大修。大修背後的想法是提高性能、存儲管理和安全性。我們可以利用 BuildKit 來保持多個構建之間的狀態。這樣,Maven 不會每次都下載依賴項,因為我們有永久存儲。要在我們的 Docker 安裝中啟用 BuildKit,我們需要編輯daemon.json
文件:
...
{
"features": {
"buildkit": true
}}
...
啟用 BuildKit 後,我們可以將 Dockerfile 更改為:
FROM maven:alpine as build
ENV HOME=/usr/app
RUN mkdir -p $HOME
WORKDIR $HOME
ADD . $HOME
RUN --mount=type=cache,target=/root/.m2 mvn -f $HOME/pom.xml clean package
FROM openjdk:8-jdk-alpine
COPY --from=build /usr/app/target/single-module-caching-1.0-SNAPSHOT-jar-with-dependencies.jar /app/runner.jar
ENTRYPOINT java -jar /app/runner.jar
當我們更改代碼或pom.xml
文件時,Docker 將始終執行 ADD 和 RUN Maven 命令。首次運行時構建時間將是最長的,因為 Maven 必須下載依賴項。隨後的運行將使用本地依賴項並執行得更快。
這種方法需要維護 Docker 卷作為依賴項的存儲。有時,我們必須強制 Maven 使用 Dockerfile 中的-U
標誌更新我們的依賴項。
4. 多模塊 Maven 項目的緩存
在前面的部分中,我們展示瞭如何利用不同的方法來加快單模塊 Maven 項目的 Docker 映像的構建時間。對於更複雜的應用,這些方法不是最佳的。多模塊 Maven 項目通常有一個模塊作為我們應用程序的入口點。一個或多個模塊包含我們的邏輯並被列為依賴項。
由於子模塊被列為依賴項,它們將阻止 Docker 進行層緩存並觸發 Maven 再次下載所有依賴項。 BuildKit 的這個解決方案在大多數情況下都很好,但正如我們所說,它可能需要不時強制更新以獲取更新的子模塊。為了避免這種情況,我們可以將項目分層並使用 Maven 增量構建:
FROM maven:alpine as build
ENV HOME=/usr/app
RUN mkdir -p $HOME
WORKDIR $HOME
ADD pom.xml $HOME
ADD core/pom.xml $HOME/core/pom.xml
ADD runner/pom.xml $HOME/runner/pom.xml
RUN mvn -pl core verify --fail-never
ADD core $HOME/core
RUN mvn -pl core install
RUN mvn -pl runner verify --fail-never
ADD runner $HOME/runner
RUN mvn -pl core,runner package
FROM openjdk:8-jdk-alpine
COPY --from=build /usr/app/runner/target/runner-0.0.1-SNAPSHOT-jar-with-dependencies.jar /app/runner.jar
ENTRYPOINT java -jar /app/runner.jar
在這個 Dockerfile 中,我們複製所有pom.xml
文件並增量構建每個子模塊,最後打包整個應用程序。經驗法則是我們構建的子模塊在鏈的後期更頻繁地更改。
5. 結論
在本文中,我們介紹瞭如何使用 Docker 構建 Maven 項目。首先,我們介紹瞭如何利用分層來緩存不經常更改的部分。接下來,我們介紹瞭如何使用 BuildKit 來保持構建之間的狀態。最後,我們展示瞭如何使用增量構建構建多模塊 Maven 項目。