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、多容器…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...