Skip to content

多阶段构建

优化镜像体积,实现构建与运行环境分离

为什么需要多阶段构建

单阶段构建的问题:

dockerfile
FROM node:18
WORKDIR /app
COPY . .
RUN npm ci && npm run build
# 构建产物 + 源代码 + node_modules 都留在镜像中
CMD ["npm", "start"]
# 最终镜像可能 1GB+

多阶段构建:

dockerfile
# 阶段1:构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 阶段2:运行(只复制构建产物)
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules  # 如果需要
CMD ["node", "dist/server.js"]
# 最终镜像可能 100MB-

多阶段构建模式

模式1:分离构建与运行环境

dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 运行阶段
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
CMD ["node", "dist/server.js"]

模式2:Webpack/Vite 构建产物分离

dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build

# 运行阶段(极简)
FROM node:18-alpine AS runner
WORKDIR /app
# 复制构建产物
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/public ./public
# 复制生产依赖
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
CMD ["node", "dist/server.js"]

模式3:Go/Java 多阶段构建

dockerfile
# 构建阶段
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

# 运行阶段
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /app
COPY --from=builder /app/main .
CMD ["./main"]

模式4:前端 SPA 应用

dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build

# 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;"]

构建参数传递

dockerfile
# 构建时传递参数
ARG NODE_VERSION=18-alpine
FROM node:${NODE_VERSION}

ARG BUILD_VERSION
RUN echo "Build version: ${BUILD_VERSION}"

# 构建命令
docker build --build-arg BUILD_VERSION=1.0.0 -t myapp .

优化技巧

技巧1:合并 RUN 指令

dockerfile
# ❌ 错误:多层,缓存利用率低
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
RUN rm -rf /var/lib/apt/lists/*

# ✅ 正确:合并减少层数
RUN apt-get update && \
    apt-get install -y curl vim && \
    rm -rf /var/lib/apt/lists/*

技巧2:.dockerignore 优化

txt
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.*
*.log
.DS_Store
coverage
.nyc_output
dist

技巧3:选择合适的基础镜像

镜像大小适用场景
ubuntu~80MB需要完整系统
debian~75MB稳定兼容
alpine~5MB最小化(推荐)
node:18-alpine~180MBNode.js 应用

技巧4:按需复制

dockerfile
# ❌ 复制全部
COPY . .

# ✅ 只复制必要文件
COPY package*.json ./
COPY src/ ./src/
COPY public/ ./public/

面试高频题

Q1: 什么是多阶段构建?有什么好处?

答案: 多阶段构建允许在一个 Dockerfile 中使用多个 FROM 指令,每个阶段可以复制前一个阶段的文件。

好处:

  • 减小最终镜像体积(只包含运行所需文件)
  • 构建与运行环境分离
  • 提高安全性(源代码不进入生产镜像)

Q2: 如何优化 Dockerfile 减小镜像大小?

答案:

  1. 使用多阶段构建
  2. 选择轻量基础镜像(alpine)
  3. 合并 RUN 指令
  4. 合理使用 .dockerignore
  5. 按需 COPY 文件
  6. 清理不必要的缓存和文件

前端面试知识库