问题背景
最近在做一个 RAG 相关的项目,需要把基于 sentence-transformers 的 embedding 服务打包成 Docker 镜像部署。项目本身不算复杂,依赖也就那么几个,结果 docker build 跑完之后一看——8GB。
镜像推送到私有仓库要等半天,开发机拉取一次占掉小半个硬盘。更麻烦的是,每次改一行代码重新构建,整个流程又来一遍。
问题定位
先看 Dockerfile,写得很“标准”:
FROM python:3.11
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
requirements.txt 里也就列了 sentence-transformers 和几个常用的库。问题显然出在 sentence-transformers 身上。
查了一下,sentence-transformers 本身只是一个轻量的封装层,它依赖的 torch 才是真正的体积大户。默认情况下,pip install sentence-transformers 会拉取带完整 CUDA 支持的 PyTorch 版本(约 2-3GB)。问题是,如果部署环境压根没有 NVIDIA 显卡,这部分依赖完全就是冗余的。
有人在 GitHub 上提过类似的问题,一个只装了 sentence-transformers 的基础镜像就达到了 7.5GB。8GB 这个数字并不夸张——它就是“标配”。
优化方案
第一刀:换基础镜像
原来用的是 python:3.11,这个镜像本身将近 1GB。换成 python:3.11-slim,基础镜像直接降到 100MB 出头。
这一步没什么技术含量,但效果立竿见影。
FROM python:3.11-slim
第二刀:安装 CPU 版 PyTorch
这是最最核心的一步,大幅度降低镜像存储,效果非常拔群。
pip install sentence-transformers 默认会把 PyTorch 当作依赖一起装,默认装的是 GPU 版本,实际上我们应用只需要CPU版本的。这里我们指定 CPU 版本的 torch 依赖。
RUN pip install --no-cache-dir torch \
-i "https://mirrors.aliyun.com/pypi/simple/" \
-f "https://mirrors.aliyun.com/pytorch-wheels/cpu"
RUN pip install --no-cache-dir -r requirements.txt -i "https://mirrors.aliyun.com/pypi/simple/"
requirements.txt 里不需要再列 torch,否则 pip 可能会尝试升级或覆盖。
本人实测,仅此一项改动,镜像就能从 8GB 降到 2GB 左右,效果拔群。
第三刀:关闭 pip 缓存
Docker 构建过程中,pip 会把下载的 wheel 包缓存到 /root/.cache/pip,这些缓存对最终运行的容器毫无用处,白白占空间。一个简单粗暴但有效的做法是用 --no-cache-dir 参数。
第三刀:优化 Docker 层缓存
Docker 的每一层 RUN 指令都会产生一个层,层越多镜像越大。更重要的是,COPY . . 应该放在依赖安装之后——代码改动的频率远高于 requirements.txt,把 requirements.txt 单独复制并安装依赖,可以让这一层被缓存复用,每次构建不用重新下载所有包。
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
requirements.txt 不变的情况下,依赖安装层直接从缓存读取,构建速度能快不少。
第四刀:清理无用文件
__pycache__、*.pyc 这些文件在运行时没有任何用处,可以在构建最后阶段删掉:
RUN find . -type d -name "__pycache__" -exec rm -rf {} + \
&& rm -rf /root/.cache/pip
最终 Dockerfile
综合以上优化,最终的 Dockerfile 大概是这个样子:
FROM python:3.11-slim
WORKDIR /app
# 先装 CPU 版 PyTorch,防止 sentence-transformers 拉 GPU 版
RUN pip install --no-cache-dir torch \
-i "https://mirrors.aliyun.com/pypi/simple/" \
-f "https://mirrors.aliyun.com/pytorch-wheels/cpu"
# 复制依赖文件并安装(利用 Docker 缓存)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt \
-i "https://mirrors.aliyun.com/pypi/simple/"
# 复制代码(放在后面,让上面的层可以被缓存)
COPY . .
# 清理无用文件
RUN find . -type d -name "__pycache__" -exec rm -rf {} + \
&& rm -rf /root/.cache/pip
CMD ["python", "app.py"]
requirements.txt 示例:
elasticsearch<9
flask==3.1.3
sentence-transformers==6.0.0
优化效果
经过上述优化(主要是 CPU 版 PyTorch + slim 基础镜像 + 关闭 pip 缓存),镜像从 8GB 降到了 2GB 左右。构建时间从十几分钟缩短到几分钟,推拉镜像的速度也明显改善。
