当前位置: 首页 > ds >正文

Docker多阶段构建Maven项目

1、Dockerfile

# 第一阶段:构建阶段 (Builder stage)
FROM maven:3.6.3-jdk-11-slim AS builder # <-- 就是这行指令
WORKDIR /app
COPY pom.xml .
COPY src ./src
# 在“builder”容器中执行编译打包命令
RUN mvn clean package -DskipTests# 第二阶段:运行阶段 (Runtime stage)
FROM openjdk:11-jre-slim # <-- 一个更小的运行环境
WORKDIR /app
# 从上一阶段(builder)的容器中复制构建产物,而不是复制本地文件
COPY --from=builder /app/target/my-app.jar ./app.jar
# 暴露端口
EXPOSE 8080
# 启动命令
CMD ["java", "-jar", "app.jar"]

2、基础镜像

FROM maven:3.6.3-jdk-11-slim AS builder

FROM:Dockerfile指令,用于指定当前构建阶段基于的集成镜像

maven:3.6.3-jdk-11-slim:这是官方提供的Maven镜像的一个特定标签

maven:镜像名称

3.6.3:指定的Maven版本

jdk-11:表示这个镜像内部预装了jdk 11

slim:镜像的变种,一个瘦身版本,通常只包含运行指定工具(Maven和JDK)所必须的最小包,去除了文档,不必要的库

as builder:为这个构建阶段命名一个别名(builder)。这个别名将在后续的阶段中被引用,以便从该阶段复杂文件

3、执行过程

当Docker守护进程执行到这个指令时,它会进行以下操作

1、拉取镜像

Docker会检查本地是否存在maven:3.6.3-jdk-11-slim这个镜像,如果不存在,它会自动从Docker Hub(或其他配置的镜像仓库)拉取该镜像

拉取的镜像包含了一个预先配置好的Linux操作系统环境,其中主要安装了:

  • 一个最小化的Linux系统(通常Debian或Alpine精简版)
  • Java开发工具包(JDK11)完整的JDK不仅仅JRE,因为要编译代码需要javac等
  • Apache Maven 3.6.3:预配置好的Maven工具,其setting.xml可能包含基础配置,并且其本地仓库(/root/.m2/repository)可能时空的或只有基本依赖

2、创建并启动一个临时容器

Docker会以这个镜像为基础,创建一个新的、暂时的容器,这个容器被标记为名为builder的构建阶段,此时,这个容器提供了一个高度专门化的环境,这个环境唯一的目的就是用于编译和构建Java Maven项目

3、为后续指令提供上下文

这行指令本身并不执行构建命令,但它设置了舞台,后续的指令(如COPY、RUN、WORKDIR)将在由这个FROM这令创建的临时容器环境中执行

4、设置工作目录

WORKDIR /app

用户指令:设置工作目录为/app,如果目录不存在,则创建它

Docker内部操作:在由上一步FROM指令创建的临时容器中,执行 mkdir -p /app 命令创建目录

设置后续指令(COPY、RUN)的当前工作目录为/app,这意味着任何相对路径都将基于此目录

5、复制

COPY pom.xml .

用户指令:将构建上下文(通常是Dockerfile所在目录)中的pom.xml文件复制到容器当前工作目录(/app)

Docker内部操作:

1、检查缓存:这是构建过程中一个非常重要的优化点,Docker会计算pom.xml文件的校验和(checksum)

2、缓存命中:如果与此前某次构建的校验和完全一致,Docker会直接复用之前构建此步骤时创建的镜像层,跳过后续所有步骤,直接进入第二阶段,提高构建速度

3、缓存未命中:如果文件变化,Docker会继续执行

4、文件复制:将主机上的pom.xml文件复制到临时容器/app目录下,并创建一个新的镜像层来记录这个变化

COPY src ./src

用户指令:将本地src目录递归复制到容器的/app/src目录中

Docker内部操作:

1、类似于上一步,Docker会检查src目录中所有文件的检验和,单由于pom.xml通常先变化,而源代码后变化,所以上一步的缓存失效通常会导致这一步也必须执行

2、将主机上的src目录及全部内容复制到临时容器中,创建另一个新的镜像层

6、构建打包

RUN mvn clean package -DskipTests

用户指令:在容器内执行Maven命令,清理就构建,打包新应用(跳过测试)

Docker内部操作:

1、创建新容器层:RUN 指令总是在新的层上执行命令

2、启动子进程:Docker在当前的临时容器内启动一个shell进程(/bin/sh -c)来运行给定的命令

3、执行Maven

        Maven读取pom.xml文件

        从远程仓库下载所有的依赖(这些依赖会被下载到容器内的Maven本地仓库,通常位于/root/.m2/repository),这是构建过程中最耗时的步骤。

        编译src下的源代码

        运行所有编译阶段的生命周期(跳过test)

        将最终的制品 jar 打包到target 目录

4、提交结果:命令执行成功后,docker将命令执行结果(即包含了编译产物的容器状态)提交为一个新的、只读的镜像层,这个层包含了 target/test.jar

至此,第一个阶段临时容器使命完成,其最终状态是一个包含了源代码,依赖库和最终构建产物的镜像,这个镜像标记为builder

7、运行阶段

FROM openjdk:11-jre-slim

用户指令:开始一个全新的、独立的构建阶段,基于一个包含JRE的轻量级的镜像

Docker内部操作:

1、重置环境:Docker完全抛弃了第一阶段创建的所有临时状态和环境,它从一个全新的镜像开始,与第一阶段的JDK、Maven、源代码完全隔离

2、拉取镜像:同样,检查并拉取openjdk:11-jre-slim镜像,这个镜像比第一阶段的Maven镜像小的多

3、创建新的临时容器:基于这个新的基础镜像创建一个新的临时容器,用于构建最终的运行时镜像

8、设置工作目录

WORKDIR /app

用户指令和操作:与第一阶段相同,在新的临时容器中创建并设置工作目录/app

9、引用第一阶段

COPY --from=builder /app/target/my-app.jar ./app.jar

用户指令:从之前命名为builder的阶段中复制文件,而不是从主机复制

Docker内部操作:

定位源阶段:Docker根据--from=builder参数,定位到第一阶段最终生成的镜像

复制文件:从builder镜像的/app/target目录找到my-app.jar文件,并将其复制到当前第二阶段的临时容器的当前工作目录/app下,并重命名为app.jar

10、监听端口

EXPOSE 8080

用户指令:什么容器运行时监听的网络端口

Docker内部操作:

1、这只是一个元数据指令,它不会实际打开任何端口

2、Docker将此信息记录到镜像的元数据中,当然使用docker inspect 查看镜像时,可以看到这个信息

3、当基于此镜像运行容器时,使用 -p 8080:8080 参数会将主机8080端口映射到这个申明的容器端口

11、执行命令

CMD ["java", "-jar", "app.jar"]

用户指令:指定容器启动默认执行的命令

Docker内部操作:

1、这也是一个元数据指令,Docker将此JSON数据格式的命令记录到镜像的元数据中

2、当运行docker run <image-name>时,容器内的pid 1进程将是 java -jar app.jar

3、因为工作目录是/app,而app.jar就在该目录下,所以命令可以成功找到并执行

http://www.xdnf.cn/news/20144.html

相关文章:

  • K8s资源管理:高效管控CPU与内存
  • React学习之路永无止境:下一步,去向何方?
  • Jmeter基础教程详解
  • STM32H750 RTC介绍及应用
  • 国产GEO工具哪家强?巨推集团、SEO研究协会网、业界科技三强对比
  • 用C++实现日期类
  • upload-labs通关笔记-第17关文件上传关卡之二次渲染jpg格式
  • 关于如何在PostgreSQL中调整数据库参数和配置的综合指南
  • Vue基础知识-脚手架开发-子传父(props回调函数实现和自定义事件实现)
  • Win11 解决访问网站525 问题 .
  • 【RK3576】【Android14】如何在Android kernel-6.1 的版本中添加一个ko驱动并编译出来?
  • Django 常用功能完全指南:从核心基础到高级实战
  • [光学原理与应用-401]:设计 - 深紫外皮秒脉冲激光器 - 元件 - 布拉格衍射在深紫外皮秒声光调制器(AOM)中的核心作用与系统实现
  • 小程序:12亿用户的入口,企业数字化的先锋军
  • Linux编程——网络编程(UDP)
  • 计算机网络模型入门指南:分层原理与各层作用
  • 对接旅游行业安全需求:旅游安全急救实训室的功能构建与育人目标
  • 网络安全初级-渗透测试
  • 用AI做TikTok影视解说,全流程全自动成片,不懂外语也能做全球矩阵!
  • 办公任务分发项目 laravel vue mysql 第一章:核心功能构建 API
  • 系统越拆越乱?你可能误解了微服务的本质!
  • 【Linux系统】线程同步
  • 正则表达式与转义符的区别。注意输入的东西经过了一次转义,一次正则表达式。\\转义是单斜杠\\在正则表达式也是单斜杠所以\\\\经过两道门才是字符单斜杠
  • MongoDB Change Streams:实时监听数据变化的实战场景
  • clickhouse迁移工具clickhouse-copier
  • Python EXCEL 小技巧:最快重新排列dataframe函数
  • 工业机器人标杆的数字化突围,珞石机器人如何以CRM实现业务重塑
  • 技术视界 | 跨域机器人通信与智能系统:打破壁垒的开源探索
  • 【Linux】环境变量与程序地址空间详解
  • ansible-角色