領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多Spring Boot 2.3.0.M1 剛剛釋出,它帶來了一些有趣的新特性,可以幫助您將 Spring Boot 應用打包成 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 中引入了兩個新特性,以幫助改進這些現有技術:buildpack 支援和分層 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] 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 虛擬機器上應該工作正常。如果您使用 Windows,請暫時使用 2.3.0.BUILD-SNAPSHOT
。
Spring Boot 提供的內建支援是開始使用構建包的好方法。由於它是構建包平臺規範的實現,因此也可以輕鬆遷移到更強大的構建包工具,例如 pack
或 kpack
,並且確信會生成相同的映象。
您可能不希望使用構建包來建立映象。也許您有圍繞 dockerfile 構建的現有工具,或者您只是更喜歡它們。無論如何,我們也希望更容易建立可以用常規 dockerfile 構建的最佳化 Docker 映象,因此我們增加了對“分層 JAR”的支援。
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
,我們提供了一種新的 layout
型別,稱為 LAYERED_JAR
。
如果您選擇使用分層格式並檢視 JAR 結構,您會看到類似這樣的內容
META-INF/ MANIFEST.MF org/ springframework/ boot/ loader/ ... BOOT-INF/ layers/
您仍然可以看到引導載入器類(您仍然可以執行 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
這將提供以下輸出
Usage: java -Djarmode=layertools -jar my-app.jar
Available commands: 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
的分層 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 映象的方法當然不乏。每種方法都有優缺點,但希望我們在 Spring Boot 2.3 中釋出的新功能無論您選擇哪種方法都能有所幫助。
Spring Boot 2.3 目前計劃於四月底釋出,在此之前,我們非常期待關於 Docker 映象的反饋(提交問題、在此評論或在 Gitter 上聊天)。
愉快的容器化!