第4章 环境导出与导入:锁定环境的“存档”与“读档”
环境配好了,依赖装齐了,代码也能跑了——然后呢?
你总得把这个环境“存个档”吧。不然换个机器、换个人,一切从头再来?
说白了,conda env export 和 conda env create -f 就是 Conda 世界里的“存档”和“读档”。
我个人习惯,项目第一天就把基础环境导出一份扔进 Git 仓库。不是多此一举,是吃过亏才长记性。
4.1 conda env export:给你的环境拍张“快照”
命令很简单,一行搞定:
conda env export -n my_project_env > environment.yml
这会把 my_project_env 里所有包的名称、版本、来源通道、甚至 build 标识,一股脑写进 environment.yml。
举个例子,我导出一个叫 ml_playground 的环境,生成的 YAML 长这样:
name: ml_playground
channels:
- conda-forge
- defaults
dependencies:
- python=3.10.12
- numpy=1.26.2
- pandas=2.1.4
- scikit-learn=1.3.2
- pip:
- mlflow==2.8.1
- tensorboard==2.15.0
注意那个 name 字段——它决定了之后 conda env create 创建的环境名。
--from-history 参数:
conda env export --from-history -n my_project_env > environment.yml
这样导出的文件更精简,跨平台兼容性也更好。我在项目交付时特别喜欢用这个选项。
4.2 解读 environment.yml:环境的“DNA”图谱
YAML 文件其实就三块内容:环境名、通道列表、依赖列表。
| 字段 | 含义 | 示例 |
|---|---|---|
name |
环境标识,创建时如果同名会覆盖 | ml_playground |
channels |
包来源通道,按优先级排列 | conda-forge, defaults |
dependencies |
包列表,支持 Conda 包和 Pip 包 | numpy=1.26.2 |
这里面有个坑——build 标识。默认导出会带上 build 号,比如 numpy=1.26.2=py310h4ffa9b7_0。这串东西在 Windows 和 Linux 上往往不一样。
我之前跨平台迁移时就踩过这个坑:Windows 上导出的文件,Linux 上直接报“找不到包”。
--no-builds:
conda env export --no-builds -n my_project_env > environment.yml
这样只锁定版本号,不锁定 build 标识,兼容性会好很多。
4.3 conda env create -f:一键还原环境
拿到 environment.yml 后,在目标机器上跑:
conda env create -f environment.yml
Conda 会按文件里的通道顺序,把每个包原封不动装回来。
如果不想用文件里写的环境名,想换个名字:
conda env create -f environment.yml -n my_new_env
这样做的好处是,你可以在同一台机器上保留多个“同款但不同名”的环境,用于对比测试。
conda env create -f 创建完环境,我第一时间会跑 conda list 检查版本是否和源环境一致。别偷懒这一步——我在项目中遇到过两次,同样的 YAML 文件,在不同时间安装出来的包版本居然不一样(因为通道里的包被更新了)。所以,锁定版本号 + 锁定通道才能真正做到“指哪打哪”。
4.4 导出指定通道:精准控制依赖来源
你有没有碰到过这种情况:团队里有人用 conda-forge,有人用 defaults,结果同一个 environment.yml 装出来的包校验值对不上?
原因很简单——通道没锁死。
导出时显式指定通道:
conda env export --channel conda-forge --channel defaults -n my_project_env > environment.yml
这样生成的 YAML 文件里,channels 列表第一行就是 conda-forge,第二行才是 defaults。创建环境时,Conda 会优先从 conda-forge 拉包。
如果你希望所有团队成员都用同一套通道优先级,还可以在导出前设置全局通道配置:
conda config --add channels conda-forge
conda config --set channel_priority strict
然后导出。这样出来的 YAML 天生就带通道优先级信息。
conda env create 会直接报错。所以,导出的通道一定要是目标机器能访问的。我建议团队内部统一使用 conda-forge 作为主通道,少用 defaults,因为 conda-forge 的包更全、更新也更及时。
4.5 实战:完整的环境迁移流程
纸上谈兵没用,我们走一遍真实的迁移流程。假设你在一台开发机上配好了环境 tf_workstation,现在要把它搬到一台没有网络的离线服务器上。
第一步,在开发机上导出:
conda env export --no-builds --channel conda-forge -n tf_workstation > environment.yml
第二步,把 environment.yml 传到目标机器(U盘、内网传输、Git 仓库都行)。
第三步,在目标机器上创建环境:
conda env create -f environment.yml -n tf_workstation
第四步,验证:
conda activate tf_workstation
conda list
python -c "import tensorflow; print(tensorflow.__version__)"
如果版本号和开发机上一致,恭喜你,迁移成功。
下面这张图总结了整个流程的核心链路:
看到没?environment.yml 就是整个流程的“桥接文件”。导出阶段生成它,导入阶段消费它。只要它内容不变,环境就能复现。
4.6 避坑指南
最后,我把这几年和 Conda 环境打交道遇到的几个典型问题,一次性列出来:
- 跨平台 build 标识不匹配——我在 Windows 上导出的环境,Linux 上死活装不上。解决方法:加
--no-builds。 - 通道不可达——YAML 里写了
defaults,但离线服务器根本没这个通道。解决方法:导出前锁定通道,并在团队内统一。 - 环境名冲突——
conda env create时如果本地已经有同名环境,Conda 不会覆盖,而是报错。解决方法:用-n指定新名字,或者先conda env remove -n old_name。 - Pip 包版本没锁死——
pip:下面的包如果不写版本号,换个时间安装可能拿到不同版本。解决方法:导出后用pip freeze手动核对,或者在environment.yml里补全版本。
总结一句话:
导出时锁通道、锁版本、去 build;导入时验版本、查冲突、改名防覆盖。做到这几点,你的环境就能像“集装箱”一样,走到哪搬到哪。