第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 上直接报“找不到包”。

避坑指南:如果你需要跨 OS 复现环境(比如从 Mac 搬到 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__)"

如果版本号和开发机上一致,恭喜你,迁移成功。

下面这张图总结了整个流程的核心链路:

环境导出与导入工作流 导出阶段 导入阶段 源环境 conda env export environment.yml (锁定文件) conda env create -f 新环境 (验证) 关键要点: 锁定通道 + 锁定版本号 + 跨平台时使用 --no-builds = 真正可复现的环境

看到没?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;导入时验版本、查冲突、改名防覆盖。做到这几点,你的环境就能像“集装箱”一样,走到哪搬到哪。