SpringBoot Docker鏡像分層的優(yōu)化指南
導(dǎo)讀
關(guān)于 如何優(yōu)化 Spring Boot 應(yīng)用程序的 Docker 鏡像構(gòu)建 的,核心思想是:不要簡(jiǎn)單地把一個(gè)“胖 JAR 包”(fat jar)直接扔進(jìn) Docker 鏡像里運(yùn)行,而應(yīng)該利用分層(layering)技術(shù)來(lái)提升鏡像的構(gòu)建效率和部署性能。
下面我用通俗易懂的方式為你逐段解析,并總結(jié)關(guān)鍵點(diǎn)。
1. 為什么不能直接復(fù)制 fat jar 到 Docker 鏡像?
原文說(shuō):
There’s always a certain amount of overhead when running a fat jar without unpacking it, and in a containerized environment this can be noticeable.
意思是:
- 雖然你可以寫(xiě)幾行
Dockerfile把 Spring Boot 的.jar文件拷貝進(jìn)去然后java -jar app.jar運(yùn)行。 - 但這樣做的問(wèn)題是:
- 性能開(kāi)銷(xiāo):JVM 每次都要從壓縮的 JAR 包中讀取類(lèi)文件,不如解壓后直接訪問(wèn)快(尤其在容器頻繁啟動(dòng)時(shí)更明顯)。
- 鏡像體積大、更新慢:整個(gè)應(yīng)用被打包成一個(gè)“胖 JAR”,每次你改了一行代碼重新打包,整個(gè) JAR 都變了 → 導(dǎo)致 Docker 鏡像的所有層都失效 → 下次構(gòu)建必須重新上傳全部?jī)?nèi)容,效率極低。
2. 解決方案:使用 分層(Layering) 技術(shù)
核心理念:
把你的 JAR 包拆分成幾個(gè)“層”(layers),比如:
| 層名 | 內(nèi)容 | 是否經(jīng)常變化 |
|---|---|---|
dependencies | 第三方依賴(lài)庫(kù)(如 Spring、MyBatis 等) | ? 很少變 |
spring-boot-loader | Spring Boot 自帶的啟動(dòng)器代碼 | ? 幾乎不變 |
snapshot-dependencies | 快照版本的依賴(lài)(開(kāi)發(fā)中的內(nèi)部模塊) | ?? 偶爾變 |
application | 你自己寫(xiě)的業(yè)務(wù)代碼和配置文件 | ? 經(jīng)常變 |
目標(biāo):讓不常變的部分放在上層,常變的部分放在下層。這樣每次只重建最底層(application),其他層可以復(fù)用緩存!
3. 如何實(shí)現(xiàn)分層?—— 添加 layers.idx
Spring Boot 支持在打包時(shí)生成一個(gè)叫 layers.idx 的索引文件,它記錄了哪些文件屬于哪一層。
例如:
- "dependencies": - BOOT-INF/lib/library1.jar - BOOT-INF/lib/library2.jar - "spring-boot-loader": - org/springframework/boot/loader/JarLauncher.class - "application": - BOOT-INF/classes/com/example/MyController.class
這個(gè)文件會(huì)在你構(gòu)建項(xiàng)目時(shí)自動(dòng)生成(需要配置 Maven 或 Gradle 插件)。
4. 使用 jarmode=layertools 提取分層內(nèi)容
Spring Boot 提供了一個(gè)特殊模式:layertools,可以用它來(lái)提取這些層。
命令:
java -Djarmode=layertools -jar my-app.jar extract
執(zhí)行后會(huì)自動(dòng)把 JAR 解開(kāi)成多個(gè)目錄:
/ ├── dependencies/ ← 第三方依賴(lài) ├── spring-boot-loader/ ← 啟動(dòng)類(lèi) ├── snapshot-dependencies/← 快照依賴(lài) └── application/ ← 你的代碼
5. 編寫(xiě)優(yōu)化版 Dockerfile(多階段構(gòu)建)
利用上面提取出的目錄結(jié)構(gòu),我們可以寫(xiě)一個(gè)高效的 Dockerfile:
# 第一階段:構(gòu)建并提取分層 FROM eclipse-temurin:11-jre AS builder WORKDIR /app COPY target/myapp.jar app.jar RUN java -Djarmode=layertools -jar app.jar extract # 第二階段:組裝最終鏡像 FROM eclipse-temurin:11-jre WORKDIR /app # 分別拷貝每一層(順序很重要!不變的放前面) COPY --from=builder /app/dependencies/ ./ COPY --from=builder /app/spring-boot-loader/ ./ COPY --from=builder /app/snapshot-dependencies/ ./ COPY --from=builder /app/application/ ./ # 啟動(dòng)時(shí)通過(guò) JarLauncher 加載 ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
好處是什么?
- 當(dāng)你下次修改了代碼再構(gòu)建鏡像時(shí):
dependencies層沒(méi)變 → Docker 直接使用緩存 ?- 只有
application層變了 → 只重新構(gòu)建這一層 ?
- 構(gòu)建速度快、推送體積小、節(jié)省網(wǎng)絡(luò)帶寬!
6. 替代方案:使用 Buildpacks(云原生構(gòu)建包)
除了手寫(xiě) Dockerfile,Spring Boot 還支持一種更簡(jiǎn)單的自動(dòng)化方式:Buildpacks。
Buildpack 是一種“智能打包工具”,能自動(dòng)識(shí)別你的應(yīng)用類(lèi)型(Java/Spring Boot),并幫你生成標(biāo)準(zhǔn)的 OCI 鏡像(就是 Docker 能跑的鏡像)。
優(yōu)點(diǎn):
- 不用手寫(xiě) Dockerfile
- 自動(dòng)生成安全、高效、符合規(guī)范的鏡像
- 支持
layers.idx,也能做到分層優(yōu)化
Maven 用戶(hù)可以直接運(yùn)行:
./mvnw spring-boot:build-image
Gradle 用戶(hù):
./gradlew bootBuildImage
就會(huì)自動(dòng)生成一個(gè)名為 myapp:latest 的本地鏡像。
總結(jié):你應(yīng)該怎么理解這段話(huà)?
| 關(guān)鍵點(diǎn) | 說(shuō)明 |
|---|---|
| 不推薦做法 | 直接 COPY app.jar . 然后 java -jar app.jar |
| 推薦做法 | 使用 分層 + 多階段 Dockerfile 或 Buildpacks |
| 核心優(yōu)勢(shì) | 利用 Docker 的緩存機(jī)制,提高 CI/CD 效率 |
| 實(shí)現(xiàn)方式 | 打包時(shí)生成 layers.idx → 用 jarmode=layertools 提取 → 分層 COPY |
| 更簡(jiǎn)單的方法 | 用 spring-boot:build-image 自動(dòng)生成優(yōu)化鏡像 |
給開(kāi)發(fā)者的建議:
- 在 pom.xml 或 build.gradle 中啟用 layered jar 支持。
- 如果你想完全控制鏡像,就寫(xiě)一個(gè)帶 layertools 的 Dockerfile。
- 如果你想快速上線、減少運(yùn)維負(fù)擔(dān),直接用 bootBuildImage 命令即可。
簡(jiǎn)單來(lái)說(shuō):“把不變的東西放上面,變的東西放下面,讓 Docker 緩存幫我們加速。”
這就是這段文檔的核心思想。
以上就是SpringBoot Docker鏡像分層的優(yōu)化指南的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot Docker鏡像分層的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring Boot 實(shí)現(xiàn)配置文件加解密原理
這篇文章主要介紹了Spring Boot 實(shí)現(xiàn)配置文件加解密原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Java 實(shí)現(xiàn)簡(jiǎn)易教務(wù)管理系統(tǒng)的代碼
這篇文章主要介紹了Java 實(shí)現(xiàn)簡(jiǎn)易教務(wù)管理系統(tǒng)的代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
利用SpringMVC接收復(fù)雜對(duì)象和多個(gè)文件(前端使用JQuery)
這篇文章主要介紹了利用SpringMVC接收復(fù)雜對(duì)象和多個(gè)文件(前端使用JQuery),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
源碼分析Spring?中?@Qualifier?注解基本用法
這篇文章主要介紹了源碼分析Spring?中?@Qualifier?注解基本用法,在源碼分析的過(guò)程中,也?GET?到?Spring?許多新的玩法,感興趣的小伙伴趕緊去試試吧2023-08-08
解決idea中Terminal終端無(wú)法執(zhí)行GIT命令+Terminal 中文亂碼問(wèn)題
這篇文章主要介紹了解決idea中Terminal終端無(wú)法執(zhí)行GIT命令+Terminal 中文亂碼問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
Java?spring?boot發(fā)送郵箱實(shí)現(xiàn)過(guò)程記錄
我們?cè)?站上注冊(cè)賬號(hào)的時(shí)候?般需要獲取驗(yàn)證碼,?這個(gè)驗(yàn)證碼?般發(fā)送在你的?機(jī)號(hào)上還有的是發(fā)送在你的郵箱中,這篇文章主要給大家介紹了關(guān)于Java?spring?boot發(fā)送郵箱實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-01-01
Mybatis-plus在項(xiàng)目中的簡(jiǎn)單應(yīng)用
Mybatis-plus是Spring框架中OOM的一大利器,其簡(jiǎn)單易用參考官網(wǎng)文檔即可很快上手,本文主要介紹了邏輯刪除,自動(dòng)填充,分頁(yè)插件等的簡(jiǎn)單使用,感興趣的可以了解一下2021-07-07
servlet實(shí)現(xiàn)用戶(hù)登錄小程序
這篇文章主要為大家詳細(xì)介紹了servlet實(shí)現(xiàn)用戶(hù)登錄的小程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05
Java調(diào)用MySQL存儲(chǔ)過(guò)程并獲得返回值的方法
這篇文章主要介紹了Java調(diào)用MySQL存儲(chǔ)過(guò)程并獲得返回值的方法,實(shí)例分析了java實(shí)現(xiàn)MySQL存儲(chǔ)過(guò)程的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07

