SpringBoot Docker镜像分层的优化指南

 更新时间:2025年10月23日 10:11:04   作者:lang20150928  
关于如何优化SpringBoot应用程序的Docker镜像构建 的,核心思想是:不要简单地把一个胖JAR包直接扔进Docker镜像里运行,而应该利用分层技术来提升镜像的构建效率和部署性能,所以本文给大家介绍了SpringBoot Docker镜像分层的优化指南,需要的朋友可以参考下

导读

关于 如何优化 Spring Boot 应用程序的 Docker 镜像构建 的,核心思想是:不要简单地把一个“胖 JAR 包”(fat jar)直接扔进 Docker 镜像里运行,而应该利用分层(layering)技术来提升镜像的构建效率和部署性能。

下面我用通俗易懂的方式为你逐段解析,并总结关键点。

1. 为什么不能直接复制 fat jar 到 Docker 镜像?

原文说:

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.

意思是:

  • 虽然你可以写几行 Dockerfile 把 Spring Boot 的 .jar 文件拷贝进去然后 java -jar app.jar 运行。
  • 但这样做的问题是:
    • 性能开销:JVM 每次都要从压缩的 JAR 包中读取类文件,不如解压后直接访问快(尤其在容器频繁启动时更明显)。
    • 镜像体积大、更新慢:整个应用被打包成一个“胖 JAR”,每次你改了一行代码重新打包,整个 JAR 都变了 → 导致 Docker 镜像的所有层都失效 → 下次构建必须重新上传全部内容,效率极低。

2. 解决方案:使用 分层(Layering) 技术

核心理念:

把你的 JAR 包拆分成几个“层”(layers),比如:

层名内容是否经常变化
dependencies第三方依赖库(如 Spring、MyBatis 等)❌ 很少变
spring-boot-loaderSpring Boot 自带的启动器代码❌ 几乎不变
snapshot-dependencies快照版本的依赖(开发中的内部模块)⚠️ 偶尔变
application你自己写的业务代码和配置文件✅ 经常变

目标:让不常变的部分放在上层,常变的部分放在下层。这样每次只重建最底层(application),其他层可以复用缓存!

3. 如何实现分层?—— 添加 layers.idx

Spring Boot 支持在打包时生成一个叫 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

这个文件会在你构建项目时自动生成(需要配置 Maven 或 Gradle 插件)。

4. 使用 jarmode=layertools 提取分层内容

Spring Boot 提供了一个特殊模式:layertools,可以用它来提取这些层。

命令:

java -Djarmode=layertools -jar my-app.jar extract

执行后会自动把 JAR 解开成多个目录:

/
├── dependencies/         ← 第三方依赖
├── spring-boot-loader/   ← 启动类
├── snapshot-dependencies/← 快照依赖
└── application/          ← 你的代码

5. 编写优化版 Dockerfile(多阶段构建)

利用上面提取出的目录结构,我们可以写一个高效的 Dockerfile

# 第一阶段:构建并提取分层
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/ ./

# 启动时通过 JarLauncher 加载
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

 好处是什么?

  • 当你下次修改了代码再构建镜像时:
    • dependencies 层没变 → Docker 直接使用缓存 ✅
    • 只有 application 层变了 → 只重新构建这一层 ❗
  • 构建速度快、推送体积小、节省网络带宽!

6. 替代方案:使用 Buildpacks(云原生构建包)

除了手写 Dockerfile,Spring Boot 还支持一种更简单的自动化方式:Buildpacks

Buildpack 是一种“智能打包工具”,能自动识别你的应用类型(Java/Spring Boot),并帮你生成标准的 OCI 镜像(就是 Docker 能跑的镜像)。

优点:

  • 不用手写 Dockerfile
  • 自动生成安全、高效、符合规范的镜像
  • 支持 layers.idx,也能做到分层优化

Maven 用户可以直接运行:

./mvnw spring-boot:build-image

Gradle 用户:

./gradlew bootBuildImage

就会自动生成一个名为 myapp:latest 的本地镜像。

总结:你应该怎么理解这段话?

关键点说明
不推荐做法直接 COPY app.jar . 然后 java -jar app.jar
推荐做法使用 分层 + 多阶段 Dockerfile 或 Buildpacks
核心优势利用 Docker 的缓存机制,提高 CI/CD 效率
实现方式打包时生成 layers.idx → 用 jarmode=layertools 提取 → 分层 COPY
更简单的方法用 spring-boot:build-image 自动生成优化镜像

给开发者的建议:

  • 在 pom.xml 或 build.gradle 中启用 layered jar 支持。
  • 如果你想完全控制镜像,就写一个带 layertools 的 Dockerfile。
  • 如果你想快速上线、减少运维负担,直接用 bootBuildImage 命令即可。

简单来说:“把不变的东西放上面,变的东西放下面,让 Docker 缓存帮我们加速。”

这就是这段文档的核心思想。

以上就是SpringBoot Docker镜像分层的优化指南的详细内容,更多关于SpringBoot Docker镜像分层的资料请关注脚本之家其它相关文章!

相关文章

  • java输入数字,输出倒序的实例

    java输入数字,输出倒序的实例

    这篇文章主要介绍了java输入数字,输出倒序的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • 举例讲解Java中的多线程编程

    举例讲解Java中的多线程编程

    这篇文章主要介绍了举例讲解Java中的多线程编程,线程是Java学习中的重要知识,需要的朋友可以参考下
    2015-09-09
  • 如何获得spring上下文的方法总结

    如何获得spring上下文的方法总结

    这篇文章主要介绍了如何获得spring上下文的方法总结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Vue中对象赋值问题:对象引用被保留仅部分属性被覆盖的解决方案

    Vue中对象赋值问题:对象引用被保留仅部分属性被覆盖的解决方案

    在 Vue 中,当你直接给一个响应式对象(如 reactive 或 ref 包装的对象)赋新值时,如果直接使用 = 赋值,可能会遇到 对象引用被保留,仅部分属性被覆盖 的问题,本文给大家介绍了Vue中对象赋值问题:对象引用被保留仅部分属性被覆盖的解决方案,需要的朋友可以参考下
    2025-07-07
  • java配置沙箱支付的实现示例

    java配置沙箱支付的实现示例

    箱支付是支付平台提供的模拟支付环境,包含测试用的 APPID、密钥、网关、测试账号等资源,下面就来介绍一下如何实现,感兴趣的可以了解一下
    2025-08-08
  • 图解Spring Security 中用户是如何实现登录的

    图解Spring Security 中用户是如何实现登录的

    这篇文章主要介绍了图解Spring Security 中用户是如何实现登录的,文中通过示例代码和图片介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • SpringBoot整合Mybatis与thymleft实现增删改查功能详解

    SpringBoot整合Mybatis与thymleft实现增删改查功能详解

    MybatisPlus是国产的第三方插件, 它封装了许多常用的CURDapi,免去了我们写mapper.xml的重复劳动。本文将整合MybatisPlus实现增删改查功能,感兴趣的可以了解一下
    2022-12-12
  • 一文教会你cmd命令运行java程序

    一文教会你cmd命令运行java程序

    这篇文章主要给大家介绍了关于如何使用cmd命令运行java程序的相关资料,这是一个非常有用的技能,特别是当您需要在没有集成开发环境(IDE)的情况下编写和运行Java代码时,需要的朋友可以参考下<BR>
    2024-06-06
  • Java实现文件变化监听代码实例

    Java实现文件变化监听代码实例

    这篇文章主要介绍了Java实现文件变化监听代码实例,通过定时任务,轮训查询文件的最后修改时间,与上一次进行对比,如果发生变化,则说明文件已经修改,进行重新加载或对应的业务逻辑处理,需要的朋友可以参考下
    2024-01-01
  • springboot的application.yml配置port不生效的解决方案

    springboot的application.yml配置port不生效的解决方案

    这篇文章主要介绍了springboot的application.yml配置port不生效的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07

最新评论