1. 程式人生 > >Spring Boot 建立 Docker 映象

Spring Boot 建立 Docker 映象

隨著越來越多的組織轉向容器和虛擬伺服器,Docker正成為軟體開發工作流程中一個更重要的部分。為此,Spring Boot 2.3中最新的功能之中,提供了為Spring Boot應用程式建立 Docker 映象的能力。 這篇文章的目的,就是為了給大家介紹如何為 Spring Boot 應用程式建立 Docker 映象。 ## 1. 傳統Docker構建 使用Spring Boot 構建 Docker 映象的傳統方法是使用 Dockerfile 。下面是一個簡單的例子: ```plaintext FROM openjdk:8-jdk-alpine EXPOSE 8080 ARG JAR_FILE=target/demo-app-1.0.0.jar ADD ${JAR_FILE} app.jar ENTRYPOINT ["java","-jar","/app.jar"] ``` 然後我們可以使用 *docker build* 命令來建立 `Docker` 映像。這對大多數應用程式都很好,但也有一些缺點。 首先,我們使用的是 `Spring Boot` 建立的 fat jar。**這會影響啟動時間,尤其是在集裝箱環境中**。我們可以通過新增jar檔案的分解內容來節省啟動時間。 其次,Docker映象是分層構建的。Spring Boot fat jar 的特性使得所有的應用程式程式碼和第三方庫都放在一個層中。**這意味著即使只有一行程式碼更改,也必須重新構建整個層**。 通過在構建之前分解 jar ,應用程式程式碼和第三方庫各自獲得自己的層。這樣,我們便可以利用Docker的快取機制。現在,當某一行程式碼被更改時,只需要重新構建相應的層。 考慮到這一點,讓我們看看Spring Boot 如何改進建立Docker映象的過程。 ## 2. Buildpacks **BuildPacks 是一種提供框架和應用程式依賴性的工具**。 例如,給定一個Spring Boot fat jar,一個buildpack將為我們提供Java執行時。這使我們可以跳過 Dockerfile 並自動獲得一個合理的docker 映象。 Spring Boot 包括對 bulidpacks 的Maven和Gradle支援。例如,使用Maven構建時,我們將執行以下命令: ```shell ./mvnw spring-boot:build-image ``` 我們觀察下一些相關的輸出,看看發生了什麼: ```shell [INFO] Building jar: target/demo-0.0.1-SNAPSHOT.jar ... [INFO] Building image 'docker.io/library/demo:0.0.1-SNAPSHOT' ... [INFO] > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 100% ... [INFO] [creator] ===> DETECTING [INFO] [creator] 5 of 15 buildpacks participating [INFO] [creator] paketo-buildpacks/bellsoft-liberica 2.8.1 [INFO] [creator] paketo-buildpacks/executable-jar 1.2.8 [INFO] [creator] paketo-buildpacks/apache-tomcat 1.3.1 [INFO] [creator] paketo-buildpacks/dist-zip 1.3.6 [INFO] [creator] paketo-buildpacks/spring-boot 1.9.1 ... [INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT' [INFO] Total time: 44.796 s ``` 第一行顯示我們構建了標準的 fat jar,與其他典型的maven包一樣。 下一行開始Docker映像構建。然後,看到這個 bulid 拉取了 packeto 構建器。 packeto 是基於雲原生 bulidpacks 的實現。**它負責分析我們的專案並確定所需的框架和庫**。在我們的例子中,它確定我們有一個Spring Boot專案並新增所需的構建包。 最後,我們看到生成的Docker映像和總構建時間。注意,在第一次構建時,花了相當多的時間下載構建包並建立不同的層。 buildpacks 的一大特點是Docker映像是多層的。因此,如果我們只更改應用程式程式碼,後續構建將更快: ```shell ... [INFO] [creator] Reusing layer 'paketo-buildpacks/executable-jar:class-path' [INFO] [creator] Reusing layer 'paketo-buildpacks/spring-boot:web-application-type' ... [INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT' ... [INFO] Total time: 10.591 s ``` ## 3. 層級jar包 在某些情況下,我們可能不喜歡使用 bulidpacks ——也許我們的基礎架構已經繫結到另一個工具上,或者我們已經有了我們想要重新使用的自定義 Dockerfiles 。 **基於這些原因,Spring Boot 還支援使用分層jars** 構建Docker映像。為了瞭解它的工作原理,讓我們看看一個典型的Spring Boot fat jar 佈局: ```bash org/ springframework/ boot/ loader/ ... BOOT-INF/ classes/ ... lib/ ... ``` fat jar 由3個主要區域組成: - 啟動Spring應用程式所需的引導類 - 應用程式程式碼 - 第三方庫 使用分層jar,結構看起來很相似,但是我們得到了一個新的 *layers.idx* 將 fat jar 中的每個目錄對映到一個層的檔案: ```yaml - "dependencies": - "BOOT-INF/lib/" - "spring-boot-loader": - "org/" - "snapshot-dependencies": - "application": - "BOOT-INF/classes/" - "BOOT-INF/classpath.idx" - "BOOT-INF/layers.idx" - "META-INF/" ``` Out-of-the-box, Spring Boot provides four layers: 開箱即用,Spring Boot 提供4層: - *dependencies*: 來自第三方的依賴 - *snapshot-dependencies*: 來自第三方的 snapshot 依賴 - *resources*: 靜態資源 - *application*: 應用程式程式碼和資源(resources) **我們的目標是將應用程式程式碼和第三方庫放置到層中,以反映它們更改的頻率**。 例如,應用程式程式碼可能是更改最頻繁的程式碼,因此它有自己的層。此外,每一層都可以獨立演化,只有當一層發生變化時,才會為它重建 Docker 映象。 現在我們瞭解了分層 jar 結構,接下來看看如何利用它來製作 Docker 映像。 ### 3.1.建立分層 jar 首先,我們必須建立一個專案來建立一個分層的jar。對於Maven,則需要在POM的 Spring Boot plugin 部分新增一個新的配置: ```xml ``` 有了這個配置,Maven *package* 命令(包括它的其他依賴命令)將使用前面提到的四個預設層生成一個新的分層jar。 ### 3.2. 檢視和提取分層 下一步,我們需要從 jar 中提取層,這樣Docker映象才能擁有正確的層。 要檢查分層jar的任何層,可以執行以下命令: ```bash java -Djarmode=layertools -jar demo-0.0.1.jar list ``` 然後提取它們,執行命令: ```bash java -Djarmode=layertools -jar demo-0.0.1.jar extract ``` ### 3.3. 建立Docker映像 將這些層合併到 Docker 映像中的最簡單方法是使用 Dockerfile : ```bash FROM adoptopenjdk:11-jre-hotspot as builder ARG JAR_FILE=target/*.jar COPY ${JAR_FILE} application.jar RUN java -Djarmode=layertools -jar application.jar extract FROM adoptopenjdk:11-jre-hotspot COPY --from=builder dependencies/ ./ COPY --from=builder snapshot-dependencies/ ./ COPY --from=builder spring-boot-loader/ ./ COPY --from=builder application/ ./ ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"] ``` 這個 Dockerfile 從fat jar中提取層,然後將每個層複製到Docker映像中。 **每個COPY指令最終都會在Docker映像**中生成一個新層。 如果我們構建這個Dockerfile,我們可以看到分層jar中的每個層都作為自己的層新增到Docker映象中: ```bash ... Step 6/10 : COPY --from=builder dependencies/ ./ ---> 2c631b8f9993 Step 7/10 : COPY --from=builder snapshot-dependencies/ ./ ---> 26e8ceb86b7d Step 8/10 : COPY --from=builder spring-boot-loader/ ./ ---> 6dd9eaddad7f Step 9/10 : COPY --from=builder application/ ./ ---> dc80cc00a655 ... ``` ## 4.總結 在本文中,我們學習了使用 Spring Boot 構建 Docker 映像的各種方法。 使用 buildpacks,我們可以獲得合適的Docker映象,而無需模板或自定義配置。 或者,再多花點功夫,我們就可以使用分層 jar 來獲得一個更加定製的Docker映象。 如果你覺得文章還不錯,記得關注公眾號: 鍋外的大佬 [劉一手的部落格](http://developl