SpringBoot-2-3-x分层构建Docker镜像实践 上

简介: SpringBoot-2-3-x分层构建Docker镜像实践

系统环境:

  • Docker 版本:19.03.13
  • Open JDK 基础镜像版本:openjdk:8u275
  • 私有的 Harbor 镜像仓库:自建 Harbor 私库
  • 项目 Github:

https://github.com/my-dlq/blog-example/tree/master/springboot/springboot-layer

参考地址:

https://docs.docker.com/storage/storagedriver/

一、什么是镜像分层

镜像的构成

现在一谈起镜像大部分都是指 Docker 引擎构建的镜像,一般 Docker 镜像是由很多层组成,底层是操作系统,然后在其之上是基础镜像或者用户自定义 Dockerfile 脚本中定义的中间层。

其中镜像在构建完成后,用户只能对镜像进行读操作,而不能进行写操作,只有镜像启动后变为容器,才能进行读写操作。镜像整体结构,可以观看下图:

该图中展示了镜像的基本组成,但是图中这一个个中间层是什么呢?要想了解这些层具体是什么,那得知道如何构建 Docker 镜像了。平时我们构建 Docker 镜像时候,都是编写 Dockerfile 脚本,然后使用 Docker 镜像构建命令,按照脚本一行行执行构建。

如下就是一个 Dockerfile 脚本,脚本内容就构建 Java 项目镜像常用的 Dockerfile 命令:

FROM openjdk:8u275
VOLUME /tmp
ADD target/*.jar app.jar
ENV TZ="Asia/Shanghai"
ENV JAVA_OPTS=""
ENV JVM_OPTS="-XX:MaxRAMPercentage=80.0"
ENTRYPOINT ["sh","-c","java $JVM_OPTS $JAVA_OPTS -jar /app.jar"]

有了 Dockerfile 脚本,我们需要执行 Docker 的构建镜像命令对执行 Dockerfile 脚本构建镜像,其中构建镜像的过程如下:

## 构建镜像的命令
$ docker build -t java-test:latest . 
## 命令执行的过程
Step 1/7 : FROM openjdk:8u275
 ---> 82f24ce79de6
Step 2/7 : VOLUME /tmp
 ---> Running in a6361fdfc193
Removing intermediate container a6361fdfc193
 ---> a43948bf1b98
Step 3/7 : ADD target/*.jar app.jar
 ---> 18f4bc60818f
Step 4/7 : ENV TZ="Asia/Shanghai"
 ---> Running in cc738aa5865b
Removing intermediate container cc738aa5865b
 ---> 538adb85609e
Step 5/7 : ENV JAVA_OPTS=""
 ---> Running in f8b635d32b2b
Removing intermediate container f8b635d32b2b
 ---> 34e7a8cd7b6e
Step 6/7 : ENV JVM_OPTS="-XX:MaxRAMPercentage=80.0"
 ---> Running in 9331cb6e443e
Removing intermediate container 9331cb6e443e
 ---> 232b9c6c1d29
Step 7/7 : ENTRYPOINT ["sh","-c","java $JVM_OPTS $JAVA_OPTS -jar /app.jar" ]
 ---> Running in c3a24fba3a10
Removing intermediate container c3a24fba3a10
 ---> a41974d5f0e3

可以看到总共存在 7 个构建步骤,每步都与 Dockerfile 里面一行指令对应。样子和下图相似:

如果这时候,我们改变原来 Dockerfile 内容,创建一个新的镜像,其 Dockerfile 如下:

FROM openjdk:8u275
VOLUME /tmp
ADD target/*.jar app.jar
ENV TZ="Asia/Macao"                  #与原来 Dockerfile 不同
ENV JVM_OPTS="-Xmx512m -Xss256k"     #与原来 Dockerfile 不同
ENTRYPOINT ["sh","-c","java $JVM_OPTS $JAVA_OPTS -jar /app.jar" ]

执行 Docker 命令构建镜像:

$ docker build -t java-test2:latest .
Step 1/6 : FROM openjdk:8u275
 ---> 82f24ce79de6
Step 2/6 : VOLUME /tmp
 ---> Using cache
 ---> a43948bf1b98
Step 3/6 : ADD target/*.jar app.jar
 ---> Using cache
 ---> 18f4bc60818f
Step 4/6 : ENV TZ="Asia/Macao"
 ---> Running in fd98b90a5485
Removing intermediate container fd98b90a5485
 ---> afab3fcdab07
Step 5/6 : ENV JVM_OPTS="-Xmx512m -Xss256k"
 ---> Running in 19a99576fba9
Removing intermediate container 19a99576fba9
 ---> 4eeab7d7c720
Step 6/6 : ENTRYPOINT ["sh","-c","java $JVM_OPTS $JAVA_OPTS -jar /app.jar" ]
 ---> Running in 2dba72e1eef4
Removing intermediate container 2dba72e1eef4
 ---> 7c706ecf7698

可以观察到执行过程中,从一开始执行的构建步骤中显示,并没有生成新的中间层镜像,而是直接使用了已经存在的缓存镜像。直至 4⁄6 这部中,由于新的 Dockerfile 与原来 Dockerfile 发生变动,所以这部中间层镜像直接是新创建的,并没有使用缓存中间层镜像。

然后往下观察,发现之后的全部构建都是新创建的中间层镜像,即是脚本最后的一行和原来相同,也没有使用缓存中间层镜像。

上面现象说明,Docker 镜像在构建过程中按照 Dockerfile 自上往下的执行顺序中,如果从最上层开始,其脚本内容和已有的缓存中间层镜像内容一致,就会引入缓存中的中间层镜像(并不是直接复制缓存镜像,而是引入镜像文件地址,多个镜像共享这些中间层镜像)。

但是,如果执行过程中中间任意一行镜像构建的内容发生变化,那么当前行和之后的全部行在执行时就不会使用缓存中的中间层镜像,而是全部创建新的镜像。

这就是 Docker 镜像中缓存中间层镜像的复用,学会使用缓存构建镜像将大大减少存储空间的占用以及镜像的构建的构建速度,镜像的缓存不仅仅体现在镜像的构建上,在执行”镜像推送”、”镜像拉取”操作时都能观察到其的好处。

  • 镜像缓在镜像推送的体现: 如镜像推送时候,也是将镜像整体构成的中间层镜像并行推送到镜像仓库,如果镜像仓库中已经存某个中间层镜像,那么推送过程就不会再次将该层镜像推送到镜像仓库,而是将仓库中并不存在中间层镜像推送到其中。
  • 镜像缓存在镜像拉取的体现: 在拉取镜像时候,如果本地某个大镜像的中间层镜像的组成中,已经包含新拉取镜像的中间层部分镜像,那么将直接复用本地已经镜像的中间层镜像,不必再将其进行拉取,而本地不存在的中间层镜像将会被继续拉取。

说了这么多,相信大家已经对镜像缓存的使用有了初步了解,那么再谈及为什么需要镜像分层就很好解释,其原因就是 Docker 想提高资源的复用率,将一个大镜像拆分成很多层小镜像组成,以达到镜像中间层的复用的目的。

二、SpringBoot 2.3.x 新增对分层的支持

SpringBoot 2.3.x 以后支持分层打包应用,需要 Pom.xml 中引入 SpringBoot 2.3.x 后的父依赖和使用 SpringBoot 打包插件 spring-boot-maven-plugin,并且开启 layers 功能,然后执行 Maven 编译源码构建 Jar 包,使用该 Jar 包就可以构建基于分层模式的 Docker 镜像:

项目 pom.xml 中引入 SpringBoot 2.3.x 依赖:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.6.RELEASE</version>
    <relativePath/>
</parent>

项目 pom.xml 中引入 spring-boot-maven-plugin 打包插件,并且开启分层功能:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <!--开启分层编译支持-->
                <layers>
                    <enabled>true</enabled>
                </layers>
            </configuration>
        </plugin>
    </plugins>
</build>

执行 Maven 命令,构建分层的 JAR 包,命令和平时的 Maven 构建命令相同:

$ mvn install

观察 Jar 结构,可以看到里面多了 classpath.idxlayers.idx 两个文件:

  • classpath.idx: 文件列出了依赖的 jar 包列表,到时候会按照这个顺序载入。
  • layers.idx: 文件清单,记录了所有要被复制到 Dokcer 镜像中的文件信息。

根据官方介绍,在构建 Docker 镜像前需要从 Jar 中提起出对应的分层文件到 Jar 外面,可用使用下面命令列出可以从分层 Jar 中提取出的文件夹信息:

$ java -Djarmode=layertools -jar target/springboot-layer-0.0.1.jar list

可用该看到以下输出,下面的内容就是接下来使用分层构建后,生成的 Jar 提取出对应资源后的结构:

dependencies
spring-boot-loader
snapshot-dependencies
application

上面即是使用分层工具提取 Jar 的内容后生成的文件夹,其中各个文件夹作用是:

  • dependencies: 存储项目正常依赖 Jar 的文件夹。
  • snapshot-dependencies: 存储项目快照依赖 Jar 的文件夹。
  • resources: 用于存储静态资源的文件夹。
  • application: 用于存储应用程序类相关文件的文件夹。

相关实践学习
Docker镜像管理快速入门
本教程将介绍如何使用Docker构建镜像,并通过阿里云镜像服务分发到ECS服务器,运行该镜像。
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
9月前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
2395 10
|
9月前
|
人工智能 前端开发 Docker
从本地到云端:用 Docker Compose 与 Offload 构建可扩展 AI 智能体
在 AI 智能体开发中,开发者常面临本地调试与云端部署的矛盾。本文介绍如何通过 Docker Compose 与 Docker Offload 解决这一难题,实现从本地快速迭代到云端高效扩容的全流程。内容涵盖多服务协同、容器化配置、GPU 支持及实战案例,助你构建高效、一致的 AI 智能体开发环境。
841 2
从本地到云端:用 Docker Compose 与 Offload 构建可扩展 AI 智能体
|
9月前
|
JavaScript Docker 容器
使用Docker多阶段构建优化镜像大小
使用Docker多阶段构建优化镜像大小
545 100
|
9月前
|
缓存 安全 Linux
优化Docker镜像大小的多阶段构建实践
优化Docker镜像大小的多阶段构建实践
541 99
|
9月前
|
安全 Go Docker
使用Docker多阶段构建优化镜像大小
使用Docker多阶段构建优化镜像大小
|
9月前
|
Java Docker 容器
使用Docker多阶段构建优化镜像大小
使用Docker多阶段构建优化镜像大小
373 8
|
Java Docker Spring
spring 代码中,解决docker环境变量传递问题
1,docker变量 在docker中定义变量,使用echo $Value 能直接在命令行中显示, 这个使用spring可以直接注入到代码当中。 php中有方便的代码直接获得环境变量,但是java用起来就麻烦点。 直接使用spring的value就行了。 2,代码 比如一个spring的用户登录action。 里面用docker配置了一个默认的admin登录
1992 0
|
11月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
1390 0
|
12月前
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
1262 0
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
563 0