[每日短篇] 26 - Spring Boot 2.3 爲 Docker Image 增長的分層 Jar 包特性

背景

Spring Boot 2.3(截至目前版本爲 M2)爲容器化部署提供了一個新特性 Layered Jar。通常來講 Spring Boot 程序都是以 fat jar 的方式構建的,文件大小動輒 50M、100M 這樣子,對 docker image 其實很不友好。Docker image 自己是分層結構,若是某一層沒有變化在 pull 時就沒必要上傳,一旦有變化就要上傳整層。一個程序中,程序自身代碼、資源的變動頻率要遠大於依賴庫的變動頻率,大多數時候由於幾行代碼變化致使上傳整個 jar 文件,不管是存儲佔用仍是時間效率上都是很大的浪費,後者在國內網速下尤爲讓人揪心。java

Layered Jar

新特性 layered jar 爲不一樣變動頻率內容分離提供了支持工具,在此基礎上分層構建 docker image 就變得很容易了。本質上這個特性是 org.springframework.boot:spring-boot-maven-plugin 提供的一種新的 layout,當使用新 layout 打包時,一個 spring-boot-layertools jar 會打包到 fat jar 中,新特性是由這個 jar 提供的。這裏不深刻解析實現細節,而是重點關注如何模式化使用這個特性得到收益。spring

要使用這個新特性須要作的事情很是少,首先是在 pom 中增長 layout 配置,我猜將來這個選項會成爲默認值,從而不需任何顯式配置。docker

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

新增的部分就是這 3 行app

<configuration>
  <layout>LAYERED_JAR</layout>
</configuration>

增長配置以後 package 打包,而後能夠執行如下命令驗證配置正確與否maven

java -Djarmode=layertools -jar <target.jar>

一個示例輸出:spring-boot

Usage:
  java -Djarmode=layertools -jar metis-server-0.1.0-SNAPSHOT.jar

Available commands:
  list     List layers from the jar that can be extracted
  extract  Extracts layers from the jar for image creation
  help     Help about any command

這是 maven 上全部須要作的事情,gradle 參照 maven 也能夠相似處理。而後須要在 Dockerfile 中再作一點處理,這用到了 docker 的多階段構建功能(17.05 增長的功能,幾乎 3 年前,但願沒人說這版本太新~)。一個示例 Dockerfile工具

FROM azul/zulu-openjdk:13 as builder                                #01
                                                                    #02
WORKDIR application                                                 #03
COPY /maven/${project.build.finalName}.jar application.jar          #04
RUN java -Djarmode=layertools -jar application.jar extract          #05
                                                                    #06
FROM azul/zulu-openjdk:13                                           #07
                                                                    #08
WORKDIR /opt/bin/                                                   #09
USER 1000:1000                                                      #10
EXPOSE 8080                                                         #11
                                                                    #12
COPY --from=builder application/dependencies/ ./                    #13
COPY --from=builder application/snapshot-dependencies/ ./           #14
COPY --from=builder application/resources/ ./                       #15
COPY --from=builder application/application/ ./                     #16
                                                                    #17
ENV TZ=Asia/Shanghai                                                #18
                                                                    #19
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]  #20

對上面的 Dockerfile 逐行解釋一下:gradle

  1. #01#07 2 個 FROM 保持統一爲運行時使用的基礎鏡像便可;
  2. #04COPY 的源根據使用的 docker 插件不一樣會有所變化,本文使用的插件爲 io.fabric8:docker-maven-plugin:0.33.0。目的文件保持 application.jar 便可,它要跟 #05 的 jar 文件名一致;
  3. #05 若是報錯須要檢查是否遺漏了參數,好比 #20 須要 --enable-preview 的話 #05 也須要加 --enable-preview
  4. #09#11#18 跟本文無關,可是算是 Spring Boot 容器化的良好實踐,能夠照抄,往後有機會專文再講;
  5. #10 也是良好實踐——非 root 用戶運行,若是遇到相關問題能夠刪除此行;
  6. #13#14#15#16#20 4 個 COPY 1 個 ENTRYPOINT 照抄便可,這是本文新特性的關鍵。

而後,用這個 Dockerfile 構建出來的鏡像內容已是拆解分紅多層的鏡像了,當變動源碼、資源、快照版依賴、正式版依賴時會依次影響更多層鏡像,從而實現每次構建上傳倉庫時存儲和傳輸耗時最小化。ui

總結

不管從易用程度仍是達到的效果上講,layered jar 都很是值得使用。要使用該特性只須要開啓 pom 中一個配置項並使用一個幾乎不須要任何修改的 Dockerfile 模板,幾乎零負擔得到可觀的收益。.net

與本文所涉及內容有所關聯的另外 2 篇帖子可供參考
[每日短篇] 23 - 動態給容器指定 Java 啓動參數
[每日短篇] 0 - Linux 的 timezone 設置

最後把不帶行號的 Dockerfile 再重複一次以方便複製

FROM azul/zulu-openjdk:13 as builder

WORKDIR application
COPY /maven/${project.build.finalName}.jar application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM azul/zulu-openjdk:13

WORKDIR /opt/bin/
USER 1000:1000
EXPOSE 8080

COPY --from=builder application/dependencies/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/resources/ ./
COPY --from=builder application/application/ ./

ENV TZ=Asia/Shanghai

ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
相關文章
相關標籤/搜索