Python环境部署(二) —— conda依赖包迁移

2020年9月5日 分类:开发环境 作者:清心涟漪

预计所需阅读时间:8分钟

转载自CSDN博客,作者:Forskamse

原文链接:https://blog.csdn.net/zbgjhy88/article/details/98845719

本人在维修电脑的过程,发现某些包或文件会因为蓝屏损坏,变得不可读写,JupyterLab频频出错,于是考虑迁移其它环境,重装Anaconda,重置Base环境。也发现,无法按照官方的教程来迁移,出现文章原作者提到的错误。虽然事后,所有环境都是手动重装,因为蓝屏怕了,磁盘错误都很多,干脆所有虏掠环境都重装吧。

而这篇文章对于使用anaconda运行环境来开发和数据分析的人都很有用,要想学习其他优秀开发者的代码,必须先把他们的环境搭建起来,想学习而又不在本机运行实践的心情是痛苦的。

前言

在《Python环境部署(一) —— pip依赖包迁移》中,我介绍了纯粹使用pip安装python模块的情况下如何进行依赖的迁移。没有看过的读者建议先看一看。

本文,我将继续介绍如何在使用conda的情况下进行环境的迁移。

同样不可靠的方法

conda与pip一样提供了导出依赖的方法:

conda list -e > requirements.txt

文件格式如下:

<package>=<version>=<build>

与pip导出的文件略有不同。
在待部署机器上,可以使用以下命令安装依赖以恢复依赖环境:

conda create --name <env> --file requirements.txt

此外,由于conda提供了虚拟环境导出的功能,可以按以下步骤复制一个环境:
首先,用以下命令导出一个environment.yml文件:

conda env export -f environment.yml

然后使用以下命令重建虚拟环境:

conda env create -f environment.yml

与pip相同,以上两种方法也存在一些问题。下面我仔细解释。

同样的问题:找不到匹配的包

使用requirements.txt安装同样存在找不到包的问题(PackagesNotFoundError):

造成这个现象,有与pip同样的原因——镜像源的更新,还有一个特殊的情况,那就是上图中无法匹配到的那些包,如果背后是pypi_0的,说明是通过pip安装的,这些包在使用requirements.txt文件安装时是不会得到conda支持的,因为他们是通过pip安装的。换言之,通过conda list导出的requirements.txt文件既包括conda安装的包,也包括pip安装的包,这些通过pip安装的包无法通过conda来安装。这其中原因包括:

  1. 命名规则的不同;
  2. 开发者数量的不对等(因此有些包conda上确实没有);
  3. 编译系统的差异导致版本号不对应;
  4. 并不是通过pypi安装的包等。

这些能够造成确实无法找到适配的包,但是更关键的还是多出了那pypi_0的后缀,这就导致了实际上很多包应该是匹配的,但是却搜索不到,因为找不到pypi_0这个Build名。

对待由于镜像源更新造成的找不到包的问题,不重要的包可以做升级,重要的包则需要单独考虑了。
批量更新conda的包可以使用:

conda update --all

在我更新conda安装的包并重新导出requirements.txt(先排除非pypi_0结尾的那些包)文件进行安装后,有63个通过pip安装的包无法匹配到:

在VSCode中,用空字符替换“=pypi_0”:

再进行一次安装尝试,则只剩下14个再conda源中确实无法搜索到的包了。

而如果我们使用conda env导出的environment.yml文件,则可以避开这个问题。在这个文件中,分别记录了使用pip安装的包和从conda的源中安装的包:

conda在处理这些pip安装的包时,将会转而采用pip来处理:

所以我给出的建议是,要解决这个问题,就直接不使用conda list导出requirements.txt这个方法,仍然使用pip导出requirements.txt文件,按照《python环境部署(一) —— pip依赖迁移》中所述的方法进行处理。对于conda安装的那一部分,导出environm.yml文件,删除其中pip部分,并使用下面介绍的方法处理。

离线部署

《python环境部署(一) —— pip依赖迁移》中已经明确了,实现离线部署才是最终的目的。

对于conda,看到一些教程介绍一个简单粗暴的方法,就是一股脑把虚拟环境目录复制下来(在conda主目录的envs目录下),然后拷贝到待部署机器上。这么做有一个非常糟糕的问题,就是prefix绝对目录的问题。在环境目录下用grep搜索一下,就可以发现,有大量文件内嵌了绝对目录,这在部署机器上是无法正常工作的,除非你能做到两台机器完全对等,连用户名都一样,这显然是不现实的。

可以用sed命令替换prefix,或者使用conda-pack这个模块:

# 安装(测试机器和待部署机器上都需要安装,建议在base环境安装)
conda install -c conda-forge conda-pack
# 测试机器上导出环境【注1】
conda pack -n my_env -o out_name.tar.gz
# 待部署机器上导入环境
mkdir -p my_env tar -xzvf out_name.tar.gz -C my_env
# 待部署机器上激活环境(这条命令会将my_env/bin添加到path环境变量,之后方可支持conda activate) 
source my_env/bin/activate
# 待部署机器上替换prefix
conda-unpack

【注1】:此时如遇“Files managed by conda were found to have been deleted/overwritten …”的问题,则应该是在使用过程中误用pip卸载了用conda安装的包,此时首先需要用conda正确卸载这些包,然后再执行打包的操作。

跨平台部署

对于跨平台部署,同样建议在真实的机器或者虚拟机中测试好以后再按以上离线部署方法去部署。这里介绍一下如何减少其中的工作量。

environment.yml中dependencies格式如下:

- <package>=<version>=<build>

其实package和version对于不同平台来说基本都是一样的,区别就在于build。conda根据不同平台构建二进制包时,就会产生有不同的build。据此,如果删除了build这个字段后,就可以匹配绝大多数的包。
在VSCode中用以下正则表达式匹配这个字段并用空字符替换:

这样一来,就只会有少数包无法正常被搜索到,再进行个别的安装即可。

关于其他工具的说明

(1)conda create --clone

conda create --clone命令亦可用于环境复制/迁移,但实测中却发现仍然会在源上搜索包进行安装,也无法限制其不去搜索源(开启–offline亦不生效),我不确定这是不是bug,总之我不用它:

conda create --offline --name [dest_venv_name]  --clone [src_venv_name] 

(2)搭建本地源

conda也可以搭建本地源来实现离线部署,但遗憾的是,我在conda 4.5.12上尝试的很多命令都无法生效(例如–offline、–download-only),我在help信息中确认过,语法上并没有问题,使我对其失去了兴趣,没有再折腾下去。所以,就到这里吧。有兴趣的就自己了解下吧。

https://blog.csdn.net/haveanybody/article/details/84025759

(2)conda-bundle

conda也有一些人编写了类似于pip-bundle的工具,叫做conda-bundle,感兴趣的可以看看。

https://github.com/minrk/conda-bundle

参考文献

https://conda.github.io/conda-pack/

https://blog.csdn.net/haveanybody/article/details/84025759

https://github.com/minrk/conda-bundle

继续阅读