
很多团队第一次用 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 |
结论
多阶段构建核心就三步:
- 构建阶段装所有需要的工具(编译工具、依赖库)
- 运行阶段只 COPY 必要文件(
COPY --from=builder) - 每个 RUN 结尾清理缓存(
--no-cache-dir、rm -rf /var/lib/apt/lists/*)
更多交流点击入群






