Docker镜像体积过大不仅影响部署速度,还增加存储成本和安全风险。多阶段构建(Multi-stage Build)是解决这个问题的利器。本文通过实际案例展示如何将Python应用镜像从1.2GB优化到120MB。
先看一个典型的Python应用Dockerfile:
# 传统方式 - 1.2GB
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]
问题分析:
# 第一阶段:构建
FROM python:3.11 AS builder
WORKDIR /app
# 安装编译依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
python3-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# 第二阶段:运行
FROM python:3.11-slim AS runtime
WORKDIR /app
# 从builder阶段复制已安装的包
COPY --from=builder /root/.local /root/.local
# 确保使用本地安装的包
ENV PATH=/root/.local/bin:$PATH
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]
效果:镜像体积从1.2GB降至380MB(减少68%)
# 第一阶段:构建
FROM python:3.11 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# 第二阶段:最小化运行环境
FROM gcr.io/distroless/python3-debian12
WORKDIR /app
# 复制Python包
COPY --from=builder /root/.local/lib/python3.11/site-packages /app/lib
ENV PYTHONPATH=/app/lib
# 复制应用代码
COPY app.py .
EXPOSE 8000
CMD ["app.py"]
效果:镜像体积降至120MB(减少90%)
多阶段构建同样适用于Node.js:
# 第一阶段:依赖安装和构建
FROM node:18-alpine AS builder
WORKDIR /app
# 安装依赖
COPY package*.json ./
RUN npm ci --only=production
# 第二阶段:生产镜像
FROM node:18-alpine AS production
WORKDIR /app
# 创建非root用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# 仅复制必要文件
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --chown=nextjs:nodejs . .
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
Go应用的多阶段构建效果最显著:
# 第一阶段:编译
FROM golang:1.21-alpine AS builder
WORKDIR /app
# 安装git(如果需要)
RUN apk add --no-cache git
# 复制依赖文件
COPY go.mod go.sum ./
RUN go mod download
# 复制源码并编译
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# 第二阶段:最小镜像
FROM scratch
WORKDIR /root/
# 从builder复制二进制文件
COPY --from=builder /app/main .
# 如果需要HTTPS支持
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
CMD ["./main"]
效果:Go应用镜像可降至15MB以下
Spring Boot应用的多阶段构建:
# 第一阶段:构建
FROM maven:3.9-eclipse-temurin-17-alpine AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# 第二阶段:运行
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# 创建非root用户
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
# 复制jar文件
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.vscode
.idea
*.md
# 不推荐
RUN apt-get update
RUN apt-get install -y python3
RUN apt-get install -y gcc
# 推荐
RUN apt-get update && apt-get install -y \
python3 \
gcc \
&& rm -rf /var/lib/apt/lists/*
# syntax=docker/dockerfile:1
FROM python:3.11
# 启用pip缓存
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
构建命令:
DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp .
docker buildx create --use
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t myapp:latest \
--push .
| 优化方式 | 镜像大小 | 构建时间 | 启动时间 |
|---|---|---|---|
| 传统构建 | 1.2GB | 45s | 8s |
| 多阶段基础版 | 380MB | 52s | 3s |
| 多阶段+distroless | 120MB | 55s | 1s |
| Go+Scratch | 15MB | 30s | 0.5s |
# 安全最佳实践
FROM python:3.11-slim
# 1. 使用非root用户
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
USER appuser
# 2. 只安装必要的包
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
# 3. 复制特定文件而非整个目录
COPY --chown=appuser:appgroup app.py requirements.txt ./
# 4. 使用只读文件系统(配合tmpfs)
# docker run --read-only --tmpfs /tmp myapp
# 5. 限制资源
# docker run --memory=512m --cpus=1.0 myapp
多阶段构建是优化Docker镜像的核心技术:
💡 工具推荐:如果你需要批量处理Docker镜像或监控容器运行状态,可以试试PySecToolkit——一个Python安全工具包,包含端口扫描、网络发现、文件安全扫描等功能,非常适合容器安全检查和运维自动化。
本文首发于 WD Tech Blog,转载请注明出处。