Docker 多阶段构建优化教程
在搬瓦工 VPS 上使用 Docker 时,镜像体积直接影响磁盘占用、拉取速度和部署效率。传统的单阶段构建方式会将编译工具、源代码和构建依赖全部打包到最终镜像中,导致镜像臃肿。Docker 多阶段构建(Multi-stage Build)通过在一个 Dockerfile 中使用多个 FROM 指令,将构建环境和运行环境分离,从而大幅减小最终镜像的体积。
一、为什么需要多阶段构建
以一个 Go 应用为例,传统单阶段构建:
# 单阶段构建 - 最终镜像约 800MB
FROM golang:1.22
WORKDIR /app
COPY . .
RUN go build -o myapp .
CMD ["./myapp"]
这个镜像包含了整个 Go 工具链、源代码和所有依赖,但运行时实际只需要编译后的二进制文件。多阶段构建可以将体积压缩到 10MB 以内。
二、多阶段构建基础
# 第一阶段:构建
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o myapp .
# 第二阶段:运行(最终镜像约 8MB)
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata
COPY --from=builder /app/myapp /usr/local/bin/myapp
USER nobody
ENTRYPOINT ["myapp"]
关键语法:
FROM ... AS builder:为构建阶段命名。COPY --from=builder:从构建阶段复制产物到运行阶段。- 最终镜像只包含运行所需的文件,不含编译工具。
三、Node.js 应用示例
# 阶段1:安装依赖
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --production
# 阶段2:构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# 阶段3:运行(最终镜像)
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json .
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]
三阶段构建将依赖安装、代码构建和最终运行环境完全分离,充分利用构建缓存。
四、Python 应用示例
# 阶段1:构建依赖(含编译工具)
FROM python:3.12-slim AS builder
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc libpq-dev && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# 阶段2:运行环境(不含编译工具)
FROM python:3.12-slim
RUN apt-get update && \
apt-get install -y --no-install-recommends libpq5 && \
rm -rf /var/lib/apt/lists/*
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
WORKDIR /app
COPY . .
RUN useradd -r appuser
USER appuser
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
五、Java 应用示例
# 阶段1:使用 Maven 构建
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B
# 阶段2:使用 JRE 运行
FROM eclipse-temurin:21-jre-alpine
COPY --from=builder /app/target/*.jar /app/app.jar
RUN addgroup -S spring && adduser -S spring -G spring
USER spring
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
构建阶段使用完整的 JDK 和 Maven,运行阶段只需轻量的 JRE,镜像大小从约 800MB 降至约 200MB。
六、前端应用示例
# 阶段1:构建前端资源
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# 阶段2:使用 Nginx 提供静态文件
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
最终镜像仅包含 Nginx 和构建产出的静态文件,体积约 30MB。
七、高级技巧
7.1 从外部镜像复制文件
# 直接从其他镜像复制文件
COPY --from=nginx:alpine /etc/nginx/nginx.conf /etc/nginx/
COPY --from=busybox:latest /bin/wget /usr/local/bin/
7.2 使用构建参数选择阶段
ARG BUILD_ENV=production
FROM node:20-alpine AS base
WORKDIR /app
COPY package.json package-lock.json ./
FROM base AS deps-production
RUN npm ci --production
FROM base AS deps-development
RUN npm ci
FROM deps-${BUILD_ENV} AS final
COPY . .
CMD ["node", "server.js"]
# 构建生产镜像
docker build --build-arg BUILD_ENV=production -t myapp:prod .
# 构建开发镜像
docker build --build-arg BUILD_ENV=development -t myapp:dev .
7.3 只构建特定阶段
# 只执行到 builder 阶段(用于调试)
docker build --target builder -t myapp:builder .
八、使用 Scratch 构建最小镜像
对于静态编译的语言(如 Go、Rust),可以使用空白的 scratch 镜像:
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o myapp .
# scratch 是一个空白镜像
FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]
最终镜像大小仅为二进制文件本身的大小,通常只有几 MB。
九、构建前后对比
# 查看镜像大小对比
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
# 典型优化效果:
# Go 应用:800MB → 8MB(减少 99%)
# Node.js:1.2GB → 150MB(减少 87%)
# Java:800MB → 200MB(减少 75%)
# 前端 SPA:1GB → 30MB(减少 97%)
总结
多阶段构建是优化 Docker 镜像体积最有效的方法之一,对搬瓦工 VPS 这种资源有限的环境尤为重要。结合 Dockerfile 最佳实践 和 BuildKit 高效构建,可以打造出极致精简的容器镜像。使用 Dive 工具可以直观地分析镜像层组成。选购搬瓦工 VPS 请访问 bwh81.net,购买时使用优惠码 NODESEEK2026 可享受 6.77% 的折扣。