代码改变世界

实用指南:了解Docker的多阶段构建(Multi-stage Build)

2025-11-25 16:12  tlnshuju  阅读(24)  评论(0)    收藏  举报

概述

你有没有发现,自己构建的 Docker 镜像动不动就几百 MB,甚至上 GB?

而别人分享的镜像,功能一样,却只有几十 MB?

问题很可能出在:你把“构建环境”也打包进了最终镜像

比如:

  • 用 Node.js 构建前端,却把 node_modulesnpm 工具留在了生产镜像
  • 用 Maven 编译 Java,却把整个 JDK 打包进去
  • 用 Python 编译依赖,却包含了 pip、编译器等构建工具

这些在运行时根本不需要的文件,白白占用了大量空间,还带来了安全风险。

解决方案就是:多阶段构建(Multi-stage Build)

什么是多阶段构建

多阶段构建允许你在一个 Dockerfile 中定义多个构建阶段,每个阶段可以基于不同的基础镜像。

你可以:

  • 第一阶段:使用“胖镜像”完成编译、打包
  • 第二阶段:使用“瘦镜像”只复制必要的产物(如可执行文件、静态文件)

最终镜像只包含运行所需的内容,不包含构建工具和中间文件。

用多阶段构建优化 Node.js 应用

场景
你有一个 React 前端项目,需要:

  1. 安装依赖(npm install
  2. 打包构建(npm run build
  3. 用 Nginx 托管静态文件

普通构建(镜像臃肿)

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm ci && npm run build
FROM nginx:alpine
COPY --from=0 /app/build /usr/share/nginx/html
EXPOSE 80

多阶段构建(镜像精简)

# 阶段 1:构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 阶段 2:运行阶段
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80

效果对比

方式镜像大小是否包含 Node.js?
普通构建~300MB是(不需要)
多阶段构建~20MB

体积减少 93%

多阶段构建如何工作

  1. 第一阶段builder):

    • 使用 node:18-alpine,安装所有依赖并执行 npm run build
    • 生成 build/ 目录(静态文件)
  2. 第二阶段(运行):

    • 使用极轻的 nginx:alpine(仅 ~16MB)
    • 只复制 builder 阶段生成的 build/ 文件
    • 最终镜像不包含 Node.js、npm、源码等

关键语法:COPY --from=builder ... ,表示从名为 builder 的阶段复制文件

更多场景

  1. Java(Spring Boot)项目
# 阶段 1:编译
FROM maven:3.8-openjdk-17 AS builder
COPY pom.xml .
COPY src ./src
RUN mvn package -DskipTests
# 阶段 2:运行
FROM eclipse-temurin:17-jre-alpine
COPY --from=builder /target/myapp.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

优势:最终镜像使用 JRE 而非 JDK,体积更小

  1. Golang 应用
# 阶段 1:编译
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o main .
# 阶段 2:运行(使用极小的 distroless 镜像)
FROM gcr.io/distroless/static-debian11
COPY --from=builder /app/main .
CMD ["/main"]

优势:最终镜像不含 shell、包管理器,极致轻量且安全

多阶段构建的好处

优势说明
镜像体积大幅减小只包含运行时所需文件
构建更高效可复用构建阶段
安全性更高不包含编译器、shell 等攻击面
职责分离构建与运行环境解耦

最佳实践

  1. 为阶段命名:使用 AS <name>,便于引用
  2. 选择合适的运行基础镜像
    • 前端:nginx:alpine
    • Java:eclipse-temurin:17-jre-alpine
    • Go:distrolessscratch
  3. 避免在最终阶段安装不必要的包
  4. 在 CI/CD 中使用多阶段构建,确保一致性

常见问题

  1. 多阶段构建会影响构建速度吗?
    不会。虽然阶段多,但 Docker 会缓存每一层,构建速度通常更快

  2. 如何只构建某个阶段?

docker build --target builder -t myapp:build .

这在调试时非常有用。

总结

传统方式多阶段构建
一个镜像搞定所有分阶段,各司其职
镜像臃肿镜像精简
安全性低安全性高

多阶段构建是编写高质量 Dockerfile 的必备技能,尤其适合生产环境