我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

conda环境复制全攻略:同机克隆、跨机迁移与离线打包

conda环境复制全攻略:同机克隆、跨机迁移与离线打包 很多人第一次在 conda 里折腾环境的时候都会有这种体验配了一个能跑的实验环境装了十几个包调了半天才把版本冲突都摆平。结果没过多久要做另一个项目需要在这个环境基础上加个新包或者新电脑上要复刻一套一模一样的就只能一边翻历史记录一边 pip install 和 conda install 来回试运气不好就得熬到半夜。conda 复制已有环境就是解决这个痛点最直接的办法。不管你是要在同一台机器上克隆一个环境出来做实验对比还是要把当前这套环境原封不动搬到另一台机器上都不需要重新手动安装更不需要去背那串又长又容易错的依赖清单。这篇文章会把复制环境这件事涉及的几个核心方案一起拆开讲清楚包括各自的适用场景、实际操作命令、以及我在真实使用中踩过的一些坑和排查思路。1. 复制环境的几种路径先想清楚再动手conda 环境复制的需求其实可以拆成两种完全不同的情况一种是在同一台机器上再克隆一份一种是换一台机器还原出一模一样的环境。很多人一上来就搜命令复制粘贴结果发现要么路径失效要么复制出来的环境根本起不来核心问题就是没搞清楚这两种场景对应的工作方式不一样。先说同一台机器上的复制。这种需求最常见的使用场景是你已经有一个稳定的环境比如叫 base 或者 project_a现在想基于它再拉一个分支出来做实验或者想在一个干净的环境里验证某些包能不能装上不想污染正在用的环境。这种情况下环境的所有文件都在本地python 解释器路径、site-packages 的位置、conda-meta 里的元数据全部是现成的复制成本最低。再就是跨机器复制。这就复杂了因为目标机器上可能没有相同的库、没有相同的操作系统、甚至 conda 的安装路径都不一样。你需要的不是复制文件而是重建依赖关系。我见过很多新手直接把整个环境文件夹拷到 U 盘里带走结果到了别的机器上python 解释器还指向原来那台机器的路径一堆包直接 import 失败。第三种方案是用 conda-pack 这类打包工具它可以克服上面两种方案的部分缺点。conda-pack 把整个环境的文件打成压缩包到目标机器上解压之后通过设置环境变量来运行它的原理是把环境内的路径映射工作做掉了适合离线环境下迁移。所以开头先记住一个原则同机器复制用conda create --clone跨机器用conda env export或者conda list --explicit离线迁移可以用 conda-pack。这几个方案不是互相替代的关系而是各有各的适用边界下面一节一节展开来讲。2. 同机克隆备份--clone参数的正确打开方式同机复制最直接的方式就是 conda 自带的 clone 功能不需要任何第三方工具命令也短。2.1 基础命令与参数说明conda create --clone 旧环境名 --name 新环境名我实际操作时最常用的是这样conda create --clone pytorch_env --name pytorch_env_backup这个命令会怎么工作conda 会扫描旧环境里 conda-meta 目录下记录的所有显式安装包然后在新环境里重放这个包列表基本上等于在本地包缓存里重新解压一份。只要包缓存在这个克隆过程可以非常快而且不需要访问网络。还有几个参数值得说一下conda create --clone old_env --name new_env --yes--yes用于跳过确认提示适合在脚本里用。如果你不确定旧环境叫什么名字可以用conda env list先查看当前机器的环境列表。2.2 验证克隆出来的环境是否完整克隆完成后一件让我每次都验证的事是检查新环境的 python 解释器路径是否独立conda run -n new_env which python如果输出路径指向的是.../envs/new_env/bin/python说明环境隔离是正常的。然后快速验证几个核心包conda run -n new_env python -c import numpy, torch; print(ok)这里想提醒一下--clone复制的是 conda 管理的包状态不会自动复制 pip 安装的非 conda 包。也就是说如果你旧环境里有的包是通过pip install装的而这些包又是非 conda 源里的克隆之后它们很可能不在新环境里这一点连官方文档都写得很委婉但实战里遇到的人非常多。3. 跨机器迁移环境导出依赖清单的完整思路跨机器复制或者说不确定目标机器是否连通、是否离线核心思路不是搬文件而是把环境的配方拎出来再到目标机器上重建。3.1 用 environment.yml 导出和重建环境这是日常最常用的方案适用面最广也最适合团队协作。conda env export environment.yml这个文件会把 conda 依赖、pip 依赖、以及渠道信息全部包含进去。拿到目标机器后conda env create -f environment.yml但这里面有个实际操作中经常会坑人的细节默认导出的 environment.yml 里会带一个prefix: /home/某用户/miniconda3/envs/旧环境名这样的字段。在很多情况下目标机器可能不是同一个路径为了避免不必要的干扰我会直接把这个字段手动删掉。更干净的做法是导出时就不带 prefixconda env export --no-builds -f environment.yml--no-builds的意思是去掉每个包后面的构建号比如numpy1.21.2py39h...变成numpy1.21.2。这样从 conda 那边搜索时更容易匹配到合适的构建版本尤其在不同操作系统之间迁移时成功率会高不少。3.2 兼顾 pip 安装的依赖刚才提到--clone不处理 pip 包的问题在conda env export这里也有类似的细节。conda 遍历环境时会把 pip 安装的包一并记录到 environment.yml 的pip:小节里这是一个好消息。但坏消息是如果某些 pip 包是通过本地源码安装的或者指向了某个私有源导出的记录可能只是file:///...这样的本地路径换台机器就失效了。所以我通常会在导出 environment.yml 之外顺手再导一份纯 pip 清单pip freeze requirements.txt然后在目标机器上创建完 conda 环境后如果发现有缺失就用pip install -r requirements.txt补一遍。这两种方式搭配使用基本能覆盖绝大多数日常项目的迁移需求。3.3 精确复现依赖的另一种姿势conda list --explicit如果你追求的是逐包精确复现可以在源机器上执行conda list --explicit spec-file.txt这个文件的本质是把每个环境的实际包下载地址来自 conda 内部的 channel URL全部列出来。拿到目标机器后执行conda create --file spec-file.txt --name new_env这个方案的优点是精确缺点是太精确了。因为记录的是具体的 URL 和构建号如果目标机器是不同操作系统、或者 conda 版本差距较大这些 URL 很可能直接失效反而不如 environment.yml 灵活。我个人通常只在两台配置非常相似的机器之间迁移时用这个方案比如都是同一个 Linux 发行版、同样的 conda 版本它的效率会明显更高。4. 离线场景下直接打包环境conda-pack 的使用细节还有一种场景越来越常见目标机器是内网机器完全连不上外网既不能用 conda 在线创建环境也无法下载包。这种情况下前面的方法都失效了需要用 conda-pack。4.1 为什么普通复制不行在没接触过 conda-pack 的时候我也想过直接cp -r环境文件夹。复制的动静很简单cp -r ~/miniconda3/envs/old_env ~/miniconda3/envs/new_env但这样复制出来的环境到另一台机器上大概率跑不了。原理其实不复杂conda 环境里的很多脚本和可执行文件内部记录了环境创建时的绝对路径典型的比如bin/目录下的脚本头部#!/home/某用户/miniconda3/envs/old_env/bin/python。一旦环境文件夹被移动到别的位置这些硬编码路径就全部指向错误的解释器环境自然就坏了。4.2 用 conda-pack 打包与解包conda-pack 就是针对这个痛点设计的它会打包环境时的绝对路径信息记录到内部解包时重新修正路径。在源机器上安装conda install conda-pack然后执行打包conda pack -n old_env -o old_env.tar.gz把这个 tar.gz 考到目标机器上解压到目标机器的 envs 目录下mkdir -p ~/miniconda3/envs/new_env tar -xzf old_env.tar.gz -C ~/miniconda3/envs/new_env接下来关键一步是激活前先激活一下环境来修正路径conda activate new_env conda-unpackconda-unpack是 conda-pack 附带的命令会在当前环境内执行路径重写把所有硬编码的旧路径替换成当前实际路径。执行完这一步环境就可以正常用了。这里有一个经验之谈打包前最好先清理一下缓存比如conda clean -a可以把里面的.tar.bz2等无用缓存文件清掉打出来的包体积会小不少传输也更快。5. 常见问题排查与避坑建议讲了这么多方法如果只是看文档可能觉得都不难。但实际操作中我几乎每次迁移都会遇到一两个小问题下面挑几个出现频率高的集中说一下。5.1 克隆之后包缺失多半是 pip 包没有复制这个前面提了很多次。如果你用--clone之后发现某个包 import 不了第一反应不要以为是克隆失败了先去新环境里执行conda list看这个包在里面没有。如果没有用pip list再查一遍。如果都没有那就是克隆本身不包含这些 pip 包手动用 pip 重新装就行。为了避免这种情况有条件的情况下尽量在创建环境时就统一用 conda 安装包如果某个包只能通过 pip 安装那就接受它的存在并在迁移文档里手动标记。5.2 导出文件里的 prefix 字段导致创建环境失败用 environment.yml 创建环境时如果报错提示和权限有关或者环境创建到了意料之外的路径可以先打开 yml 文件看一眼末尾有没有写死 prefix。我之前有一次就是忽略了这个问题结果新环境被创建到了旧用户目录下面折腾了半天才发现。5.3 跨平台迁移时 conda env export 出来的包找不到如果你在 Windows 上导出 environment.yml然后在 Linux 上创建大概率会碰到一些包在 Linux 渠道里根本没有对应的构建版本。这种情况建议在导出时加上--no-builds并且删掉特定平台的限制或者重新调整依赖的版本范围把某些不兼容的包改为从 pip 安装。5.4 复制完环境之后 import 报错如果你用文件复制方式跑起来之后 import 某个库报错且报了和.so 文件或lib相关的错大概率是因为环境内部还残留了旧路径。用conda-unpack修正一次或者删除环境重新用 conda-pack 打包。不要想着手动去改那些脚本一地碎砖头捡不完的。5.5 做一个自己的首次迁移核查清单以下是我每次迁移环境后一定会做的一组检查在这里直接列出来供你参考检查项操作命令预期结果环境列表conda env list新环境出现在列表中Python 路径conda run -n 新环境 which python指向新环境自身路径核心包可用conda run -n 新环境 python -c import 核心包正常导入无报错pip 包状态conda run -n 新环境 pip list确认关键 pip 包已存在启动脚本尝试真实运行一个项目脚本不需要手动改任何路径如果你迁移的是一个训练或推理项目核心包可用的检查建议多测几个。有一次我只验证了 torch 能导入结果一跑代码发现某个自定义组件依赖的 scikit-learn 版本错了白白排查了很久。6. 写在最后的一个小技巧关于 conda 环境复制我自己的习惯是每次环境稳定之后立刻在项目根目录执行一次conda env export environment.yml然后把这个文件提交到版本管理里。虽然看起来很简单但真的能让你在几个月后回头看的时候不用对着一个陌生环境发呆。还有一个容易被忽略的点conda 的包缓存机制其实也会影响复制速度。如果你发现--clone速度非常慢而且还会重新下载东西可以去看看pkgs目录是否被清理过。如果确实缓存不足又必须快速拟出一份环境临时通过指定旧环境的--copy参数强制使用拷贝模式也能解决问题。最终一样复制环境这件事本身并不难难的是一开始就想清楚复制之后你打算怎么维护它。希望这篇内容能帮你少走点弯路遇到具体问题的时候也能有据可查。
返回列表