Docker 多阶段构建:镜像体积减少 90% 的技巧

Docker 多阶段构建:镜像体积减少 90% 的技巧

引言:Docker 镜像体积的噩梦

想象一下,一个 2GB 的 Docker 镜像,传输需要 30 分钟,启动需要 2 分钟,存储占用巨大…

这就是没有使用多阶段构建的 Docker 镜像的真实写照。今天这篇教程将带你彻底掌握多阶段构建,让你的镜像体积减少 90%!

第一章:为什么需要多阶段构建?

1.1 传统 Docker 镜像的问题

“`dockerfile

❌ 传统 Dockerfile 的问题

FROM golang:1.21

安装依赖

RUN apt-get update && apt-get install -y \
git \
curl \
build-essential

克隆源码

RUN git clone https://github.com/yourapp/yourapp.git

编译应用

WORKDIR /yourapp
RUN go build -o main .

复制源码

COPY . .

运行时环境

RUN apt-get install -y \
openssl \
ca-certificates

结果:镜像体积 1.5GB

其中包含:编译器、依赖、源码、构建工具


传统镜像的问题:
    • 体积巨大(1-2GB)

 

    • 包含不必要的构建工具

 

    • 镜像传输慢

 

    • 启动时间长

 

    • 安全风险高

 

 

1.2 多阶段构建的优势

 

dockerfile

✅ 多阶段构建

第一阶段:构建

FROM golang:1.21 AS builder

WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

第二阶段:运行

FROM alpine:3.18

RUN apk –no-cache add ca-certificates

WORKDIR /root/
COPY –from=builder /build/main .

EXPOSE 8080
CMD [“./main”]

结果:镜像体积 35MB

相比传统方式减少 97.5%!


多阶段构建的优势:
    • 镜像体积减少 90%+

 

    • 只包含运行时依赖

 

    • 更安全的镜像

 

    • 更快的构建和部署

 

    • 更好的缓存机制

 

 

第二章:多阶段构建原理

 

2.1 工作原理

 

┌─────────────────────────────────────────────────┐
│ 多阶段构建流程 │
├─────────────────────────────────────────────────┤
│ │
│ Dockerfile: │
│ FROM base1 # 阶段 1 │
│ …build steps… │
│ FROM base2 AS final # 阶段 2 │
│ COPY –from=builder /output . │
│ │
│ 构建过程: │
│ 1. 构建阶段 1 → 生成产物 │
│ 2. 构建阶段 2 → 复制产物 → 生成最终镜像 │
│ 3. 只保留最终阶段 │
│ │
│ 优势: │
│ ✓ 构建工具不进入最终镜像 │
│ ✓ 多个阶段独立构建 │
│ ✓ 灵活复用中间产物 │
└─────────────────────────────────────────────────┘

2.2 构建缓存优化

 

dockerfile

✅ 优化缓存顺序

FROM golang:1.21 AS builder

1. 先复制依赖文件(变化少)

COPY go.mod go.sum ./
RUN go mod download

2. 再复制源码(变化多)

COPY . .

3. 最后编译

RUN go build -o main .

这样修改代码时不会重新下载依赖!

第三章:实战案例

 

3.1 Go 应用多阶段构建

 

dockerfile

Go 应用的完整多阶段构建

FROM golang:1.21-alpine AS builder

设置工作目录

WORKDIR /build

复制依赖文件

COPY go.mod go.sum ./
RUN go mod download

安装构建工具(仅构建阶段)

RUN apk add –no-cache git

克隆源码

RUN git clone https://github.com/yourorg/yourapp.git .

编译应用

CGO_ENABLED=0 禁用 Cgo,生成纯二进制

GOOS=linux 编译 Linux 版本

GOARCH=amd64 指定架构

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags=”-s -w” -o main .

验证二进制文件

RUN ls -lh main

生产阶段

FROM alpine:3.18

创建非 root 用户

RUN adduser -D -g ” appuser

安装运行时依赖

RUN apk –no-cache add ca-certificates tzdata

设置时区

ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

复制构建产物

COPY –from=builder /build/main /app/main
COPY –from=builder /build/config.yaml /app/config.yaml

设置权限

RUN chown -R appuser:appuser /app

切换到非 root 用户

USER appuser

WORKDIR /app

暴露端口

EXPOSE 8080

健康检查

HEALTHCHECK –interval=30s –timeout=3s –start-period=5s –retries=3 \
CMD wget –no-verbose –tries=1 –spider http://localhost:8080/health || exit 1

启动应用

CMD [“./main”]

可选:添加入口脚本

ENTRYPOINT [“/app/entrypoint.sh”]


镜像大小对比:

传统方式:

  • 镜像:1.2GB
  • 层数:25 层
  • 构建时间:5 分钟

多阶段构建:

  • 镜像:35MB
  • 层数:8 层
  • 构建时间:5 分钟(相同)

节省:97% 体积

3.2 Node.js 应用多阶段构建

 

dockerfile

Node.js 应用多阶段构建

第一阶段:构建

FROM node:20-alpine AS builder

WORKDIR /app

复制 package.json

COPY package*.json ./

安装依赖(使用国内镜像加速)

RUN npm config set registry https://registry.npmmirror.com && \
npm ci –only=production

复制源码

COPY . .

构建应用

RUN npm run build

生产阶段

FROM node:20-alpine

安装运行时依赖

RUN apk –no-cache add \
ca-certificates \
&& rm -rf /var/cache/apk/*

WORKDIR /app

复制 node_modules

COPY –from=builder –chown=node:node /app/node_modules ./node_modules

复制构建产物

COPY –from=builder –chown=node:node /app/dist ./dist
COPY –from=builder –chown=node:node /app/package.json ./

切换到非 root 用户

USER node

EXPOSE 3000

环境变量

ENV NODE_ENV=production
ENV PORT=3000

CMD [“node”, “dist/index.js”]


镜像大小对比:

传统方式:

  • 镜像:950MB
  • 包含:Node.js + npm + 构建工具

多阶段构建:

  • 镜像:85MB
  • 包含:Node.js + 生产依赖

节省:91% 体积

3.3 Rust 应用多阶段构建

 

dockerfile

Rust 应用多阶段构建

第一阶段:构建

FROM rust:1.75 AS builder

WORKDIR /build

创建项目结构

RUN cargo new myapp –bin
WORKDIR /build/myapp

复制 Cargo.toml

COPY Cargo.toml ./

下载依赖

RUN cargo fetch

复制源码

COPY src ./src

发布构建(优化版本)

RUN cargo build –release

验证二进制文件

RUN ls -lh target/release/myapp

生产阶段

FROM gcr.io/distroless/cc-debian12

WORKDIR /app

复制编译好的二进制文件

COPY –from=builder /build/myapp/target/release/myapp /app/myapp

设置入口点

ENTRYPOINT [“/app/myapp”]


镜像大小对比:

传统方式:

  • 镜像:400MB
  • 包含:Rust 工具链

多阶段构建:

  • 镜像:15MB
  • 包含:仅二进制文件

节省:96% 体积

3.4 Python 应用多阶段构建

 

dockerfile

Python 应用多阶段构建

第一阶段:构建和测试

FROM python:3.11-slim AS builder

WORKDIR /app

安装构建依赖

RUN apt-get update && apt-get install -y \
gcc \
python3-dev \
&& rm -rf /var/lib/apt/lists/*

复制依赖文件

COPY requirements.txt .
RUN pip install –user –no-cache-dir -r requirements.txt

复制源码

COPY . .

运行测试

RUN pytest tests/ || echo “Tests failed but continuing…”

第二阶段:运行

FROM python:3.11-slim AS runner

WORKDIR /app

安装运行时依赖

RUN apt-get update && apt-get install -y \
curl \
&& rm -rf /var/lib/apt/lists/*

复制虚拟环境

COPY –from=builder /root/.local /root/.local

设置 PATH

ENV PATH=/root/.local/bin:$PATH

复制源码

COPY . .

创建非 root 用户

RUN useradd -m -u 1000 appuser && \
chown -R appuser:appuser /app

USER appuser

环境变量

ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

EXPOSE 8000

CMD [“python”, “app.py”]


镜像大小对比:

传统方式:

  • 镜像:800MB
  • 包含:完整 Python + 构建工具

多阶段构建:

  • 镜像:120MB
  • 包含:Python + 依赖

节省:85% 体积

3.5 Java 应用多阶段构建

 

dockerfile

Java 应用多阶段构建(Maven)

第一阶段:构建

FROM maven:3.9-eclipse-temurin-17 AS builder

WORKDIR /app

复制 pom.xml

COPY pom.xml .

下载依赖(缓存优化)

RUN mvn dependency:go-offline -B

复制源码

COPY src ./src

构建应用(跳过测试加速)

RUN mvn clean package -DskipTests -B

第二阶段:运行

FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

创建非 root 用户

RUN addgroup -S appgroup && adduser -S appuser -G appgroup

复制构建产物

COPY –from=builder /app/target/myapp-1.0.jar app.jar

设置权限

RUN chown -R appuser:appgroup /app

USER appuser

JVM 参数优化

ENV JAVA_OPTS=”-Xms256m -Xmx512m -XX:+UseG1GC”

健康检查

HEALTHCHECK –interval=30s –timeout=3s –retries=3 \
CMD wget –quiet –tries=1 –spider http://localhost:8080/health || exit 1

启动应用

ENTRYPOINT [“sh”, “-c”, “java $JAVA_OPTS -jar app.jar”]


镜像大小对比:

传统方式:

  • 镜像:600MB
  • 包含:JDK + Maven + 依赖

多阶段构建:

  • 镜像:180MB
  • 包含:JRE + 应用

节省:70% 体积

第四章:镜像优化技巧

 

4.1 多阶段构建优化策略

 

dockerfile

✅ 优化 1:最小化基础镜像

使用 -alpine 或 -slim 版本

FROM golang:1.21-alpine # 而不是 golang:1.21

✅ 优化 2:删除不必要的文件

FROM golang:1.21 AS builder
RUN rm -rf /root/.cache/go-build

✅ 优化 3:使用 .dockerignore

.dockerignore 文件

.git
*.md
tests/
*.log
.env

✅ 优化 4:并行构建

FROM golang:1.21 AS builder
RUN go build -j=4 -o main .

✅ 优化 5:多架构支持

使用 buildx 多平台构建

docker buildx build –platform linux/amd64,linux/arm64 -t myapp:latest .

4.2 镜像层优化

 

dockerfile

❌ 错误的层优化

RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

✅ 正确的层优化

RUN apt-get update && apt-get install -y \
git \
curl \
&& rm -rf /var/lib/apt/lists/*


层优化效果:

错误方式:

  • 镜像层:10 层
  • 体积:200MB

正确方式:

  • 镜像层:3 层
  • 体积:180MB

节省:10% 体积

4.3 压缩和优化

 

dockerfile

✅ 使用压缩优化镜像

FROM alpine:3.18 AS runner

使用 gzip 压缩配置

COPY –from=builder config.json.gz /app/config.json.gz
RUN gunzip /app/config.json.gz

✅ 使用 distroless 基础镜像

FROM gcr.io/distroless/static-debian12

仅包含二进制文件,无 shell、无包管理器

COPY –from=builder /build/myapp /app/myapp
CMD [“/app/myapp”]


distroless 镜像对比:

传统方式:

  • 镜像:100MB
  • 包含:shell、bash、包管理器等

distroless:

  • 镜像:12MB
  • 仅包含二进制和必要库

节省:88% 体积

第五章:性能优化

 

5.1 构建时间优化

 

dockerfile

✅ 优化 1:缓存依赖

FROM golang:1.21-alpine AS builder

先下载依赖(缓存友好)

COPY go.mod go.sum ./
RUN go mod download

再复制源码(变化频繁)

COPY . .

RUN go build -o main .

5.2 并行构建

 

dockerfile

✅ 并行构建优化

FROM golang:1.21-alpine AS builder

并行编译

RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags=”-s -w” \
-trimpath \
-gcflags=”all=-N -l” \
-o main .

5.3 镜像扫描

 

bash

优化后镜像扫描

docker scan myapp:latest

使用 trivy 扫描

trivy image myapp:latest

使用 grype 扫描

grype myapp:latest -o sarif > results.sarif

5.4 完整优化示例

 

dockerfile

最终优化版本

FROM golang:1.21-alpine AS builder

WORKDIR /build

1. 缓存依赖

COPY go.mod go.sum ./
RUN go mod download

2. 复制源码

COPY . .

3. 编译优化

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags=”-s -w -X main.Version=1.0.0″ \
-trimpath -o main .

4. 验证

RUN ./main version

生产阶段

FROM alpine:3.18

安装运行时依赖

RUN apk –no-cache add ca-certificates tzdata

创建用户

RUN adduser -D -g ” appuser

WORKDIR /app
COPY –from=builder /build/main .
COPY –from=builder /build/config.yaml .

RUN chown -R appuser:appuser /app
USER appuser

ENV TZ=Asia/Shanghai
EXPOSE 8080

HEALTHCHECK –interval=30s –timeout=3s –retries=3 \
CMD wget –no-verbose –tries=1 –spider http://localhost:8080/health || exit 1

CMD [“./main”]

第六章:最佳实践

 

6.1 最佳实践清单

 

✅ 最佳实践:

    1. 始终使用多阶段构建
    2. 使用最小化基础镜像(-alpine, -slim)
    3. 优化 Dockerfile 层顺序
    4. 使用 .dockerignore
    5. 删除构建产物中的临时文件
    6. 使用非 root 用户
    7. 添加健康检查
    8. 使用 .dockerignore 排除不必要文件
    9. 扫描镜像漏洞
    10. 使用标签管理版本

❌ 避免:

      1. 在 Dockerfile 中下载大文件
      2. 在镜像中安装开发工具
      3. 使用 root 用户运行
      4. 不设置资源限制
      5. 忘记删除缓存文件
      6. 不使用 .dockerignore

6.2 性能对比数据

 

┌─────────────────────────────────┬──────────────┬──────────────┬────────────┐
│ 场景 │ 传统方式 │ 多阶段构建 │ 优化 │
├─────────────────────────────────┼──────────────┼──────────────┼────────────┤
│ Go 应用(100 个依赖) │ 1.2GB │ 35MB │ 97%↓ │
│ Node.js 应用(200 个依赖) │ 950MB │ 85MB │ 91%↓ │
│ Python 应用(50 个依赖) │ 800MB │ 120MB │ 85%↓ │
│ Rust 应用 │ 400MB │ 15MB │ 96%↓ │
│ Java 应用(Spring Boot) │ 600MB │ 180MB │ 70%↓ │
│ 构建时间 │ 5 分钟 │ 5 分钟 │ 持平 │
│ 镜像推送时间(1G) │ 60 秒 │ 2 秒 │ 97%↓ │
│ 容器启动时间 │ 30 秒 │ 5 秒 │ 83%↓ │
└─────────────────────────────────┴──────────────┴──────────────┴────────────┘

6.3 监控指标

 

bash

镜像大小监控

docker images –format “table {{.Repository}}\t{{.Tag}}\t{{.Size}}”

镜像层分析

docker history myapp:latest

容器资源使用

docker stats –no-stream

构建时间监控

time docker build -t myapp:latest .
“`

总结:多阶段构建最佳实践

通过多阶段构建:

核心优势:

        • 镜像体积减少 70-97%
        • 更安全的镜像
        • 更快的部署速度
        • 更好的缓存利用

最佳实践:

        • ✅ 始终使用多阶段构建
        • ✅ 使用最小化基础镜像
        • ✅ 优化 Dockerfile 层顺序
        • ✅ 使用非 root 用户
        • ✅ 添加健康检查

性能提升:

        • 构建时间:持平或更快
        • 部署时间:减少 97%
        • 存储成本:降低 90%
        • 传输时间:减少 97%

掌握多阶段构建,让你的 Docker 镜像更小、更快、更安全!🚀

参考资源:

      • [Docker 官方文档 – 多阶段构建](https://docs.docker.com/build/building/multi-stage/)
      • [最佳实践指南](https://docs.docker.com/develop/develop-images/docker-best-practices/)
      • [Dockerfile 参考](https://docs.docker.com/engine/reference/builder/)
      • [Alpine 镜像](https://alpinelinux.org/)

标签

发表评论