使用 Spring Boot 2.3.0.M1 建立 Docker 映像檔

工程 | Phil Webb | 2020年1月27日 | ...

Spring Boot 2.3.0.M1 剛剛發布,它帶來了一些有趣的新功能,可以幫助您將 Spring Boot 應用程式打包成 Docker 映像檔。在這篇部落格文章中,我們將看看開發人員建立 Docker 映像檔的典型方式,並展示如何使用這些新功能來改進它們。

常見的 Docker 技術

雖然一直以來都可以將 Spring Boot 產生的 fat JAR 檔轉換為 Docker 映像檔,但很容易產生次佳的結果。如果您在網路上搜尋「dockerize spring boot app」,很有可能會找到一篇文章或部落格文章建議您建立如下所示的 dockerfile

FROM openjdk:8-jdk-alpine
EXPOSE 8080
ARG JAR_FILE=target/my-application.jar
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

雖然這種方法運作良好,而且簡潔明瞭,但仍有一些地方不夠理想。

上述檔案的第一個問題是 JAR 檔未解壓縮。執行 fat JAR 檔始終存在一定的額外負擔,在容器化環境中,這可能會很明顯。通常最好解壓縮您的 JAR 檔,並以展開形式執行。

該檔案的第二個問題是,如果您頻繁更新應用程式,它的效率不高。Docker 映像檔是以層建構的,在這種情況下,您的應用程式及其所有相依性都放在單一層中。由於您重新編譯程式碼的頻率可能比升級您使用的 Spring Boot 版本更高,因此最好將內容稍微分開。如果您將 JAR 檔放在應用程式類別之前的層中,Docker 通常只需要變更最底層,就可以從其快取中取得其他層。

Spring Boot 2.3.0.M1 中引入了兩個新功能,以幫助改進這些現有技術:buildpack 支援和 layered jar。

Buildpacks

如果您曾經使用過應用程式平台,例如 Cloud Foundry 或 Heroku,那麼您可能已經使用過 buildpack,甚至可能沒有意識到!Buildpacks 是平台的一部分,它會取得您的應用程式並將其轉換為平台實際可以運行的東西。例如,Cloud Foundry 的 Java buildpack 會注意到您正在推送 `.jar` 檔案,並自動新增相關的 JRE。

直到最近,buildpacks 仍與平台緊密結合,您無法輕鬆地獨立使用它們。值得慶幸的是,它們現在已經解放,透過 Cloud Native Buildpacks,您可以使用它們來建立與 Docker 相容的映像檔,您可以在任何地方運行。

Spring Boot 2.3.0.M1 直接包含對 Maven 和 Gradle 的 buildpack 支援。這表示您只需輸入單一命令,即可快速將合理的映像檔放入您本地運行的 Docker daemon 中。對於 Maven,您可以輸入 `mvn spring-boot:build-image`,對於 Gradle,則是 `gradle bootBuildImage`。發布的映像檔名稱將是您的應用程式名稱,標籤將是版本。

讓我們看看一個使用 Maven 的範例

首先使用 start.spring.io 建立一個新的 Spring Boot 專案

$ curl https://start.spring.io/starter.zip -d bootVersion=2.3.0.M1 -d dependencies=web -o demo.zip $ unzip demo.zip

接下來,請確保您已安裝並運行本地 Docker,然後輸入

$ ./mvnw spring-boot:build-image

第一次運行需要一些時間,但後續呼叫會更快。您應該在建置日誌中看到類似這樣的內容

[INFO] Building image 'docker.io/library/demo:0.0.1-SNAPSHOT' [INFO] [INFO] > Pulling builder image 'docker.io/cloudfoundry/cnb:0.0.43-bionic' 100% [INFO] > Pulled builder image 'cloudfoundry/cnb@sha256:c983fb9602a7fb95b07d35ef432c04ad61ae8458263e7fb4ce62ca10de367c3b' [INFO] > Pulling run image 'docker.io/cloudfoundry/run:base-cnb' 100% [INFO] > Pulled run image 'cloudfoundry/run@sha256:ba9998ae4bb32ab43a7966c537aa1be153092ab0c7536eeef63bcd6336cbd0db' [INFO] > Executing lifecycle version v0.5.0 [INFO] > Using build cache volume 'pack-cache-5cbe5692dbc4.build' [INFO] [INFO] > Running detector [INFO] [detector] 6 of 13 buildpacks participating ... [INFO] [INFO] > Running restorer [INFO] [restorer] Restoring cached layer 'org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b' ... [INFO] [INFO] > Running cacher [INFO] [cacher] Reusing layer 'org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b' [INFO] [cacher] Reusing layer 'org.cloudfoundry.jvmapplication:executable-jar' [INFO] [cacher] Caching layer 'org.cloudfoundry.springboot:spring-boot' [INFO] [cacher] Reusing layer 'org.cloudfoundry.springautoreconfiguration:46ab131165317d91fd4ad3186abf755222744e2d277dc413def06f3ad45ab150' [INFO] [INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT'

就是這樣!您的應用程式已編譯、打包並轉換為 Docker 映像檔。您可以使用以下命令進行測試

$ docker run -it -p8080:8080 demo:0.0.1-SNAPSHOT

注意

遺憾的是,`M1` 不支援 Windows,但它應該可以在 Mac 或 Linux VM 上正常運作。如果您使用 Windows,請暫時使用 `2.3.0.BUILD-SNAPSHOT`。

Spring Boot 提供的內建支援提供了一個開始使用 buildpacks 的絕佳方式。由於它是 buildpack 平台規格的實作,因此也很容易遷移到更強大的 buildpack 工具,例如 `pack``kpack`,並確信會產生相同的映像檔。

Layered Jars

您可能不想使用 buildpacks 來建立映像檔。也許您有現有的工具是圍繞 dockerfiles 建構的,或者您只是更喜歡它們。無論如何,我們都希望讓使用常規 dockerfile 建立最佳化 Docker 映像檔變得更容易,因此我們新增了對「layered jar」的支援。

Spring Boot 一直以來都支援自己的「fat jar」格式,讓您可以建立一個可以使用 `java -jar` 運行的歸檔檔。如果您曾經查看過該 JAR 檔的內容,您會看到如下所示的結構

META-INF/ MANIFEST.MF org/ springframework/ boot/ loader/ ... BOOT-INF/ classes/ ... lib/ ...

JAR 檔分為三個主要部分

  • 用於引導 JAR 檔載入的類別

  • 您的應用程式類別在 `BOOT-INF/classes` 中

  • 相依性在 `BOOT-INF/lib` 中

由於這種格式是 Spring Boot 獨有的,因此我們有可能以有趣的方式演進它。在 Spring Boot `2.3.0.M1` 中,我們提供了一種名為 `LAYERED_JAR` 的新 `layout` 類型。

如果您選擇使用 layered 格式並查看 JAR 檔結構,您會看到類似這樣的內容

META-INF/ MANIFEST.MF org/ springframework/ boot/ loader/ ... BOOT-INF/ layers// classes/ ... lib/ .../ classes/ ... lib/ ... layers.idx

您仍然可以看到引導載入器類別(您仍然可以運行 `java -jar`),但現在 `lib` 和 `classes` 資料夾已被拆分並分類到各個層中。還有一個新的 `layers.idx` 檔案,用於提供應新增層的順序。

最初,我們提供了以下開箱即用的層

  • dependencies (用於常規發布的相依性)

  • snapshot-dependencies (用於快照相依性)

  • resources (用於靜態資源)

  • application (用於應用程式類別和資源)

這種分層設計旨在根據程式碼在應用程式建置之間變更的可能性來分隔程式碼。程式庫程式碼在建置之間變更的可能性較低,因此將其放置在自己的層中,以允許工具從快取中重複使用這些層。應用程式程式碼在建置之間變更的可能性較高,因此將其隔離在單獨的層中。

提取層

即使使用新格式,您仍然需要費一些功夫才能提取檔案,以便它們可以被您的 `dockerfile` 複製。這些載入器類別需要位於 JAR 檔的根目錄中,但您可能希望在建置映像檔時將它們放在實際的層中。當然,您可以使用 `unzip` 和 `mv` 的某種組合來完成此操作,但我們嘗試透過引入「jar 模式」的概念使其更加容易。

`jarmode` 是一種特殊的系統屬性,您可以在啟動 JAR 檔時設定它。它允許引導程式碼運行與您的應用程式完全不同的東西。例如,提取層的東西。

以下是如何使用 `layertools` jar 模式啟動您的 JAR 檔

$ java -Djarmode=layertools -jar my-app.jar

這將提供以下輸出

用法:java -Djarmode=layertools -jar my-app.jar

可用命令: list 列出可從 JAR 檔中提取的層 extract 提取 JAR 檔中的層以用於映像檔建立 help 關於任何命令的說明

在此模式下,您可以 `list` 或 `extract` 層。

編寫 dockerfile

讓我們繼續使用我們上面產生的範例應用程式,並向其中新增一個 `dockerfile`。

首先編輯 `pom.xml` 並新增以下內容

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
			<configuration>
				<layout>LAYERED_JAR</layout>
			</configuration>
		</plugin>
	</plugins>
</build>

然後重新建置 JAR 檔

$ mvn clean package

一切順利的話,我們現在應該有一個具有 `jarmode` 支援的 layered jar。使用以下命令進行測試

$ java -Djarmode=layertools -jar target/demo-0.0.1-SNAPSHOT.jar list

您應該看到以下輸出,它告訴我們層以及應新增層的順序

dependencies snapshot-dependencies resources application

我們現在可以製作一個 `dockerfile`,用於提取和複製每個層。以下是一個範例

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/snapshot-dependencies/ ./ COPY --from=builder application/resources/ ./ COPY --from=builder application/application/ ./ ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

這是一個多階段 dockerfile。`builder` 階段提取稍後需要的資料夾。每個 `COPY` 命令都與我們之前列出的層相關。

要建置映像檔,我們可以運行

$ docker build . --tag demo

然後我們可以測試它

$ docker run -it -p8080:8080 demo:latest

總結

透過 buildpacks、dockerfiles 和現有的外掛程式(例如 jib),絕對不乏建立 Docker 映像檔的方法。每種方法都有優缺點,但希望我們在 Spring Boot 2.3 中發布的新功能無論您選擇哪種方法都會有所幫助。

Spring Boot 2.3 目前計劃於 4 月底發布,我們非常希望在那之前收到關於 Docker 映像檔的回饋(提出問題、在此處評論或在 Gitter 上聊天)。

容器化愉快!

取得 Spring 電子報

保持與 Spring 電子報的聯繫

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進展。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供對 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

查看全部