当前位置: 首页 > 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、多容器…...

汽车电子电气架构演进:从分布式 ECU 到中央计算平台

目录 一、电子电气架构的六大演进阶段 二、高性能处理器与软件平台重构 三、宝马分层式电子电气架构设计 四、中央通信服务器与可扩展网络 五、车云一体架构与软件开发变革 六、架构升级代码示例&#xff1a;SOA 服务注册与调用 七、中央计算平台配置示例&#xff08;代码…...

STM32H743+CubeMX配置FDCAN实战:如何利用TxFIFO优化FreeRTOS下的CAN通信性能?

STM32H743CubeMX配置FDCAN实战&#xff1a;如何利用TxFIFO优化FreeRTOS下的CAN通信性能&#xff1f; 在嵌入式系统开发中&#xff0c;CAN总线因其高可靠性和实时性被广泛应用于工业控制、汽车电子等领域。当我们将目光投向STM32H743这类高性能微控制器时&#xff0c;其内置的FD…...

Qwen3-14B多场景落地指南:内容创作、编程辅助、教育问答一体化方案

Qwen3-14B多场景落地指南&#xff1a;内容创作、编程辅助、教育问答一体化方案 1. 开箱即用的私有部署方案 Qwen3-14B私有部署镜像为企业和开发者提供了一站式解决方案&#xff0c;无需复杂的环境配置即可快速启用大模型能力。这个经过深度优化的镜像专为RTX 4090D 24GB显存环…...

macOS安装OpenClaw全流程:Qwen2.5-VL-7B图文模型调试技巧

macOS安装OpenClaw全流程&#xff1a;Qwen2.5-VL-7B图文模型调试技巧 1. 为什么选择OpenClawQwen2.5-VL组合 去年冬天第一次接触OpenClaw时&#xff0c;我正被重复性的截图标注工作折磨得焦头烂额。当时尝试过几个自动化工具&#xff0c;要么功能太局限&#xff0c;要么需要把…...

结合鸿蒙系统特性:在HarmonyOS应用中嵌入Pixel Couplet Gen生成能力

结合鸿蒙系统特性&#xff1a;在HarmonyOS应用中嵌入Pixel Couplet Gen生成能力 1. 引言&#xff1a;当传统艺术遇见分布式技术 春节贴春联是中国人延续千年的文化传统&#xff0c;而如今&#xff0c;借助AI技术和鸿蒙系统的分布式能力&#xff0c;我们可以让这一传统焕发新的…...

Java调用动态库总崩溃?从SIGSEGV日志反向定位到C端ABI兼容性缺陷——一线故障复盘(含GDB+Java Core联合调试全流程)

第一章&#xff1a;Java调用动态库总崩溃&#xff1f;从SIGSEGV日志反向定位到C端ABI兼容性缺陷——一线故障复盘&#xff08;含GDBJava Core联合调试全流程&#xff09;某金融风控系统在JDK 17 Alpine Linux&#xff08;musl libc&#xff09;环境下频繁触发 JVM Crash&#…...

intv_ai_mk11企业应用案例:如何将intv_ai_mk11集成进内部知识库与客服预处理流程

intv_ai_mk11企业应用案例&#xff1a;如何将intv_ai_mk11集成进内部知识库与客服预处理流程 1. 企业面临的挑战与AI解决方案 在当今企业运营中&#xff0c;知识管理和客户服务是两大核心痛点。许多企业面临以下问题&#xff1a; 知识库利用率低&#xff1a;员工难以快速找到…...

经典算法实现:二分查找、全排列与子集生成

在算法学习中&#xff0c;二分查找、全排列、子集生成是非常基础且重要的内容。本文将结合 C 代码&#xff0c;详细讲解这三种经典算法的实现思路与核心逻辑&#xff0c;帮助大家理解算法的底层原理和代码落地方式。一、二分查找&#xff08;Binary Search&#xff09;二分查找…...

Visium HD空转实战:Space Ranger v4.0.1从安装到结果解读全流程

1. Visium HD与Space Ranger初探 第一次接触Visium HD技术时&#xff0c;我被它强大的空间转录组分析能力震撼到了。简单来说&#xff0c;这项技术能让我们在组织切片上精确到单个细胞的位置&#xff0c;同时获取它们的基因表达数据。想象一下&#xff0c;这就像给组织样本拍了…...

避坑指南:在Windows/Mac上从零部署Grounding DINO和SAM的完整流程(含模型下载、环境配置)

避坑指南&#xff1a;在Windows/Mac上从零部署Grounding DINO和SAM的完整流程 部署多模态AI模型时&#xff0c;90%的失败发生在环境配置阶段。本文将手把手带你避开所有常见陷阱&#xff0c;从模型下载到最终运行&#xff0c;提供双系统兼容的解决方案。不同于常规教程&#xf…...