conda pack 不打包 pip install -e 安装的开发包,导致 ImportError;需改用 pip install . 并加 --include-requirements 和 --force;Docker 构建应分层缓存 requirements.txt;硬编码路径须改为相对路径或环境变量;跨平台/隔离场景优先选 Docker。

conda pack 打包后在目标机器解压运行报错 ImportError: No module named 'xxx'
根本原因是 conda pack 默认不打包 site-packages 中通过 pip install -e . 安装的本地开发包(即带有 .egg-link 或 develop-eggs 的包),它只冻结当前环境的 conda 和 pip 非编辑模式安装项。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 迁移前先确认所有依赖是否都为非开发模式安装:
pip list --local | grep -v "(editable)",若存在mylib 0.1.0 d:\dev\mylib这类输出,需先改用pip install .重装 - 打包时显式启用
--include-requirements并配合--force覆盖已有包缓存:conda pack -n myenv --include-requirements=requirements.txt --force - 解压后不要直接激活 shell,而是用
source myenv/bin/activate(Linux/macOS)或myenv\Scripts\activate.bat(Windows),否则PYTHONPATH和conda-meta路径不会被正确注入 - 若项目含 C 扩展(如
numpy、pyarrow),务必在与目标机器相同架构(x86_64/aarch64)和 glibc 版本的机器上打包,否则解压后import会 segfault
Docker 构建镜像时 Python 包反复下载,构建速度慢且不可复现
核心问题在于 Docker 默认分层缓存机制对 requirements.txt 变更过于敏感:只要文件内容变,后续所有 RUN 指令都会失效重跑,包括耗时的 pip install。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 把
requirements.txt复制和安装拆成独立层,且仅在该文件变更时重建:COPY requirements.txt /tmp/→RUN pip install --no-cache-dir -r /tmp/requirements.txt - 避免在
requirements.txt中写死 commit hash 或使用git+https://...等动态源,这类条目会导致每次构建都认为“有变化” - 若项目含私有包,优先用
pip install --find-links file:///wheels --trusted-host localhost -r requirements.txt预置 wheel 文件,而非运行时拉源码 - 基础镜像选
python:3.9-slim-bookworm而非python:3.9,减少 apt 包冲突和体积,但注意slim镜像不含gcc,编译型包需额外apt-get install build-essential
conda pack 与 Docker 都无法处理项目中硬编码的绝对路径
比如代码里写了 open('/home/user/data/config.yaml') 或 subprocess.run(['/opt/mytool/bin/process']),这类路径在新环境必然失效,且 neither 工具会自动重写。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 统一用
pathlib.Path(__file__).parent / 'data' / 'config.yaml'替代绝对路径,确保基于项目根目录定位资源 - 把外部工具路径抽成环境变量,启动时注入:
ENV MYTOOL_PATH=/app/tool,代码中读os.getenv('MYTOOL_PATH') - 若必须保留绝对路径逻辑(如 legacy 系统集成),可在容器启动时用
sed -i 's|/old/path|/new/path|g' /app/main.py临时替换,但仅限调试,不可进 CI - conda pack 解压后可通过
conda activate --stack+ 修改myenv/etc/conda/activate.d/env_vars.sh注入运行时路径变量,比硬编码更可控
如何判断该用 conda pack 还是 Docker
关键看部署目标环境的约束强度:如果目标机器能保证 OS、glibc、CUDA 版本与打包机一致,且允许解压即用,conda pack 更轻量;一旦涉及跨平台、权限隔离、服务编排或不可信环境,Docker 是唯一合理选择。
容易被忽略的细节:
-
conda pack不打包系统级动态库(如libcuda.so.1),即使 conda 安装了cudatoolkit,运行时仍依赖宿主机提供 —— 这点常被误认为“已打包 CUDA” - Docker 镜像内
pip list显示的包版本,未必等于实际 import 的版本:若存在site-packages外的.pth文件或PYTHONPATH干预,需用python -c "import xxx; print(xxx.__file__)"实际验证 - 两者都不解决配置外挂问题。数据库地址、密钥等必须通过环境变量或挂载卷传入,不能打进包或镜像 —— 否则每次换环境都要重打包


















