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% 的折扣。

关于本站

搬瓦工VPS中文网(bwgvps.com)是非官方中文信息站,整理搬瓦工的方案、优惠和教程。我们不销售主机,不提供技术服务。

新手必读
搬瓦工优惠码

NODESEEK2026(优惠 6.77%)

购买时填入即可抵扣。