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

server/2025/2/22 16:19:06/

在 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/server/169410.html

相关文章

VMware新建虚拟机

看看自己的电脑是什么内核,有几个处理器 再分配给虚拟机 镜像文件需要自己安装下载地方https://mirrors.aliyun.com/centos/?spma2c6h.13651104.d-2001.8.3fb1320cuI1jeS 然后就出现了 然后开启虚拟机,等待 等待之后如下,选择语言 等待一段时…

【前端】如何安装配置WebStorm软件?

文章目录 前言一、前端开发工具WebStorm和VS Code对比二、官网下载三、安装1、开始安装2、选择安装路径3、安装选项4、选择开始菜单文件夹5、安装成功 四、启动WebStorm五、登录授权六、开始使用 前言 WebStorm 是一款由 JetBrains 公司开发的专业集成开发环境(IDE…

【Scrapy】Scrapy教程2——工作原理

文章目录 数据流组件引擎Engine调度器Scheduler下载器Downloader爬虫Spiders项目管道Item Pipeline下载器中间件Downloader Middlewares爬虫中间件Spider Middlewares 在学习Scrapy前,我们需要先了解其架构和工作原理,这样才能很好的去使用Scrapy。 Scra…

Unity中NavMesh的使用 及其 导出给java服务端进行寻路

1.先添加 AI Navigation组件 2.Windows-->AI-->Navigation(Obsolete) 这样子就可以看到烘焙按钮 3.将物体标记为行走和不可行走 4.添加一个Plane和一些球体,并把需要形成NavMesh的物体选择为静态 // 因为只能烘焙静态的 之后可以看出烘焙后,看着被…

玄机——第二章 日志分析-apache日志分析

玄机——第二章 日志分析-apache日志分析 目录 玄机——第二章 日志分析-apache日志分析一、提交当天访问次数最多的IP,即黑客IP二、黑客使用的浏览器指纹是什么,提交指纹的md5三、查看包含index.php页面被访问的次数,提交次数四、查看黑客IP…

Qt QListWidget 总结

Qt QListWidget 总结 1. 概述 QListWidget 是 Qt 中用于显示和管理列表项的控件,继承自 QListView,但提供更简单的项(Item-Based)接口。适用于简单列表场景(如文件列表、选项菜单),支持文本、…

微信小程序 - 网络请求基础路径集中管理(基础路径集中管理策略、动态切换基础路径)

一、基础路径集中管理 在微信小程序项目开发中,经常会将请求的基础路径集中管理 这样可以避免在多个页面中重复定义,同时也方便后续维护与修改 二、基础路径集中管理策略 1、使用全局变量 微信小程序提供了 App 对象,可以在 app.js 中定义…

MongoDB:listDatabases failed : not master and slaveOk=false

个人博客地址:MongoDB:listDatabases failed : not master and slaveOkfalse | 一张假钞的真实世界 异常描述 如果在MongoDB的SECONDARY上查询数据时会报如下错误信息: > show databases; 2018-09-20T17:40:55.3770800 E QUERY [thread…