Docker 多阶段构建:把镜像从 1.2GB 压缩到 89MB
我们的 Node.js 应用镜像原来是 1.2GB,部署慢、存储贵。
用多阶段构建优化后,镜像只有 89MB,部署速度提升 10 倍。
问题背景
原来的 Dockerfile
DOCKERFILE
# ❌ 单阶段构建
FROM node:20
WORKDIR /app
# 复制所有文件
COPY . .
# 安装所有依赖(包括 devDependencies)
RUN npm install
# 构建
RUN npm run build
# 启动
CMD ["node", "dist/index.js"]问题:
- 镜像包含所有源代码
- 包含 devDependencies(TypeScript、ESLint 等)
- 包含构建工具(webpack、babel 等)
- 最终镜像 1.2GB
镜像大小分析
BASH
$ docker images myapp
REPOSITORY TAG IMAGE ID CREATED SIZE
myapp latest abc123 2 minutes ago 1.2GB
$ docker history myapp
IMAGE CREATED SIZE
abc123 2 min ago 450MB # node_modules
def456 2 min ago 380MB # build tools
ghi789 2 min ago 220MB # source code
jkl012 5 min ago 150MB # base image多阶段构建
优化后的 Dockerfile
DOCKERFILE
# ✅ 多阶段构建
# 阶段 1:构建阶段
FROM node:20-alpine AS builder
WORKDIR /app
# 只复制 package 文件(利用 Docker 缓存)
COPY package*.json ./
# 安装所有依赖(包括 devDependencies)
RUN npm ci
# 复制源代码
COPY . .
# 构建
RUN npm run build
# 阶段 2:生产阶段
FROM node:20-alpine AS production
WORKDIR /app
# 只复制 package 文件
COPY package*.json ./
# 只安装生产依赖
RUN npm ci --only=production && npm cache clean --force
# 从构建阶段复制构建产物
COPY --from=builder /app/dist ./dist
# 非 root 用户运行
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs /app
USER nodejs
# 启动
CMD ["node", "dist/index.js"]优化效果
BASH
$ docker images myapp
REPOSITORY TAG IMAGE ID CREATED SIZE
myapp latest xyz789 1 minute ago 89MB从 1.2GB 降到 89MB,减少 93%。
进阶优化
优化 1:使用 distroless 基础镜像
DOCKERFILE
# 阶段 1:构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 阶段 2:生产(使用 distroless)
FROM gcr.io/distroless/nodejs20-debian12 AS production
WORKDIR /app
# 复制 node_modules
COPY --from=builder /app/node_modules ./node_modules
# 复制构建产物
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
# 启动
CMD ["dist/index.js"]distroless 镜像不包含 shell、包管理器等,更安全。
优化 2:分层缓存优化
DOCKERFILE
# 阶段 1:依赖安装(独立缓存层)
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# 阶段 2:构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 阶段 3:生产
FROM node:20-alpine AS production
WORKDIR /app
# 复制生产依赖(缓存友好)
COPY --from=deps /app/node_modules ./node_modules
# 复制构建产物
COPY --from=builder /app/dist ./dist
COPY package.json ./
USER node
CMD ["node", "dist/index.js"]好处:
- 依赖不变时,deps 层被缓存
- 源代码变化只影响 builder 层
- 构建速度更快
优化 3:多架构支持
DOCKERFILE
# 使用 buildx 构建多架构镜像
# docker buildx build --platform linux/amd64,linux/arm64 -t myapp .
FROM --platform=$BUILDPLATFORM node:20-alpine AS builder
ARG TARGETPLATFORM
ARG BUILDPLATFORM
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# 根据目标平台构建
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
npm run build:arm64; \
else \
npm run build; \
fi
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./
USER node
CMD ["node", "dist/index.js"]Python 项目示例
优化前
DOCKERFILE
# ❌ 单阶段
FROM python:3.12
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]镜像大小:850MB
优化后
DOCKERFILE
# ✅ 多阶段
# 阶段 1:构建依赖
FROM python:3.12-slim AS builder
WORKDIR /app
# 安装构建工具
RUN apt-get update && apt-get install -y \
build-essential \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
# 安装依赖到指定目录
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# 阶段 2:生产
FROM python:3.12-slim AS production
WORKDIR /app
# 复制安装的依赖
COPY --from=builder /install /usr/local
# 只复制必要文件
COPY app.py .
COPY src/ ./src/
# 非 root 用户
RUN useradd -m -u 1001 appuser && chown -R appuser:appuser /app
USER appuser
CMD ["python", "app.py"]镜像大小:180MB
Go 项目示例
优化前
DOCKERFILE
# ❌ 包含完整 Go 工具链
FROM golang:1.22
WORKDIR /app
COPY . .
RUN go build -o main .
CMD ["./main"]镜像大小:1.1GB
优化后
DOCKERFILE
# ✅ 多阶段 + scratch
# 阶段 1:构建
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="-w -s" -o main .
# 阶段 2:最小镜像
FROM scratch AS production
# 复制 CA 证书(HTTPS 需要)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# 复制二进制
COPY --from=builder /app/main /main
# 复制配置
COPY --from=builder /app/config /config
# 非 root
USER 1001
ENTRYPOINT ["/main"]镜像大小:12MB
最佳实践
1. 使用 .dockerignore
CODE
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.*
*.md
.vscode
.idea
coverage
.nyc_output
dist
build2. 合理利用缓存
DOCKERFILE
# ✅ 好的缓存策略
COPY package*.json ./
RUN npm ci # 依赖不变时缓存
COPY tsconfig.json ./
COPY src/ ./src/
RUN npm run build # 源代码变化时重新构建
# ❌ 差的缓存策略
COPY . . # 任何文件变化都重新安装依赖
RUN npm install3. 安全实践
DOCKERFILE
# ✅ 安全实践
# 使用特定版本
FROM node:20.11.0-alpine3.19
# 非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
# 只复制必要文件
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"4. 多环境构建
DOCKERFILE
# 使用构建参数
ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV
# 根据环境安装依赖
RUN if [ "$NODE_ENV" = "development" ]; then \
npm install; \
else \
npm ci --only=production; \
fi性能对比
| 项目类型 | 优化前 | 优化后 | 减少 |
|---------|--------|--------|------|
| Node.js | 1.2GB | 89MB | 93% |
| Python | 850MB | 180MB | 79% |
| Go | 1.1GB | 12MB | 99% |
总结
多阶段构建的核心原则:
1. 分离构建和运行环境
2. 只复制必要文件到生产镜像
3. 使用最小基础镜像
4. 合理利用 Docker 缓存
5. 非 root 用户运行
遵循这些原则,镜像大小可以减少 80-99%。
优化时间:2026年7月
项目类型:Node.js / Python / Go
镜像减少:80-99%
#Docker #多阶段构建 #容器优化 #DevOps
读者评论 5