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

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 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: 用于存储应用程序类相关文件的文件夹。

相关实践学习
通过容器镜像仓库与容器服务快速部署spring-hello应用
本教程主要讲述如何将本地Java代码程序上传并在云端以容器化的构建、传输和运行。
Kubernetes极速入门
Kubernetes(K8S)是Google在2014年发布的一个开源项目,用于自动化容器化应用程序的部署、扩展和管理。Kubernetes通常结合docker容器工作,并且整合多个运行着docker容器的主机集群。 本课程从Kubernetes的简介、功能、架构,集群的概念、工具及部署等各个方面进行了详细的讲解及展示,通过对本课程的学习,可以对Kubernetes有一个较为全面的认识,并初步掌握Kubernetes相关的安装部署及使用技巧。本课程由黑马程序员提供。 &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
相关文章
|
7天前
|
运维 Kubernetes Docker
利用Docker和Kubernetes构建微服务架构
利用Docker和Kubernetes构建微服务架构
|
9天前
|
Java
SpringBoot构建Bean(RedisConfig + RestTemplateConfig)
SpringBoot构建Bean(RedisConfig + RestTemplateConfig)
30 2
|
13天前
|
存储 监控 Linux
docker构建镜像详解!!!
本文回顾了Docker的基本命令和管理技巧,包括容器和镜像的增删改查操作,容器的生命周期管理,以及如何通过端口映射和数据卷实现容器与宿主机之间的网络通信和数据持久化。文章还详细介绍了如何使用Docker部署一个简单的Web应用,并通过数据卷映射实现配置文件和日志的管理。最后,文章总结了如何制作自定义镜像,包括Nginx、Python3和CentOS镜像,以及如何制作私有云盘镜像。
81 2
|
7天前
|
API Docker 容器
【赵渝强老师】构建Docker Swarm集群
本文介绍了如何使用三台虚拟主机构建Docker Swarm集群。首先在master节点上初始化集群,然后通过特定命令将node1和node2作为worker节点加入集群。最后,在master节点上查看集群的节点信息,确认集群构建成功。文中还提供了相关图片和视频教程,帮助读者更好地理解和操作。
|
7天前
|
缓存 JavaScript 安全
深入理解Docker镜像构建过程
深入理解Docker镜像构建过程
23 0
|
9天前
|
XML 存储 Java
SpringBoot集成Flowable:构建强大的工作流引擎
在企业级应用开发中,工作流管理是核心功能之一。Flowable是一个开源的工作流引擎,它提供了BPMN 2.0规范的实现,并且与SpringBoot框架完美集成。本文将探讨如何使用SpringBoot和Flowable构建一个强大的工作流引擎,并分享一些实践技巧。
26 0
|
Java 测试技术 Docker
Spring Boot 学习研究笔记(十九)-docker部署SpringBoot 日志目录挂载
Spring Boot 学习研究笔记(十九)-docker部署SpringBoot 日志目录挂载
505 0
|
Java 应用服务中间件 Linux
使用docker部署springboot项目小白教程
使用docker部署springboot项目小白教程
527 0
|
6月前
|
Java 关系型数据库 Docker
docker打包部署spring boot应用(mysql+jar+Nginx)
docker打包部署spring boot应用(mysql+jar+Nginx)
|
6月前
|
关系型数据库 Java 数据库
docker部署postgresql数据库和整合springboot连接数据源
docker部署postgresql数据库和整合springboot连接数据源
160 0