Docker 多阶段构建实战:把镜像从 1.2GB 压到 200MB

Docker 多阶段构建实战封面

很多团队第一次用 Docker 部署应用,打出来的镜像少说 800MB,大的直奔 2GB。push 慢、pull 慢、服务器硬盘被吃光。

本文解决:如何用 Docker 多阶段构建,把镜像体积压缩到原来的 1/5。有具体数字对比,有 3 个真实踩坑记录,照着做能直接用。

问题出在哪

先看一个典型的 Python 项目 Dockerfile(大多数中文教程都这么写):

FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \\
    python3.11 python3-pip gcc g++ libffi-dev libssl-dev make curl

WORKDIR /app
COPY requirements.txt .
RUN pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
COPY . .
CMD ["python3", "main.py"]

这个镜像装了什么:Ubuntu 系统(~80MB)+ 编译工具链(~500MB)+ 开发头文件(~200MB)+ pip 缓存(~100MB)。实际运行时 gcc、make、开发头文件完全没用,纯粹占空间。

多阶段构建的原理

Docker 17.05 引入了多阶段构建:构建阶段和运行阶段分开,用最后一个阶段的产物作为最终镜像

# 阶段1:构建
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip3 install --prefix=/install --no-cache-dir \\
    -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

# 阶段2:运行
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /install /usr/local
COPY main.py .
CMD ["python", "main.py"]

关键两行:AS builder 给构建阶段命名,COPY --from=builder 从构建阶段只取需要的东西。结果:从 1.2GB 降到 180MB

踩坑记录 #1:pip 缓存

加了 --no-cache-dir 才彻底关闭缓存,不关的话同一个项目镜像会多 30MB。这个坑我踩过——打完镜像一查还是 210MB,对了半天才发现 pip 缓存没关。

踩坑记录 #2:依赖带 C 扩展

如果用了 numpy、pandas、psycopg2 这些带 C 扩展的库,python:3.11-slim 没有编译工具,会报 error: Can not find suitable compiler

解法:构建阶段装编译工具,运行阶段不要。

# 阶段1:构建
FROM python:3.11-slim AS builder
RUN apt-get update && apt-get install -y gcc g++ libffi-dev libssl-dev make
COPY requirements.txt .
RUN pip3 install --prefix=/install --no-cache-dir -r requirements.txt

# 阶段2:运行
FROM python:3.11-slim
RUN apt-get update && apt-get install -y libgomp1 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /install /usr/local
COPY main.py .
CMD ["python", "main.py"]

运行阶段 apt-get 结尾必须加 rm -rf /var/lib/apt/lists/*,不删的话 apt 缓存占 50-100MB。

踩坑记录 #3:.dockerignore

这个问题很隐蔽。项目目录里的 .git__pycache__node_modules、甚至训练数据 CSV 文件,都会被 COPY 一起打进去。

__pycache__
*.pyc
.git
.venv
node_modules
*.md
tests
.pytest_cache

真实案例:项目里有 300MB 训练数据,没加 .dockerignore 之前镜像凭空多 300MB。排查半天才定位到这个问题。

Node.js 项目同样适用

# 阶段1:构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --prefix=/install

# 阶段2:运行
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /install ./
COPY index.js .
CMD ["node", "index.js"]

node:20-alpine(~130MB)+ 多阶段构建,最终 150MB 以内。直接 FROM node 的做法轻松破 1GB。

效果对比

方式 镜像体积
单阶段(Ubuntu + 全量工具链) 1.2GB
多阶段(slim + 无缓存) 180MB
多阶段 + .dockerignore 170MB
多阶段 + alpine 140MB

结论

多阶段构建核心就三步:

  1. 构建阶段装所有需要的工具(编译工具、依赖库)
  2. 运行阶段只 COPY 必要文件COPY --from=builder
  3. 每个 RUN 结尾清理缓存--no-cache-dirrm -rf /var/lib/apt/lists/*
© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享