SpringBoot项目docker分层镜像构建案例

 更新时间:2026年02月10日 09:18:02   作者:昵称为空C  
本文主要介绍Spring Boot项目的docker镜像分层构建,通过将外部依赖、内部依赖和应用程序代码分离,可以利用Docker的缓存机制,加快构建速度和测试环境镜像拉取速度,感兴趣的可以了解一下

摘要:本文主要介绍Spring Boot项目的docker镜像分层构建,分为外部依赖和内部依赖和项目classes依赖,让docker构建的时候,可以使用之前的缓存,加快构建速度和测试环境镜像拉取速度。

为什么使用分层 Docker 镜像?

传统的 Docker 镜像构建方式会将整个应用程序 JAR/WAR 文件复制到一个层中。当你修改代码时,整个层都会失效,导致 Docker 必须重新构建整个应用程序层,即使只是做了微小的代码更改。

通过我们的分层方法,我们将以下内容分离:

  1. 外部依赖(第三方库)
  2. 内部依赖(公司特定库)
  3. 应用程序代码(实际业务逻辑)

这样,当你修改代码时,只需要重新构建应用程序代码层,而依赖层可以从缓存中重用。

Maven 插件配置详解

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <executions>
        <execution>
          <id>copy-external-dependencies</id>
          <phase>package</phase>
          <goals>
            <goal>copy-dependencies</goal>
          </goals>
          <configuration>
            <outputDirectory>${project.build.directory}/lib/external</outputDirectory>
            <includeScope>compile</includeScope>
            <excludeGroupIds>cn.hutool</excludeGroupIds>
          </configuration>
        </execution>

        <execution>
          <id>copy-internal-dependencies</id>
          <phase>package</phase>
          <goals>
            <goal>copy-dependencies</goal>
          </goals>
          <configuration>
            <outputDirectory>${project.build.directory}/lib/internal</outputDirectory>
            <includeScope>compile</includeScope>
            <includeGroupIds>cn.hutool</includeGroupIds>
          </configuration>
        </execution>
      </executions>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>21</source>
        <target>21</target>
        <encoding>UTF-8</encoding>
        <!-- 如果使用预览特性 -->
        <compilerArgs>
          <arg>--enable-preview</arg>
        </compilerArgs>
        <release>21</release>
      </configuration>
    </plugin>
  </plugins>
</build>

工作原理

1. Maven 依赖分离插件配置详解

我们的 pom.xml 使用 maven-dependency-plugin 在构建过程中分离依赖。该插件包含两个执行配置:

外部依赖复制配置

<execution>
    <id>copy-external-dependencies</id>
    <phase>package</phase>
    <goals>
        <goal>copy-dependencies</goal>
    </goals>
    <configuration>
        <outputDirectory>${project.build.directory}/lib/external</outputDirectory>
        <includeScope>compile</includeScope>
        <excludeGroupIds>cn.hutool</excludeGroupIds>
    </configuration>
</execution>

配置参数说明:

<id>: 执行的唯一标识符

<phase>: 绑定到 Maven 生命周期的 package 阶段

<goals>: 执行的目标是 copy-dependencies

<configuration>: 配置详情

  • <outputDirectory>: 输出目录,${project.build.directory} 是 target 目录
  • <includeScope>: 包含作用域为 compile 的依赖
  • <excludeGroupIds>: 排除 groupId 为 cn.hutool 的依赖(将其视为内部依赖)

内部依赖复制配置

<execution>
    <id>copy-internal-dependencies</id>
    <phase>package</phase>
    <goals>
        <goal>copy-dependencies</goal>
    </goals>
    <configuration>
        <outputDirectory>${project.build.directory}/lib/internal</outputDirectory>
        <includeScope>compile</includeScope>
        <includeGroupIds>cn.hutool</includeGroupIds>
    </configuration>
</execution>

配置参数说明:

<id>: 执行的唯一标识符

<phase>: 同样绑定到 Maven 生命周期的 package 阶段

<goals>: 执行的目标也是 copy-dependencies

<configuration>: 配置详情

  • <outputDirectory>: 输出目录为 target/lib/internal
  • <includeScope>: 包含作用域为 compile 的依赖
  • <includeGroupIds>: 只包含 groupId 为 cn.hutool 的依赖(作为内部依赖处理)

通过这两个执行配置,Maven 会在 package 阶段自动将依赖分别复制到对应的目录中:

  • target/lib/external/ - 第三方依赖(排除了 cn.hutool)
  • target/lib/internal/ - 内部/公司特定依赖(只有 cn.hutool)
  • target/classes/ - 编译后的应用程序代码

2. 分层 Dockerfile 详解

我们的 Dockerfile 通过首先复制依赖来利用 Docker 层缓存:

FROM eclipse-temurin:21-jre-alpine
WORKDIR /app

# 1. 复制外部依赖(很少更改)
COPY target/lib/external/*.jar ./lib/external/

# 2. 复制内部依赖(偶尔更改)
COPY target/lib/internal/*.jar ./lib/internal/

# 3. 复制应用程序类(经常更改)
COPY target/classes ./classes

EXPOSE 8080

ENV CUSTOM_OPTS="-Xms256m -Xmx768m"
ENV JAVA_OPTS="--enable-preview -XX:+UseSerialGC"
ENTRYPOINT ["sh", "-c", "java $CUSTOM_OPTS $JAVA_OPTS -cp /app/classes:/app/lib/external/*:/app/lib/internal/* com.example.layer.Sp3LayerApplication"]

Dockerfile 配置说明:

FROM eclipse-temurin:21-jre-alpine: 使用轻量级的 Alpine Linux 上的 Temurin JDK 21 运行时环境

WORKDIR /app: 设置工作目录为 /app

COPY target/lib/external/*.jar ./lib/external/: 将外部依赖复制到镜像中,这是最稳定的层

COPY target/lib/internal/*.jar ./lib/internal/: 将内部依赖复制到镜像中,这层比外部依赖更可能变化

COPY target/classes ./classes: 将应用程序类文件复制到镜像中,这层最容易发生变化

EXPOSE 8080: 暴露应用程序端口

ENV 指令设置 JVM 参数:

  • CUSTOM_OPTS: 自定义 JVM 参数,设置初始堆内存和最大堆内存
  • JAVA_OPTS: Java 虚拟机选项,启用预览特性和使用串行垃圾收集器

ENTRYPOINT: 定义容器启动时执行的命令,使用类路径包含所有依赖和应用程序类

类路径说明:
-cp /app/classes:/app/lib/external/*:/app/lib/internal/* 指定了类路径的顺序:

  1. /app/classes: 应用程序编译后的类文件优先级最高
  2. /app/lib/external/*: 外部依赖次之
  3. /app/lib/internal/*: 内部依赖最后

这种顺序确保了应用程序类可以覆盖依赖中的同名类(如果需要的话),并且保证了正确的类加载顺序。

3. 构建过程详解

Maven 构建生命周期集成

当执行 mvn package 命令时,Maven 会按照以下顺序执行构建阶段:

  1. validate - 验证项目正确性
  2. compile - 编译源代码
  3. test - 运行测试
  4. package - 打包应用程序

在 package 阶段,maven-dependency-plugin 会自动执行我们配置的两个依赖复制任务:

  1. 首先执行 copy-external-dependencies,将外部依赖复制到 target/lib/external/
  2. 然后执行 copy-internal-dependencies,将内部依赖复制到 target/lib/internal/
  3. 同时,Maven 默认会将编译后的类文件放在 target/classes/ 目录中

完整构建步骤

清理并打包应用程序:

mvn clean package

此命令会触发依赖分离过程,生成三个目录:

  • target/classes/ - 应用程序编译后的类文件
  • target/lib/external/ - 外部依赖 JAR 文件
  • target/lib/internal/ - 内部依赖 JAR 文件
  1. 构建 Docker 镜像:
docker build -t sp3-layer .

Docker 构建过程会按顺序创建四个层:

  • 基础镜像层(eclipse-temurin:21-jre-alpine)
  • 外部依赖层(COPY target/lib/external/*.jar)
  • 内部依赖层(COPY target/lib/internal/*.jar)
  • 应用程序类层(COPY target/classes)

构建优化效果

由于 Docker 的层缓存机制:

  • 当只修改应用程序代码时,只有最后一层需要重建
  • 当添加新的外部依赖时,需要重建外部依赖层及其后的所有层
  • 当添加新的内部依赖时,需要重建内部依赖层和应用程序类层
  • 只有基础镜像变更时,才需要完全重建

优势

  1. 更快的构建速度:代码更改时只需重新构建应用程序代码层
  2. 更小的传输量:推送到注册中心时,只传输更改的层
  3. 更好的缓存效果:依赖层在各个构建之间缓存并重用
  4. 减少带宽消耗:在 CI/CD 流水线中减少数据传输

版本更新工作流程

  1. 在应用程序中进行代码更改
  2. 运行 mvn clean package 仅重新编译更改的代码
  3. 运行 docker build -t sp3-layer:v2 . 构建新镜像
  4. 只有 target/classes 层会被重新构建
  5. 只将更改的层推送到容器注册中心

配置自定义指南

要将此配置应用于其他项目,您需要根据项目的依赖结构调整配置:

修改依赖分组

如果您有不同的内部依赖,需要修改 excludeGroupIdsincludeGroupIds 配置:

<!-- 外部依赖配置 -->
<excludeGroupIds>com.yourcompany,org.another.internal.group</excludeGroupIds>
<!-- 内部依赖配置 -->
<includeGroupIds>com.yourcompany,org.another.internal.group</includeGroupIds>

最佳实践

  1. 顺序很重要:首先复制不经常更改的层
  2. 最小化层数:只分离实际在不同频率下更改的内容
  3. 多阶段构建:考虑与多阶段构建结合使用以获得更小的最终镜像
  4. 缓存失效:了解 Docker 的层缓存机制以最大化收益

这种方法可以显著减少 CI/CD 流水线中的构建时间,并在部署更新时最小化网络传输。

到此这篇关于SpringBoot项目docker分层镜像构建案例的文章就介绍到这了,更多相关SpringBoot docker分层镜像 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于params、@PathVariabl和@RequestParam的用法与区别说明

    基于params、@PathVariabl和@RequestParam的用法与区别说明

    这篇文章主要介绍了方法参数相关属性params、@PathVariabl和@RequestParam用法与区别,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • MyBatis注解实现动态SQL问题

    MyBatis注解实现动态SQL问题

    这篇文章主要介绍了MyBatis注解实现动态SQL问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • IntelliJ IDEA cmd和idea Terminal查看java版本不一致的解决方案

    IntelliJ IDEA cmd和idea Terminal查看java版本不一致的解决

    原来win10电脑上安装的是jdk8的版本,因某些原因,现在想换成jdk7的版本,修改环境变量后,在cmd中执行 [java -version]命令,显示的是7的版本,遇到这样的问题如何解决呢?下面小编给大家分享IntelliJ IDEA cmd和idea Terminal查看java版本不一致的解决方案,一起看看吧
    2023-09-09
  • springboot如何获取yaml/yml(或properties)配置文件信息

    springboot如何获取yaml/yml(或properties)配置文件信息

    在SpringBoot项目中,读取配置文件信息是常见需求,可以通过@Autowired注入Environment类,使用@Value注解直接注入配置信息,或定义工具类结合ApplicationRunner进行高级配置信息获取,特别提到
    2024-11-11
  • Springboot 中使用Sentinel的详细步骤

    Springboot 中使用Sentinel的详细步骤

    文章介绍了如何在SpringBoot中使用Sentinel进行限流和熔断降级,首先添加依赖,配置Sentinel控制台地址,定义受保护的资源,配置流控规则,启动Sentinel控制台和SpringBoot应用,最后测试和监控,感兴趣的朋友一起看看吧
    2025-02-02
  • 基于Java制作一个好玩的打飞机游戏

    基于Java制作一个好玩的打飞机游戏

    这篇文章主要介绍了基于Java制作的打飞机小游戏,这里整理了详细的代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • VSCode 配置 Spring Boot 项目开发环境的全过程

    VSCode 配置 Spring Boot 项目开发环境的全过程

    两三年前曾经试过配置Java环境, 存在不少问题作罢. 最近搜了下相关的文章, 感觉VSCode对Java项目的支持比三年前完善了不少. 今天实际配置了一下环境, 把自己常用的功能过了一遍, 基本能跑通开发流程, 做个笔记,需要的朋友可以参考下
    2024-03-03
  • Java文本编辑器实现方法详解

    Java文本编辑器实现方法详解

    这篇文章主要介绍了Java文本编辑器实现方法,结合实例形式详细分析了java文本编辑器结构、原理、布局、实现步骤与相关操作技巧,需要的朋友可以参考下
    2019-03-03
  • java实现新浪微博Oauth接口发送图片和文字的方法

    java实现新浪微博Oauth接口发送图片和文字的方法

    这篇文章主要介绍了java实现新浪微博Oauth接口发送图片和文字的方法,涉及java调用新浪微博Oauth接口的使用技巧,具有一定参考接借鉴价值,需要的朋友可以参考下
    2015-07-07
  • ​​Spring Boot SPI 用法教程​

    ​​Spring Boot SPI 用法教程​

    文章介绍了Java SPI(服务提供者接口)和Spring Boot中的SPI机制,包括它们的定义、使用方式和实战示例,文章还讨论了常见问题和最佳实践,如版本兼容性、条件装配、避免类路径冲突以及扩展点选择,感兴趣的朋友一起看看吧
    2025-11-11

最新评论