Docker 多阶段构建:优化镜像大小

ops/2025/2/22 3:02:40/

在 Docker 中,构建镜像时,我们通常会将应用及其所有依赖打包到镜像中。然而,随着时间的推移,镜像的大小会随着依赖项和构建工具的增加而变得越来越大,这不仅增加了存储成本,还会降低容器启动速度。多阶段构建(Multi-stage Builds) 是一种优化 Docker 镜像大小的技术,它通过分阶段的方式来创建镜像,避免了不必要的构建工具和依赖项进入最终镜像。

本文将深入探讨 Docker 多阶段构建 的概念、用法以及如何优化镜像大小。


1. 什么是 Docker 多阶段构建?

1.1 多阶段构建的基本概念

Docker 多阶段构建 是一种在 Dockerfile 中使用多个 FROM 指令的构建方式。每个 FROM 指令都定义了一个新的构建阶段,前一个阶段的输出可以传递给下一个阶段。这样,我们可以在不同的阶段中使用不同的基础镜像和依赖,最终将构建和运行环境分开,确保最终镜像中只包含必要的文件和依赖。

多阶段构建的流程
  1. 阶段 1:使用包含所有构建工具和依赖的镜像(例如:node:alpinemaven)进行应用的构建。
  2. 阶段 2:从前一个阶段复制构建结果,只包含应用程序和运行时依赖,使用更小的基础镜像(例如:nginx:alpineopenjdk:alpine)构建最终镜像。

通过这种方式,最终镜像只包含运行应用所需的文件,而不会包含构建过程中不必要的依赖和工具,显著减少镜像的大小。


2. 为什么使用多阶段构建?

2.1 优化镜像大小

多阶段构建的最大优点就是 减小镜像大小。在传统的单阶段构建中,构建工具(如编译器、测试工具等)通常会被打包到最终的镜像中。而多阶段构建则将构建过程与运行时环境隔离开,避免将不必要的构建工具包含在最终镜像中。

2.2 提高构建效率

通过将构建和运行环境分离,构建过程可以更为高效,并且可以根据需求选择合适的基础镜像。

2.3 更好的安全性

在多阶段构建中,最终的镜像仅包含应用和运行时所需的最小环境,从而减少了攻击面,提升了镜像的安全性。


3. 多阶段构建示例

3.1 构建一个 Java 应用的多阶段构建

假设我们有一个简单的 Java 项目,使用 Maven 进行构建,并最终生成一个 JAR 文件。我们可以通过多阶段构建,先在一个包含 Maven 的镜像中构建应用,然后在一个轻量级的镜像中运行应用。

示例 Dockerfile(多阶段构建)
dockerfile"># 阶段 1:构建阶段
FROM maven:3.8.1-openjdk-11-slim AS build# 设置工作目录
WORKDIR /app# 将本地代码复制到容器中
COPY . .# 使用 Maven 构建应用
RUN mvn clean package -DskipTests# 阶段 2:运行阶段
FROM openjdk:11-jre-slim# 设置工作目录
WORKDIR /app# 从构建阶段复制构建好的 JAR 文件
COPY --from=build /app/target/my-app.jar /app/my-app.jar# 运行应用
CMD ["java", "-jar", "/app/my-app.jar"]
解析
  1. 构建阶段

    • 使用 maven:3.8.1-openjdk-11-slim 镜像,它包含了 Maven 和 JDK,用于构建应用。
    • 将项目的源代码复制到 /app 目录,并通过 mvn clean package 命令构建应用。
  2. 运行阶段

    • 使用更小的 openjdk:11-jre-slim 镜像,它只包含 Java 运行时环境(JRE),适合生产环境运行应用。
    • 从构建阶段复制生成的 JAR 文件到运行阶段的 /app 目录。
    • 使用 java -jar 启动应用。

3.2 构建一个 Node.js 应用的多阶段构建

假设你有一个 Node.js 应用,并希望通过 Docker 构建和运行它。在多阶段构建中,我们可以先在一个包含 Node.js 的镜像中进行应用的构建和依赖安装,然后将构建的产物传递到一个轻量级的镜像中运行。

示例 Dockerfile(Node.js 应用)
dockerfile"># 阶段 1:构建阶段
FROM node:16 AS build# 设置工作目录
WORKDIR /app# 复制 package.json 和 package-lock.json
COPY package*.json ./# 安装应用依赖
RUN npm install# 复制其他源代码
COPY . .# 构建生产版本
RUN npm run build# 阶段 2:运行阶段
FROM nginx:alpine# 从构建阶段复制静态文件到 Nginx 的文件夹
COPY --from=build /app/dist /usr/share/nginx/html# 暴露 80 端口
EXPOSE 80# 启动 Nginx
CMD ["nginx", "-g", "daemon off;"]
解析
  1. 构建阶段

    • 使用包含 Node.js 的镜像 node:16 来构建应用。
    • 复制 package.jsonpackage-lock.json 文件,执行 npm install 安装依赖。
    • 复制项目的源代码并执行 npm run build 来构建生产版本。
  2. 运行阶段

    • 使用更小的 nginx:alpine 镜像,只包含 Nginx 服务器。
    • 从构建阶段复制生成的静态文件到 Nginx 的文件夹中。
    • 使用 Nginx 提供服务,暴露 80 端口。

4. Docker 多阶段构建优化技巧

4.1 减少构建镜像的层数

每个 RUNCOPY 等指令都会生成一个新的镜像层。为了减少镜像的大小,可以将多个命令合并为一个 RUN 指令,减少镜像的层数。

dockerfile">RUN apt-get update && \apt-get install -y python3 python3-pip && \rm -rf /var/lib/apt/lists/*

4.2 使用小型基础镜像

选择合适的基础镜像可以减少构建后的镜像体积。例如,使用 alpine 镜像,它比传统的 Ubuntu 镜像要小得多:

dockerfile">FROM node:16-alpine

4.3 清理临时文件

在构建过程中,许多临时文件(如安装包缓存)可能会增加镜像的体积。在构建镜像时,应清理这些不必要的文件。

dockerfile">RUN apt-get update && \apt-get install -y python3 python3-pip && \rm -rf /var/lib/apt/lists/*  # 清理 APT 缓存

4.4 只复制需要的文件

避免将不必要的文件(如测试文件、日志文件、编译工具等)复制到镜像中。可以使用 .dockerignore 文件来排除不需要的文件。

.dockerignore 示例

node_modules
*.log
.git

5. 总结

5.1 多阶段构建的优点

  • 减少镜像体积:多阶段构建只将运行时必需的文件复制到最终镜像,避免了不必要的构建工具和依赖。
  • 提高安全性:最终镜像只包含必要的运行环境,减少了潜在的安全漏洞。
  • 优化构建效率:分阶段构建可以选择合适的基础镜像,使得每个阶段只包含必要的内容,提升构建效率。

5.2 多阶段构建的最佳实践

  • 合并多个 RUN 指令,减少镜像层数。
  • 使用轻量级的基础镜像(如 alpine)。
  • 清理构建过程中的临时文件和缓存。
  • 使用 .dockerignore 排除不必要的文件。
  • 只将生产环境必需的文件复制到最终镜像中。

通过掌握 多阶段构建 技术,你可以创建更加精简、安全和高效的 Docker 镜像,提升容器

化应用的部署和运行效率! 🚀


http://www.ppmy.cn/ops/160404.html

相关文章

nlp|微调大语言模型初探索(3),qlora微调deepseek记录

前言 上篇文章记录了使用lora微调llama-1b,微调成功,但是微调llama-8b显存爆炸,这次尝试使用qlora来尝试微调参数体量更大的大语言模型,看看64G显存的极限在哪里。 1.Why QLora? QLoRA 在模型加载阶段通过 4-bit 量化大幅减少了模型权重的显存占用。QLoRA 通过 反量化到 …

14、《SpringBoot+MyBatis集成(2)——进阶配置XML与注解的灵活运用》

SpringBootMyBatis集成进阶配置 - XML与注解的灵活运用 前言 在Spring Boot与MyBatis的集成开发中,开发者常面临XML映射文件与注解两种SQL定义方式的选择,以及复杂场景下的动态SQL、多数据源等进阶需求。本文将从核心配置的灵活性出发,对比X…

一周学会Flask3 Python Web开发-response响应格式

锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 在HTTP响应中,数据可以通过多种格式传输。大多数情况下,我们会使用HTML格式,这也是Flask中…

< OS 有关 > Ubuntu 24 SSH 服务器更换端口 in jp/us VPSs

原因: 两台 VPS 的 ssh 端口一直被密码重试, us 这台已经封了 632, jp 这台两周前清过一次 sqlite3 数据,现在赞到 1008 Fail2Ban 是使用 sqlite3 来记录,数据量大后,硬盘的 I/O 会飙升,我有写过一个 app…

IntelliJ IDEA 插件推荐篇 - 2025年

IntelliJ IDEA 开发插件推荐 IntelliJ IDEA 是许多开发者的首选集成开发环境(IDE),其强大的功能和灵活的插件生态系统使其在 Java、Kotlin 等语言开发中备受青睐。通过安装合适的插件,你可以进一步提升开发效率、优化工作流程并增…

Golang深度学习

前言 在2009年,Google公司发布了一种新的编程语言,名为Go(或称为Golang),旨在提高编程效率、简化并发编程,并提供强大的标准库支持。Go语言的设计者们希望通过Go语言能够解决软件开发中的一些长期存在的问…

vue中table行根据某一属性显示不同颜色

需求&#xff1a;当借阅图书到期未归还时&#xff0c;这一行数据显示成红色&#xff0c;以起到警示提醒作用 1.dom中在el-table中添加属性 cellStyle“cellStyle” <TableView:row-class-name"rowClassName":isPaginationShow"true":openRightMenu"…

网页版的俄罗斯方块

1、新建一个txt文件 2、打开后将代码复制进去保存 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>俄…