Spring Boot 2.3(截至目前版本爲 M2)爲容器化部署提供了一個新特性 Layered Jar。通常來講 Spring Boot 程序都是以 fat jar 的方式構建的,文件大小動輒 50M、100M 這樣子,對 docker image 其實很不友好。Docker image 自己是分層結構,若是某一層沒有變化在 pull
時就沒必要上傳,一旦有變化就要上傳整層。一個程序中,程序自身代碼、資源的變動頻率要遠大於依賴庫的變動頻率,大多數時候由於幾行代碼變化致使上傳整個 jar 文件,不管是存儲佔用仍是時間效率上都是很大的浪費,後者在國內網速下尤爲讓人揪心。java
新特性 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
#01
、#07
2 個 FROM
保持統一爲運行時使用的基礎鏡像便可;#04
處 COPY
的源根據使用的 docker 插件不一樣會有所變化,本文使用的插件爲 io.fabric8:docker-maven-plugin:0.33.0
。目的文件保持 application.jar
便可,它要跟 #05
的 jar 文件名一致;#05
若是報錯須要檢查是否遺漏了參數,好比 #20
須要 --enable-preview
的話 #05
也須要加 --enable-preview
;#09
、#11
、#18
跟本文無關,可是算是 Spring Boot 容器化的良好實踐,能夠照抄,往後有機會專文再講;#10
也是良好實踐——非 root 用戶運行,若是遇到相關問題能夠刪除此行;#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"]