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 使用了两个阶段:
- 构建阶段:使用包含编译应用程序所需构建工具的基础镜像。包括安装构建工具、复制源代码和执行构建命令。
- 最终阶段:使用适合运行应用程序的较小基础镜像。从构建阶段复制编译好的工件(例如 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 了。
在包含所有其他文件夹和文件(如 src、pom.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-builder 的 Docker 镜像。
注意
在你的多阶段 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 应用程序使用多阶段构建额外资源 在传统构建中,所有构建指令都在一个构建容器中顺序执行:下载依赖项、编译代码、打包应用程序。所有这些层最终都在你的最终镜像中。这种方法虽然可行…...
Linux应急响应——知攻善防应急靶场-Linux(1)
文章目录 查看history历史指令查看开机自启动项异常连接和端口异常进程定时任务异常服务日志分析账户排查总结 靶场出处是知攻善防 Linux应急响应靶机 1 前景需要: 小王急匆匆地找到小张,小王说"李哥,我dev服务器被黑了",快救救我&…...
基于CDMA的多用户水下无线光通信(1)——背景介绍
研究生期间做多用户水下无线光通信(Underwater Optical Wireless Communication,UOWC),写几篇博客分享一下学的内容。导师给了大方向,让我用直接序列码分多址(Direct Sequence Code Division Multiple Acce…...
基于springboot websocket和okhttp实现消息中转
1、业务介绍 消息源服务的消息不能直接推给用户侧,用户与中间服务建立websocket连接,中间服务再与源服务建立websocket连接,源服务的消息推给中间服务,中间服务再将消息推送给用户。流程如下图: 此例中我们定义中间服…...
@PostConstruct 注解的方法用于资源的初始化
PostConstruct 是 Java EE 5 引入的一个注解,主要用于依赖注入完成之后,需要执行的方法上。这个注解的方法会在依赖注入完成后自动被容器(如 EJB 容器或 Spring 容器)调用,并且只会被调用一次。 PostConstruct 注解的…...
(一)SvelteKit教程:hello world
(一)SvelteKit教程:hello world sveltekit 的官方教程,在这里:Creating a project • Docs • SvelteKitCreating a project • Docs • SvelteKit 我们可以按照如下的步骤来创建一个项目: npm create s…...
华为Atlas NPU ffmpeg 编译安装
处理器:鲲鹏920 NPU:昇腾 310P3 操作系统:Kylin Linux Advanced Server V10 CANN:Ascend-cann-toolkit_8.0.RC1_linux-aarch64.run FFmpeg: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. 功能和工具: Sass:提供了更多的功能和内置方法,如条件语句、循环、数学函数等。Sass 也支持更复杂的操作和逻辑构建。 Less:功能也很强大,但相比之下,Sass 在功能上更为丰富和成熟。 2、编译环境&…...
力扣-2663
题目 如果一个字符串满足以下条件,则称其为 美丽字符串 : 它由英语小写字母表的前 k 个字母组成。它不包含任何长度为 2 或更长的回文子字符串。 给你一个长度为 n 的美丽字符串 s 和一个正整数 k 。 请你找出并返回一个长度为 n 的美丽字符串&#…...
CausalMMM:基于因果结构学习的营销组合建模
1. 摘要 在线广告中,营销组合建模(Marketing Mix Modeling,MMM) 被用于预测广告商家的总商品交易量(GMV),并帮助决策者调整各种广告渠道的预算分配。传统的基于回归技术的MMM方法在复杂营销场景…...
编译 CUDA 程序的基本知识和步骤
基本工具 NVCC(NVIDIA CUDA Compiler): nvcc 是 NVIDIA 提供的 CUDA 编译器,用于将 CUDA 源代码(.cu 文件)编译成可执行文件或库。它可以处理 CUDA 和主机代码(例如 C)的混合编译。nvcc 调用底层…...
[SAP ABAP] 排序内表数据
语法格式 整表排序 SORT <itab> [ASCENDING|DESCENDING]. 按指定字段排序 SORT <itab> BY f1 [ASCENDING|DESCENDING] f2 [ASCENDING|DESCENDING] ... fn [ASCENDING|DESCENDING].<itab>:代表内表 不指定排序方式则默认升序排序 示例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、对操作建模 一个活动图从本质上说是一个流程图,展现从活动到活动的控制流 活动图…...
【web2】jquary,bootstrap,vue
文章目录 1.jquary:选择器1.1 jquery框架引入:$("mydiv") 当成id选择器1.2 jquery版本/对象:$(js对象) -> jquery对象1.3 jquery的页面加载事件:$ 想象成 window.onload 1.4 jquery的基本选择器:$()里内容…...
独角兽品牌獭崎酱酒:高性价比的酱香之选
在酱香型白酒领域中,獭崎酱酒以其独特的品牌定位和高性价比迅速崛起,成为市场上备受关注的独角兽品牌。作为贵州茅台镇的一款新秀酱香酒,獭崎酱酒不仅传承了百年酿造工艺,还以创新的商业模式和亲民的价格赢得了广大消费者的青睐。…...
java打印菱形和空心菱形
java打印菱形 菱形分上下两个部分。其中上部分同打印金字塔;下部分循环部分i是递减 (ps:菱形层数只能为奇数) import java.util.Scanner;public class Lingxing{public static void main(String[] args) {Scanner myScanner new Scanner(S…...
Day10 —— 大数据技术之Scala
Scala编程入门 Scala的概述什么是Scala?Scala的重要特点Scala的使用场景 Scala的安装Scala基础Scala总结 Scala的概述 什么是Scala? Scala是一种将面向对象和函数式编程结合在一起的高级语言,旨在以简洁、优雅和类型安全的方式表达通用编程…...
Linux应用系统快速部署:docker快速部署linux应用程序
目录 一、背景 (一)引入docker的起因 (二)docker介绍 (三)Docker部署的优势 1、轻量级和可移植性 2、快速部署和扩展 3、一致性 4、版本控制 5、安全性 6、资源隔离 7、简化团队协作 8、多容器…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
