使用 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 生成的胖 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 檔案未解壓。執行胖 JAR 總是會帶來一定的開銷,在容器化環境中,這可能會很明顯。通常最好解壓 JAR 並以展開的形式執行。

檔案的第二個問題是,如果您經常更新應用程式,它的效率不高。Docker 映象分層構建,在這種情況下,您的應用程式及其所有依賴項都被放入一個單獨的層中。由於您重新編譯程式碼的頻率可能高於升級 Spring Boot 版本的頻率,因此最好將事物分開。如果您將 JAR 檔案放在應用程式類之前的層中,Docker 通常只需要更改最底層,並可以從其快取中獲取其他層。

Spring Boot 2.3.0.M1 引入了兩項新功能,旨在改進這些現有技術:構建包支援和分層 JAR。

構建包

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

直到最近,構建包都與平臺緊密耦合,您無法輕鬆獨立使用它們。值得慶幸的是,它們現在已經擺脫了束縛,藉助 Cloud Native Buildpacks,您可以使用它們建立可以在任何地方執行的 Docker 相容映象。

Spring Boot 2.3.0.M1 直接為 Maven 和 Gradle 包含了構建包支援。這意味著您只需輸入一個命令,即可快速將一個合適的映象放入您本地執行的 Docker 守護程式中。對於 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] 構建映象 'docker.io/library/demo:0.0.1-SNAPSHOT' [INFO] [INFO] > 拉取構建器映象 'docker.io/cloudfoundry/cnb:0.0.43-bionic' 100% [INFO] > 已拉取構建器映象 'cloudfoundry/cnb@sha256:c983fb9602a7fb95b07d35ef432c04ad61ae8458263e7fb4ce62ca10de367c3b' [INFO] > 拉取執行映象 'docker.io/cloudfoundry/run:base-cnb' 100% [INFO] > 已拉取執行映象 'cloudfoundry/run@sha256:ba9998ae4bb32ab43a7966c537aa1be153092ab0c7536eeef63bcd6336cbd0db' [INFO] > 執行生命週期版本 v0.5.0 [INFO] > 使用構建快取卷 'pack-cache-5cbe5692dbc4.build' [INFO] [INFO] > 執行檢測器 [INFO] [detector] 13 個構建包中的 6 個參與 ... [INFO] [INFO] > 執行恢復器 [INFO] [restorer] 恢復快取層 'org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b' ... [INFO] [INFO] > 執行快取器 [INFO] [cacher] 重複使用層 'org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b' [INFO] [cacher] 重複使用層 'org.cloudfoundry.jvmapplication:executable-jar' [INFO] [cacher] 快取層 'org.cloudfoundry.springboot:spring-boot' [INFO] [cacher] 重複使用層 'org.cloudfoundry.springautoreconfiguration:46ab131165317d91fd4ad3186abf755222744e2d277dc413def06f3ad45ab150' [INFO] [INFO] 成功構建映象 'docker.io/library/demo:0.0.1-SNAPSHOT'

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

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

注意

不幸的是,M1 不支援 Windows,但它應該在 Mac 或 Linux 虛擬機器上正常工作。如果您使用的是 Windows,請暫時使用 2.3.0.BUILD-SNAPSHOT

Spring Boot 提供的內建支援是開始使用構建包的絕佳方式。由於它是構建包平臺規範的實現,因此也可以輕鬆遷移到更強大的構建包工具,例如 packkpack,並且可以確信會生成相同的映象。

分層 JAR

您可能不想使用構建包來建立映象。也許您有圍繞 dockerfile 構建的現有工具,或者您只是更喜歡它們。無論哪種方式,我們都希望透過引入“分層 JAR”支援,讓使用常規 dockerfile 構建最佳化的 Docker 映象變得更容易。

Spring Boot 一直支援自己的“胖 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 型別。

如果您選擇分層格式並檢視 JAR 結構,您會看到類似這樣的內容:

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

您仍然可以看到引導載入器類(您仍然可以執行 java -jar),但現在 libclasses 資料夾已被拆分並分類為層。還有一個新的 layers.idx 檔案,它提供了應該新增層的順序。

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

  • dependencies(用於常規釋出的依賴項)

  • snapshot-dependencies(用於快照依賴項)

  • resources(用於靜態資源)

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

這種分層設計旨在根據程式碼在應用程式構建之間發生變化的 likelihood 來分離程式碼。庫程式碼在構建之間發生變化的可能性較小,因此它被放置在其自己的層中,以允許工具從快取中重用這些層。應用程式程式碼在構建之間發生變化的可能性更大,因此它被隔離在單獨的層中。

提取層

即使採用新格式,您仍然需要經過一些步驟才能提取檔案,以便您的 dockerfile 可以複製它們。那些載入器類需要位於 JAR 的根目錄中,但您可能希望在構建映象時將它們放在實際的層中。當然,您可以使用 unzipmv 的組合來完成此操作,但我們試圖透過引入“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 關於任何命令的幫助

在此模式下,您可以 listextract 層。

編寫 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 的分層 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

總結

有了構建包、Dockerfile 和現有外掛(如 jib),建立 Docker 映象的方法 certainly 不少。每種方法都有其優缺點,但希望我們在 Spring Boot 2.3 中釋出的新功能對您選擇的任何方法都會有所幫助。

Spring Boot 2.3 目前計劃於 4 月底釋出,我們非常期待在此之前收到關於 Docker 映象的反饋(提出問題,在此處評論或在 Gitter 上聊天)。

愉快的容器化!

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有