😎 Node.js 应用多阶段构建 Dockerfile 详解

作者:你的人类朋友日期:2025/11/12

前言

🍃 你好啊,我是你的人类朋友!✨

本文主要来一起阅读一个高效的 Node.js 应用 Dockerfile。

在开始分析这个 Dockerfile 之前,先问大家一个问题:为什么这个 Dockerfile 要分两个阶段来构建,而不是直接复制所有文件然后安装依赖? 读完本文后,你就能找到答案!

😎 小贴士:如果你不懂啥是两段构建,问题不大,后面有解释,可以继续看。

下面展示的是一个用于部署 Node.js 应用的 Dockerfile,让我们先看看完整代码:

1FROM node:18-alpine AS builder
2
3WORKDIR /app
4
5# 只复制 package.json
6COPY package.json ./
7
8# 安装依赖
9RUN npm install
10
11# 运行时阶段
12FROM node:18-alpine
13
14WORKDIR /app
15
16# 从构建阶段复制 node_modules
17COPY --from=builder /app/node_modules ./node_modules
18
19# 复制源代码:注意,这边默认小伙伴们配置了 .dockerignore 文件,所以这一步不会复制 node_modules 目录。建议大家都补上 .dockerignore 文件,好处多多!
20COPY . .
21
22EXPOSE 3000
23
24CMD ["node", "index.js"]
25

如果你是个新手,这个时候有点头晕是正常的,莫慌,让我们进入正文!😁

正文

🤔 啥是单段构建与多段构建?

单段构建:整个应用在一个 Docker 镜像中完成构建和运行,适合简单应用。

多段构建:将构建过程分为多个阶段,每个阶段负责不同的任务,最终只将必要的文件复制到最终镜像中。这样做可以减小镜像大小,提高安全性。

看不懂?问题不大,下面使用 dockerfile 来作比较:

✍️ 单段构建示例

1FROM node:18-alpine
2WORKDIR /app
3COPY . .
4RUN npm install
5EXPOSE 3000
6CMD ["node", "index.js"]
7

我们来总结一下,他有什么特征?

  • 整个 Dockerfile 只有一个 FROM 指令
  • 所有操作(安装依赖、编译代码、运行应用)都在同一个镜像中完成
  • 最终生成的镜像包含了构建过程中的所有文件和工具

还看不懂的话,直接记住:单段构建:只有 1 个 FROM

✍️ 多段构建示例

1# ❗孩子们,我是第一个FROM!!!
2FROM node:18-alpine AS builder
3
4WORKDIR /app
5
6# 只复制 package.json
7COPY package.json ./
8
9# 安装依赖
10RUN npm install
11
12# ❗孩子们,我是第二个FROM!!
13# 运行时阶段
14FROM node:18-alpine
15
16WORKDIR /app
17
18# 从构建阶段复制 node_modules
19COPY --from=builder /app/node_modules ./node_modules
20
21# 复制源代码
22COPY . .
23
24EXPOSE 3000
25
26CMD ["node", "index.js"]
27

我们来总结一下,他有什么特征?

  • 整个 Dockerfile 有两个 FROM 指令
  • 所有操作(安装依赖、编译代码、运行应用)都在不同的镜像中完成
  • 最终生成的镜像只包含运行时必要的文件和工具

还看不懂的话,直接记住:多段构建:有 2 个 FROM

套公式做题就是快!bro 😏

逐行解读 Dockerfile

我们现在知道啥是单段构建与多段构建

下面就来详细康康这个所谓的多段构建,其每一个阶段都在做啥!【当然,其实每一步这边都会解释!】

第一阶段:构建阶段

1FROM node:18-alpine AS builder
2
  • 使用 Node.js 18 的 Alpine Linux 版本作为基础镜像
  • Alpine 版本非常轻量,适合生产环境
  • AS builder 给这个阶段命名为 "builder",方便后续引用
1WORKDIR /app
2
  • 设置工作目录为 /app,后续命令都在这个目录下执行。

补充知识:啥是 WORKDIR(工作目录)?

我不解释什么是工作目录,你只需要知道,设置 WORKDIR /app 就相当于你先进入容器的 /app 文件夹,之后的所有操作都在这个 /app 文件夹里完成。

其实很好理解,如果没有设置工作目录,操作会在根目录 / 进行,文件会放得乱七八糟的。

1COPY package.json ./
2
  • 只复制 package.json 文件到当前目录
  • 这是关键步骤:先只复制依赖定义文件
1RUN npm install
2
  • 安装项目依赖包
  • 由于只复制了 package.json,Docker 会缓存这一层,如果 package.json 没变化,后续构建会直接使用缓存。构建是啥意思?构建就是把你的源代码和配置打包成一个可以运行的 Docker 镜像的过程。

第二阶段:运行时阶段

1FROM node:18-alpine
2
  • 开始新的构建阶段,再次使用相同的基础镜像
  • 这样确保运行环境与构建环境一致
1WORKDIR /app
2
  • 同样设置工作目录为 /app
1COPY --from=builder /app/node_modules ./node_modules
2
  • --from=builder 从之前的构建阶段复制文件
  • 只复制已安装的 node_modules 目录到当前镜像
  • 这样避免了在最终镜像中包含构建工具和缓存文件
1COPY . .
2
  • 复制所有源代码到镜像中
  • 由于依赖已经安装好,这里不会触发依赖重新安装
1EXPOSE 3000
2
  • 声明容器运行时监听的端口是 3000
  • 这只是文档说明,实际端口映射需要在运行容器时设置
1CMD ["node", "index.js"]
2
  • 设置容器启动时执行的命令
  • 使用数组格式,直接运行 node index.js

🤔【疑问】 你可能会有的疑问:

看到这你可能一脸懵逼,第一个阶段安装依赖,第二个阶段用第一阶段的依赖,就能够节省资源?这就是所谓的多段构建吗?

✍️【回答】是的,这就是多段构建的精髓所在。

第一个阶段(builder)负责安装依赖,生成 node_modules 文件夹。第二个阶段只从这个阶段复制 node_modules 文件夹,而不复制其他构建相关的文件。

那么这样做为什么就能节省资源呢 😎:

最终镜像只包含运行需要的文件(你的代码+node_modules)

不包含 npm 安装过程中产生的缓存文件和临时文件

不包含构建工具和开发依赖

简单来说: 第一个阶段准备材料,第二个阶段只拿需要的材料来运行,把垃圾留在原地。

🤔【疑问】你老是说什么缓存文件、临时文件之类的,为啥我自己pnpm i或者npm i的时候没看到啥缓存文件、临时文件?

✍️【回答】当你运行 npm install 时,npm 其实会在后台下载包到缓存目录(通常在用户主目录的 .npm 文件夹中),然后从缓存复制到项目的 node_modules。虽然你在项目里看不到这些缓存文件,但它们确实存在于系统其他地方。

而多阶段构建的优势就是连这些隐藏的系统级缓存文件都不会带到最终镜像中,从而减小了镜像的大小,进而提高了镜像的加载速度。😎

最后

OK 了兄弟们,现在全体目光向我看齐,看我看我,我来总结下!

回到开头的问题:为什么要分两个阶段构建?

答案主要有三点:

  1. 减小镜像大小:最终镜像只包含运行所需的文件,不包含构建过程中的【中间文件】和【缓存】
  2. 提高构建速度:利用 Docker 缓存机制,当 package.json 不变时,直接使用缓存的依赖层,也就是 npm install 这一层。如果 package.json 改变了,才会重新安装依赖。
  3. 增强安全性:最终镜像不包含构建工具,减少了攻击面。哈意思?就是说,因为最终镜像不包含构建工具,所以就不能通过攻击工具来攻击应用,这方面算作了解吧!

这就是本文的全部内容了,祝你今天开心~

☀️


😎 Node.js 应用多阶段构建 Dockerfile 详解》 是转载文章,点击查看原文


相关推荐


测试自动化Replay:让数据库迁移测试回归真实场景的一把“利器”
我是杰尼2025/11/10

测试自动化Replay:让数据库迁移测试回归真实场景的一把“利器” 在当前数据库国产化的大趋势下,越来越多的企业开始从传统进口数据库向国产数据库进行替代。然而,真正影响迁移进度的往往不是数据迁移本身,而是 测试验证环节。 如果测试不能覆盖真实业务场景,迁移后的系统上线后很可能出现: 性能明显下降 并发冲突和慢查询频发 数据一致性异常难以定位 很多企业迁移项目延期 1~3 个月,根本原因都指向了同一个问题:测试不够真实。 那么,是否有办法把“生产环境正在发生的真实行为”,直接搬进测试环境中来复


C++死锁深度解析:从成因到预防与避免
oioihoii2025/11/8

第一部分:什么是死锁? 死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。若无外力干涉,这些线程都将无法向前推进。 一个经典的死锁场景被称为 “哲学家就餐问题” :五位哲学家围坐一桌,每两人之间有一支筷子。哲学家要么思考,要么就餐。就餐时需要同时拿起左右两边的筷子。如果所有哲学家同时拿起左边的筷子,那么他们都会永远等待右边的筷子被释放,从而陷入死锁。 第二部分:死锁产生的四个必要条件(Coffman条件) 这四个条件必须同时满足,死锁才会发生。因此,我们的所有策略都


Python编程实战 · 基础入门篇 | 字典(dict)
程序员爱钓鱼2025/11/6

在 Python 的世界里,除了列表(list)和元组(tuple)这样的序列类型,还有一种更灵活的数据结构——字典(dict)。 如果说列表像“有序的货架”,那么字典就像“带标签的抽屉”:每个数据都有一个唯一的“名字”,叫做 键(key)。 字典是 Python 最常用、最强大的数据结构之一。它能高效地存储和查找数据,被广泛用于配置、数据映射、缓存、对象建模等场景。 一 什么是字典 字典(Dictionary)是一种 键值对(key-value) 的数据结构。 它以 键(key) 定位对应


(2)100天python从入门到拿捏
硅基工坊2025/11/1

Python 的主要运用领域 Python 是一种多用途的编程语言,广泛应用于许多不同的领域。以下是 Python 的一些主要运用领域,并附上使用 Python 的公司及其产品示例。 文章目录 Python 的主要运用领域1. Web 开发2. 数据科学与分析3. 机器学习与人工智能4. 自动化与脚本编程5. 爬虫与数据抓取6. 金融与量化分析7. 科学计算与工程8. 游戏开发9. 桌面应用开发10. 嵌入式与物联网(IoT) 1. Web 开发 领域简介:Pytho


Redis(93)Redis的数据加密机制是什么?
Victor3562025/10/30

Redis本身并没有内置的数据加密机制,不过可以通过一些外部技术和配置来确保数据在传输和存储时的安全性。以下是一些常见的做法: 传输层安全性(TLS/SSL): 使用TLS/SSL加密客户端和Redis服务器之间的通信。 磁盘加密: 使用操作系统级别的磁盘加密来加密存储在磁盘上的数据。 应用层加密: 在将数据存储到Redis之前,在应用程序中对数据进行加密。 1. 传输层安全性(TLS/SSL) Redis 6.0 及以上版本支持TLS/SSL。以下是如何配置TLS/SSL: 配


Redis(86)Redis缓存的命中率如何提高?
Victor3562025/10/27

提高Redis缓存的命中率是确保系统性能和稳定性的关键。以下是一些提高Redis缓存命中率的策略,以及相应的代码示例: 1. 合理设置缓存过期时间 设置合适的缓存过期时间可以防止缓存数据过时,同时减少缓存击穿的风险。根据数据的更新频率和访问模式,选择合适的过期时间。 示例代码: import redis.clients.jedis.Jedis; public class CacheExpirationExample { private Jedis jedis; public


前端仔的一次运维实践
yuanyxh2025/10/25

最近有反馈官网访问异常卡顿,查看 Portainer 面板发现官网后台的后端服务所依赖的 jdk、mysql 等 docker 容器在不断的奔溃重建,查询发现奔溃日志,交由后端排查。 后端反馈服务器磁盘不足导致 mysql 无法启动,后端访问连不上数据库导致的。 查询磁盘占用,发现官网的 nginx 日志文件占用近 20GB,删除后官网后端访问正常运行。 日志切分与压缩 为了避免日志持续增长占用磁盘空间,需要对日志进行管理,这里使用 linux 系统自带的 logrotate 日志管理工具实现自


云开发Copilot实战:零代码打造智能体小程序指南
腾讯云开发CloudBase2025/10/22

云开发Copilot借助AI技术,实现小程序和Web应用的低代码生成与优化,大幅降低开发门槛,提升效率。无需编码,用户可通过自然语言描述需求,快速创建并发布应用,适合初创团队和快速迭代场景。 简述云开发的功能及优势 你是否曾经设想过,有一天只需简单描述需求,就能生成一个完整的小程序或 Web 应用,甚至连一行代码都无需编写?在如今快速发展的技术浪潮中,低代码开发正在重新定义开发效率,而腾讯云的云开发 Copilot 正是其中的佼佼者。借助 AI 技术,它不仅能够迅速生成应用和页面,还能优化样式、


用 Python 揭秘 IP 地址背后的地理位置和信息
烛阴2025/10/21

准备工作:安装必备工具 首先,请确保你的Python环境中安装了requests库。 pip install requests 第一步:查询自己的公网 IP 信息 import requests import json # 向ipinfo.io发送请求,不带任何IP地址,它会默认查询你自己的IP url = "https://ipinfo.io/json" try: response = requests.get(url) response.raise_for_status


使用AI编程工具的“经济学”:成本控制与性能优化策略
rengang662025/10/20

最近跟几个朋友聊天,发现大家都在用AI编程工具,比如Cursor、Claude Code、Codex等。聊到兴头上,我问了一个“煞风景”的问题:“兄弟们,这月API账单看了吗?” 空气突然安静。 没错,AI编程工具确实香,写代码、改Bug、写文档,效率起飞。但“免费的午餐”总是短暂的,当我们真正把它用在项目里,或者用量一大起来,那账单就跟坐了火箭一样往上蹿。今天,我就想跟大家聊聊,作为一个“精打细算”的程序员,我们怎么在享受AI便利的同时,把成本控制得死死的,实现“降本增效”的终极目标。 这篇文

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2025 聚合阅读