欢迎来到晓黎团队博客

加载中...

Writing2026-10-20

Docker 多阶段构建:镜像体积从 2GB 到 200MB

多阶段构建不是银弹,但它能让生产镜像只包含运行时依赖。我们的 API 镜像从 2GB 降到 200MB。

喜欢 0阅读 68
Docker 多阶段构建:镜像体积从 2GB 到 200MB

Docker 多阶段构建:镜像体积从 2GB 到 200MB

示意图

一、大镜像的代价

第一次看到"一个 Node 服务镜像 2GB"不用惊讶——默认基础镜像带着完整系统,npm install 把 devDependencies 和编译缓存全留在层里,构建工具的中间产物没人清理。体积的代价很实际:每次部署多拉几分钟、安全扫描面更大、仓库存储翻倍。

多阶段构建(multi-stage build)是目前最干净的解法:构建环境和运行环境本来就是两件事,别让它们挤在同一个镜像里。

二、基本结构

dockerfile 复制代码
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS runtime
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/prod_modules ./node_modules
CMD ["node", "dist/server.js"]

关键在第二个 FROM:最终镜像只从第一阶段 COPY 需要的产物,编译缓存、构建工具、devDependencies 全部留在被丢弃的中间层里。

三、四个省体积的细节

1. 依赖装两遍。 第一阶段装全量依赖用于构建,再单独 npm ci --omit=dev 出一份生产依赖给运行镜像。比在一个 node_modules 里删删改改可靠。

2. 利用层缓存。 COPY package*.json 单独一层、npm ci 紧随其后、最后才 COPY . .——代码改动不会触发依赖重装,CI 构建时间能差出一个数量级。

3. 基础镜像对齐。 运行镜像用 alpine/slim/distroless 取决于原生依赖:纯 JS 服务可以 distroless,有 glibc 依赖就 slim。别为省 50MB 引入一个调试不了的运行时。

4. 构建产物检查。 加一层 RUN du -sh /app 或事后 docker history,体积回涨通常是有东西被意外 COPY 进来了——source map、测试 fixture、.git 目录是惯犯。

四、收益怎么算

我们一个真实服务:2.1GB → 190MB,拉取时间从 90 秒降到 8 秒,镜像扫描出的漏洞数下降一大截(大部分在构建工具链里)。构建时间反而略增(多装一遍依赖)——这是正确的取舍:构建时间花一次,拉取时间每次部署都花。

多阶段构建没有魔法,就是把"构建需要一切"和"运行只需最小集"这两个事实,写进 Dockerfile 的两段 FROM 里。


撰稿:邓湘雷

Docker 多阶段构建:镜像体积从 2GB 到 200MB
  • 发布于

    2026-10-20

  • 更新于

    2026-10-23

  • 类目

  • 作者

    晓黎团队

  • 版权协议

Designed and developed by 晓黎团队
本站已经持续运行 0 天
文章版权以页面声明为准