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

Docker 多阶段构建

多阶段构建

目录

  • 尝试
  • 创建 Dockerfile
  • 构建容器镜像
  • 运行 Spring Boot 应用程序
  • 使用多阶段构建
  • 额外资源

在传统构建中,所有构建指令都在一个构建容器中顺序执行:下载依赖项、编译代码、打包应用程序。所有这些层最终都在你的最终镜像中。这种方法虽然可行,但会导致镜像臃肿,携带不必要的负载,并增加你的安全风险。这时多阶段构建就派上用场了。
多阶段构建在你的 Dockerfile 中引入多个阶段,每个阶段都有特定的目的。可以将其视为在多个不同环境中并行运行构建的不同部分。通过将构建环境与最终运行时环境分离,你可以显著减少镜像大小和攻击面。这对于具有大型构建依赖项的应用程序尤其有利。
多阶段构建推荐用于所有类型的应用程序。
对于解释型语言,如 JavaScript、Ruby 或 Python,你可以在一个阶段中构建和压缩代码,然后将生产就绪的文件复制到一个较小的运行时镜像中。这优化了你的部署镜像。
对于编译型语言,如 C、Go 或 Rust,多阶段构建让你在一个阶段中编译,并将编译好的二进制文件复制到最终的运行时镜像中。无需在最终镜像中捆绑整个编译器。
以下是使用伪代码的多阶段构建结构的简化示例。注意这里有多个 FROM 语句和新的 AS <stage-name>。此外,第二阶段中的 COPY 语句是从前一阶段复制的。

# 第1阶段:构建环境
FROM builder-image AS build-stage 
# 安装构建工具(如 Maven、Gradle)
# 复制源代码
# 构建命令(如编译、打包)# 第2阶段:运行时环境
FROM runtime-image AS final-stage  
# 从构建阶段复制应用程序工件(如 JAR 文件)
COPY --from=build-stage /path/in/build/stage /path/to/place/in/final/stage
# 定义运行时配置(如 CMD、ENTRYPOINT) 

这个 Dockerfile 使用了两个阶段:

  1. 构建阶段:使用包含编译应用程序所需构建工具的基础镜像。包括安装构建工具、复制源代码和执行构建命令。
  2. 最终阶段:使用适合运行应用程序的较小基础镜像。从构建阶段复制编译好的工件(例如 JAR 文件)。最后,定义用于启动应用程序的运行时配置(使用 CMD 或 ENTRYPOINT)。

尝试

在本手把手指南中,你将学习如何利用多阶段构建为示例 Java 应用程序创建精简高效的 Docker 镜像。你将使用一个简单的基于 Spring Boot 的“Hello World”应用程序作为示例。

下载并安装 Docker Desktop

打开这个预初始化的项目来生成一个 ZIP 文件。如下所示:

Spring Initializr 是一个 Spring 项目的快速启动生成器。它提供了一个可扩展的 API,用于生成基于 JVM 的项目,包含多个常见概念的实现,例如 Java、Kotlin 和 Groovy 的基础语言生成。

选择“Generate”以创建并下载该项目的 ZIP 文件。

在本演示中,你将 Maven 构建自动化与 Java、Spring Web 依赖项和 Java 21 配对作为元数据。

导航到项目目录。解压缩文件后,你会看到以下项目目录结构:

spring-boot-docker
├── Dockerfile
├── Dockerfile.multi
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src├── main│   ├── java│   │   └── com│   │       └── example│   │           └── springbootdocker│   │               └── SpringBootDockerApplication.java│   └── resources│       ├── application.properties│       ├── static│       └── templates└── test└── java└── com└── example└── springbootdocker└── SpringBootDockerApplicationTests.java

src/main/java 目录包含项目的源代码,src/test/java 目录包含测试源代码,pom.xml 文件是项目的项目对象模型 (POM)。

pom.xml 文件是 Maven 项目配置的核心。它是一个单一的配置文件,包含大部分构建自定义项目所需的信息。POM 很庞大,看起来可能令人畏惧。但幸运的是,你还不需要理解其中的每一个细节就可以有效使用它。

创建一个显示“Hello World!”的 RESTful Web 服务。

src/main/java/com/example/springbootdocker/ 目录下,你可以修改 SpringBootDockerApplication.java 文件,内容如下:

package com.example.springbootdocker;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@SpringBootApplication
public class SpringBootDockerApplication {@RequestMapping("/")public String home() {return "Hello World";}public static void main(String[] args) {SpringApplication.run(SpringBootDockerApplication.class, args);}
}

SpringBootDockerApplication.java 文件首先声明了你的 com.example.springbootdocker 包并导入必要的 Spring 框架。这个 Java 文件创建了一个简单的 Spring Boot Web 应用程序,当用户访问其主页时会响应“Hello World”。

创建 Dockerfile

现在你已经有了项目,可以开始创建 Dockerfile 了。

在包含所有其他文件夹和文件(如 srcpom.xml 等)的同一文件夹中创建一个名为 Dockerfile 的文件。

Dockerfile 中,通过添加以下行定义你的基础镜像:

FROM eclipse-temurin:21.0.2_13-jdk-jammy

现在,使用 WORKDIR 指令定义工作目录。这将指定未来命令的运行目录以及文件将在容器镜像内复制的位置。

WORKDIR /app

将 Maven 包装脚本和项目的 pom.xml 文件复制到容器内的当前工作目录 /app 中。

COPY .mvn/ .mvn
COPY mvnw pom.xml ./

在容器内执行命令。它运行 ./mvnw dependency:go-offline 命令,使用 Maven 包装器 (./mvnw) 下载项目的所有依赖项而不构建最终的 JAR 文件(有助于更快的构建)。

RUN ./mvnw dependency:go-offline

将主机机器上的 src 目录复制到容器内的 /app 目录中。

COPY src ./src

设置容器启动时要执行的默认命令。该命令指示容器使用 spring-boot:run 目标运行 Maven 包装器 (./mvnw),这将构建并执行你的 Spring Boot 应用程序。

CMD ["./mvnw", "spring-boot:run"]

这样,你应该有以下 Dockerfile

FROM eclipse-temurin:21.0.2_13-jdk-jammy
WORKDIR /app
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
COPY src ./src
CMD ["./mvnw", "spring-boot:run"]

构建容器镜像

执行以下命令构建 Docker 镜像:

docker build -t spring-helloworld .

使用 docker images 命令检查 Docker 镜像的大小:

docker images

执行后将产生如下输出:

REPOSITORY          TAG       IMAGE ID       CREATED          SIZE
spring-helloworld   latest    ff708d5ee194   3 minutes ago    880MB

此输出显示你的镜像大小为 880MB。它包含完整的 JDK、Maven 工具链等。在生产环境中,你不需要在最终镜像中包含这些内容。

运行 Spring Boot 应用程序

现在你已经构建了镜像,是时候运行容器了。

docker run -d -p 8080:8080 spring-helloworld

你将在容器日志中看到类似以下的输出:

[INFO] --- spring-boot:3.3.0-M3:run (default-cli) @ spring-boot-docker ---
[INFO] Attaching agents: [].   ____          _            __ __/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::             (v3.3.0-M3)2024-04-04T15:36:47.202Z  INFO 42 --- [spring-boot-docker] [           main]       c.e.s.SpringBootDockerApplication        : Starting SpringBootDockerApplication using Java    21.0.2 with PID 42 (/app/target/classes started by root in /app)….

通过浏览器访问 http://localhost:8080 ,或使用以下 curl 命令访问你的“Hello World”页面:

curl localhost:8080

输出:

Hello World

使用多阶段构建

考虑以下 Dockerfile

FROM eclipse-temurin:21.0.2_13-jdk-jammy AS builder
WORKDIR /opt/app
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
COPY ./src ./src
RUN ./mvnw clean installFROM eclipse-temurin:21.0.2_13-jre-jammy AS final
WORKDIR /opt/app
EXPOSE 8080
COPY --from=builder /opt/app/target/*.jar /opt/app/*.jar
ENTRYPOINT ["java", "-jar", "/opt/app/*.jar"]

注意,这个 Dockerfile 被分成了两个阶段。

第一阶段与之前的 Dockerfile 相同,提供了一个用于构建应用程序的 Java 开发工具包 (JDK) 环境。这个阶段被命名为 builder

第二阶段是一个新的阶段,名为 final。它使用一个更精简的 eclipse-temurin:21.0.2_13-jre-jammy 镜像,只包含运行应用程序所需的 Java 运行时环境 (JRE)。这个镜像提供了一个 Java 运行时环境 (JRE),足以运行编译好的应用程序 (JAR 文件)。

对于生产环境,强烈推荐使用 jlink 生成自定义 JRE 类似的运行时。JRE 镜像适用于所有版本的 Eclipse Temurin,但 jlink 允许你创建仅包含应用程序所需 Java 模块的最小运行时。这可以显著减少大小并提高最终镜像的安全性。参考此页面以获取更多信息。

使用多阶段构建,一个 Docker 构建使用一个基础镜像进行编译、打包和单元测试,然后使用另一个镜像进行应用程序运行。因此,最终镜像较小,因为它不包含任何开发或调试工具。通过将构建环境与最终运行时环境分离,你可以显著减少镜像大小并提高最终镜像的安全性。

现在,重建你的镜像并运行准备好的生产构建。

docker build -t spring-helloworld-builder .

此命令使用你所在目录中的 Dockerfile 的最终阶段构建一个名为 spring-helloworld-builderDocker 镜像。

注意

在你的多阶段 Dockerfile 中,最终阶段 (final) 是构建的默认目标。这意味着如果你不使用 --target 标志在 docker build 命令中显式指定目标阶段,Docker 将自动构建最后一个阶段。你可以使用 docker build -t spring-helloworld-builder --target builder . 构建仅包含 JDK 环境的构建阶段。

使用 docker images 命令查看镜像大小差异:

docker images

你会得到类似以下的输出:

spring-helloworld-builder latest    c5c76cb815c0   24 minutes ago      428MB
spring-helloworld         latest    ff708d5ee194   About an hour ago   880MB

你的最终镜像只有 428 MB,而原始构建大小为 880 MB。

通过优化每个阶段并仅包含必要内容,你能够显著减少整体镜像大小,同时仍然实现相同的功能。这不仅提高了性能,还使你的 Docker 镜像更加轻量、安全且易于管理。

相关文章:

Docker 多阶段构建

多阶段构建 目录 尝试创建 Dockerfile构建容器镜像运行 Spring Boot 应用程序使用多阶段构建额外资源 在传统构建中&#xff0c;所有构建指令都在一个构建容器中顺序执行&#xff1a;下载依赖项、编译代码、打包应用程序。所有这些层最终都在你的最终镜像中。这种方法虽然可行…...

Linux应急响应——知攻善防应急靶场-Linux(1)

文章目录 查看history历史指令查看开机自启动项异常连接和端口异常进程定时任务异常服务日志分析账户排查总结 靶场出处是知攻善防 Linux应急响应靶机 1 前景需要&#xff1a; 小王急匆匆地找到小张&#xff0c;小王说"李哥&#xff0c;我dev服务器被黑了",快救救我&…...

基于CDMA的多用户水下无线光通信(1)——背景介绍

研究生期间做多用户水下无线光通信&#xff08;Underwater Optical Wireless Communication&#xff0c;UOWC&#xff09;&#xff0c;写几篇博客分享一下学的内容。导师给了大方向&#xff0c;让我用直接序列码分多址&#xff08;Direct Sequence Code Division Multiple Acce…...

vlan三层交换技术--交换机--(自作)

...

基于springboot websocket和okhttp实现消息中转

1、业务介绍 消息源服务的消息不能直接推给用户侧&#xff0c;用户与中间服务建立websocket连接&#xff0c;中间服务再与源服务建立websocket连接&#xff0c;源服务的消息推给中间服务&#xff0c;中间服务再将消息推送给用户。流程如下图&#xff1a; 此例中我们定义中间服…...

@PostConstruct 注解的方法用于资源的初始化

PostConstruct 是 Java EE 5 引入的一个注解&#xff0c;主要用于依赖注入完成之后&#xff0c;需要执行的方法上。这个注解的方法会在依赖注入完成后自动被容器&#xff08;如 EJB 容器或 Spring 容器&#xff09;调用&#xff0c;并且只会被调用一次。 PostConstruct 注解的…...

(一)SvelteKit教程:hello world

&#xff08;一&#xff09;SvelteKit教程&#xff1a;hello world sveltekit 的官方教程&#xff0c;在这里&#xff1a;Creating a project • Docs • SvelteKitCreating a project • Docs • SvelteKit 我们可以按照如下的步骤来创建一个项目&#xff1a; npm create s…...

华为Atlas NPU ffmpeg 编译安装

处理器&#xff1a;鲲鹏920 NPU&#xff1a;昇腾 310P3 操作系统&#xff1a;Kylin Linux Advanced Server V10 CANN&#xff1a;Ascend-cann-toolkit_8.0.RC1_linux-aarch64.run FFmpeg&#xff1a;AscendFFmpegPlugin(不要用AscendFFmpeg) AscendFFmpegPlugin下载地址&…...

Python 虚拟环境 requirements.txt 文件生成 ;pipenv导出pip安装文件

搜索关键词: Python 虚拟环境Pipenv requirements.txt 文件生成;Pipenv 导出 pip requirements.txt安装文件 本文基于python版本 >3.9 文章内容有效日期2023年01月开始(因为此方法从这个时间开始是完全ok的) 上述为pipenv的演示版本 使用以下命令可精准生成requirement…...

Less与Sass的区别

1. 功能和工具&#xff1a; Sass&#xff1a;提供了更多的功能和内置方法&#xff0c;如条件语句、循环、数学函数等。Sass 也支持更复杂的操作和逻辑构建。 Less&#xff1a;功能也很强大&#xff0c;但相比之下&#xff0c;Sass 在功能上更为丰富和成熟。 2、编译环境&…...

力扣-2663

题目 如果一个字符串满足以下条件&#xff0c;则称其为 美丽字符串 &#xff1a; 它由英语小写字母表的前 k 个字母组成。它不包含任何长度为 2 或更长的回文子字符串。 给你一个长度为 n 的美丽字符串 s 和一个正整数 k 。 请你找出并返回一个长度为 n 的美丽字符串&#…...

CausalMMM:基于因果结构学习的营销组合建模

1. 摘要 在线广告中&#xff0c;营销组合建模&#xff08;Marketing Mix Modeling&#xff0c;MMM&#xff09; 被用于预测广告商家的总商品交易量&#xff08;GMV&#xff09;&#xff0c;并帮助决策者调整各种广告渠道的预算分配。传统的基于回归技术的MMM方法在复杂营销场景…...

编译 CUDA 程序的基本知识和步骤

基本工具 NVCC&#xff08;NVIDIA CUDA Compiler&#xff09;: nvcc 是 NVIDIA 提供的 CUDA 编译器&#xff0c;用于将 CUDA 源代码&#xff08;.cu 文件&#xff09;编译成可执行文件或库。它可以处理 CUDA 和主机代码&#xff08;例如 C&#xff09;的混合编译。nvcc 调用底层…...

[SAP ABAP] 排序内表数据

语法格式 整表排序 SORT <itab> [ASCENDING|DESCENDING]. 按指定字段排序 SORT <itab> BY f1 [ASCENDING|DESCENDING] f2 [ASCENDING|DESCENDING] ... fn [ASCENDING|DESCENDING].<itab>&#xff1a;代表内表 不指定排序方式则默认升序排序 示例1 结果显…...

【UML用户指南】-21-对基本行为建模-活动图

目录 1、概念 2、组成结构 2.1、动作 2.2、活动节点 2.3、控制流 2.4、分支 2.5、分岔和汇合 2.6、泳道 2.7、对象流 2.8、扩展区域 3、一般用法 3.1、对工作流建模 3.2、对操作建模 一个活动图从本质上说是一个流程图&#xff0c;展现从活动到活动的控制流 活动图…...

【web2】jquary,bootstrap,vue

文章目录 1.jquary&#xff1a;选择器1.1 jquery框架引入&#xff1a;$("mydiv") 当成id选择器1.2 jquery版本/对象&#xff1a;$(js对象) -> jquery对象1.3 jquery的页面加载事件&#xff1a;$ 想象成 window.onload 1.4 jquery的基本选择器&#xff1a;$()里内容…...

独角兽品牌獭崎酱酒:高性价比的酱香之选

在酱香型白酒领域中&#xff0c;獭崎酱酒以其独特的品牌定位和高性价比迅速崛起&#xff0c;成为市场上备受关注的独角兽品牌。作为贵州茅台镇的一款新秀酱香酒&#xff0c;獭崎酱酒不仅传承了百年酿造工艺&#xff0c;还以创新的商业模式和亲民的价格赢得了广大消费者的青睐。…...

java打印菱形和空心菱形

java打印菱形 菱形分上下两个部分。其中上部分同打印金字塔&#xff1b;下部分循环部分i是递减 &#xff08;ps:菱形层数只能为奇数&#xff09; import java.util.Scanner;public class Lingxing{public static void main(String[] args) {Scanner myScanner new Scanner(S…...

Day10 —— 大数据技术之Scala

Scala编程入门 Scala的概述什么是Scala&#xff1f;Scala的重要特点Scala的使用场景 Scala的安装Scala基础Scala总结 Scala的概述 什么是Scala&#xff1f; Scala是一种将面向对象和函数式编程结合在一起的高级语言&#xff0c;旨在以简洁、优雅和类型安全的方式表达通用编程…...

Linux应用系统快速部署:docker快速部署linux应用程序

目录 一、背景 &#xff08;一&#xff09;引入docker的起因 &#xff08;二&#xff09;docker介绍 &#xff08;三&#xff09;Docker部署的优势 1、轻量级和可移植性 2、快速部署和扩展 3、一致性 4、版本控制 5、安全性 6、资源隔离 7、简化团队协作 8、多容器…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...