ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Anaconda离线迁移:conda-pack与CondaPackError排查

Anaconda离线迁移:conda-pack与CondaPackError排查 内网机器、隔离网段的服务器、客户现场那台连不上外网的工控机——只要你做过 Python 项目交付这几样东西里总有一样会撞上。Anaconda 环境离线迁移这件事说起来就一句话把源机器上跑得好好的 conda 环境原封不动搬到没有外网的机器上还能跑。但真动手的时候CondaPackError这行红字经常在你敲完打包命令的第三秒就跳出来之后就是一屏一屏的文件列表刷过去看着头皮发麻。我前后给三四个隔离环境搬过 conda 环境踩的坑从 pip 和 conda 混装互相覆盖到软链接指向源机器绝对路径再到 conda-unpack 被跑了两遍把路径重写坏基本上能犯的错都犯过一遍。这篇就把整套流程和 CondaPackError 的排查思路摊开讲清楚这套方案适合谁适合所有需要把 Python 环境搬进内网、或者需要给多台离线机器批量部署同一套环境的同学包括做交付的、做算法验证的、做实验复现的。不需要你有多深的 conda 底层知识但需要你愿意动手敲命令、看报错。1. 离线迁移方案怎么选别一上来就拷贝 envs 目录1.1 先搞清楚离线到底卡在哪一步很多人第一次做环境迁移下意识的动作是cp -r ~/anaconda3/envs/myenv /目标机器/anaconda3/envs/然后在新机器上conda activate myenv结果发现 python 能起来但 pip 报错、脚本 shebang 指向老路径、某些包 import 的时候直接找不到动态库。原因是 conda 环境里到处写死了prefix也就是环境所在的绝对路径。这不是几个配置文件的问题是二进制文件里、脚本头里、.pth文件里、甚至.pyc里都可能带着老路径。所以离线迁移真正的难点从来不是怎么把文件搬过去而是搬过去之后怎么让所有路径重新对上。理解了这一点方案选型的标准就清楚了谁的路径重写能力最靠得住谁就是首选。注意路径写死这件事跟环境里装了多少 pip 包强相关。纯 conda 装的环境路径基本都规范pip 装得越杂后期修路径越痛苦。1.2 三种主流方案的正面对比我把常见的三条路子列出来你自己对号入座方案核心原理优点硬伤适用场景conda env export导出 yaml导出依赖清单目标机器重新解析安装文件极小几 KB跨平台友好目标机器必须有离线 channel 全量包否则解析失败目标机器有内网 conda 镜像源conda-pack打包把整个 env 目录压成 tar用conda-unpack重写 prefix完全离线一次打包多次复用一致性极强源/目标架构、glibc 必须兼容无任何内网源纯离线交付直接cp -renvs 目录暴力复制零学习成本路径全乱基本不可用不推荐除非环境是极简纯 conda 环境第一种方案的问题在于完全离线这四个字很致命。conda env export导出的是依赖关系目标机器要真的装上还是得去 pkgs 目录或者 channel 里找包。如果内网没有搭建完整的镜像源这条路基本走不通。而且 yaml 里经常混着pip:段落pip 部分照样得联网。第二种方案就是本文的主角。它把环境目录整个打包到目标机器解压之后跑一次conda-unpack把所有记录在案的前缀字符串替换掉。这个过程不需要网络也不需要目标机器有任何 conda 包缓存。第三种方案我不建议但你可以把它当成兜底手段记在心里——万一 conda-pack 死活打不出来包环境又急着要交付cp -r加手工修 shebang 也能救场只是工作量会翻几倍。1.3 conda-pack 的路径重写机制值得多讲两句conda-unpack为什么能修路径它的做法是在打包时把所有文件扫一遍记录下哪些文件里出现了源 prefix 这个字符串解包时把这些出现位置替换成目标 prefix。文本文件比如脚本、.pth、activate直接字符串替换就好二进制文件.so、可执行文件麻烦一点因为替换后长度可能变化会破坏文件结构所以它的处理方式是把新路径后面用空字节补齐保证总长度和原来一致。这个机制解释了两个常见现象一是为什么conda-unpack只能跑一次——跑第二次的时候 prefix 已经变了再按老规则替换就会把路径改坏二是为什么路径长度差异太大偶尔会出问题——极少数二进制文件对尾部空字节敏感。知道了原理遇到诡异现象时排查方向就明确了。实操心得打包前尽量把源环境放在和目标环境长度接近的路径下。比如源是/home/user/anaconda3/envs/myenv目标尽量别是/opt/a/b长度差太夸张虽然一般没事但真出问题时你会怀疑人生。2. 动手之前环境体检和打包前的清理2.1 四个必须确认的兼容性指标打包只需要一行命令但打包前如果不做体检大概率是把问题从源机器搬到目标机器然后在解包现场炸掉。我一般固定查这四项# 1. 操作系统与内核 cat /etc/os-release uname -r # 2. glibc 版本这个最关键 ldd --version | head -1 # 3. CPU 架构 uname -m # 4. Python 版本与环境路径 conda activate myenv python -c import sys, platform; print(sys.version); print(platform.machine()); print(sys.prefix)glibc 的兼容规则是向下兼容不向上兼容。在高版本 glibc 的机器上打包拿到低版本 glibc 的机器上跑通常会报GLIBC_2.xx not found。反过来低版本打包、高版本运行基本没问题因为新系统带着老符号。所以如果源机器和目标机器系统版本不一致尽量选系统版本更低的那台作为打包源。架构这条更简单粗暴x86_64打的包aarch64上一定跑不了没有商量余地。给 ARM 边缘设备搬环境就得在 ARM 机器上打包或者用 qemu 模拟一套 ARM 环境来打。2.2 conda-pack 本身的安装离线情况下怎么办联网环境里一行命令搞定conda install -c conda-forge conda-pack麻烦的是源机器本身也不联网。这时候有两个办法。第一个办法是从别的能上网的机器上把 conda-pack 的包文件.tar.bz2或.conda格式下下来拷进内网然后本地安装conda install --offline /path/to/conda-pack-0.7.1-pyhd8ed1ab_0.tar.bz2--offline是让 conda 别去联网找依赖直接用本地包。如果报缺依赖就得把依赖包一起下下来按顺序装。第二个办法是走 pip 的 whl 离线包pip install --no-index --find-links/path/to/wheels conda-pack我个人更推荐第一种因为 conda-pack 装在哪个环境其实无所谓装在 base 里最省事。装在 base 里用conda pack -n myenv一样能打包其他环境。注意conda pack -n myenv是在当前环境执行 conda-pack但打包的目标是myenv。容易搞混的是-n和-p-n后面跟环境名-p后面跟环境路径。别写反了。2.3 打包前必做的三件清理工作清理这件事做完能省一堆麻烦还能显著缩小包体积。第一件删除 Editable 安装。这是 CondaPackError 最高频的触发源下一章会展开。现在先自查pip list --editable如果输出里有一堆指向本地目录的-e安装要么卸载重装成正式版本要么心里有数一会儿要加参数跳过。第二件清理缓存和编译产物。这些文件不影响功能但会让包膨胀pip cache purge find $CONDA_PREFIX -name __pycache__ -type d -exec rm -rf {} 2/dev/null find $CONDA_PREFIX -name *.pyc -delete 2/dev/null第三件检查环境里有没有塞业务数据。我见过有人把几个 G 的训练数据、日志、甚至模型权重直接放在环境目录下打出来的包 8 个 G传输和接收双方都很痛苦。环境目录就该只放环境业务数据单独传输。du -sh $CONDA_PREFIX du -sh $CONDA_PREFIX/* | sort -rh | head -10这个命令能快速定位体积大头。如果发现lib/python3.x/site-packages下面某个包特别大比如 torch 的几个 G那是正常的不用管如果发现根目录下有个data文件夹好几个 G那就该移走。3. conda-pack 打包全流程实操3.1 核心命令与参数逐条拆解最基础的一条命令conda pack -n myenv -o /data/backup/myenv.tar.gz看起来简单但实际生产里我一般会加上几个参数conda pack -n myenv \ -o /data/backup/myenv.tar.gz \ --ignore-missing-files \ --dereference \ --n-threads 4 \ --compress-level 6每个参数解决什么问题--ignore-missing-files跳过那些记录在 conda-meta 里、但实际已经不存在的文件。这个参数是把双刃剑用了能绕过deleted/overwritten报错但可能带出一个残缺环境。我通常先用它把包打出来再去目标机器上验证功能确认没问题才敢交付。--dereference把符号链接实体化成真实文件。不带它的话包里的软链接会保持原样到目标机器上可能指向一个根本不存在的路径。这个参数会让包变大一些但省心。--n-threads 4多线程压缩。环境大的时候比如带 torch 的单线程能压十几分钟开 4 线程能砍掉大半时间。--compress-level 6压缩级别 1 到 9越高越小越慢。6 是个甜点值再往上收益很小时间成本陡增。还有一个很有用的参数是--exclude可以把不需要的目录排除掉conda pack -n myenv -o myenv.tar.gz \ --exclude lib/python3.10/site-packages/tests/*能把包体积压下来不少但要小心别排除掉运行时真正需要的模块。3.2 打包过程的现场记录与产物校验一条正常打包命令的输出大概长这样Collecting packages... Packing environment at /home/user/anaconda3/envs/myenv to /data/backup/myenv.tar.gz [########################################] | 100% Completed | 38.2s看到进度条跑满先别急着高兴做三个校验# 1. 文件能不能正常解出 tar -tzf myenv.tar.gz /dev/null echo archive OK # 2. conda-meta 是否完整 tar -tzf myenv.tar.gz | grep -c conda-meta/.*\.json # 3. 记录校验和用于传输后比对 md5sum myenv.tar.gz myenv.tar.gz.md5第二项尤其重要。conda-meta目录是环境的身份证记录着每个包的元信息和所有文件清单。如果打包后这个目录里的 json 文件数量明显少于conda list的包数量说明包本身就有问题解包后conda-unpack会找不到元数据。传输完成后在目标机器上再跑一次md5sum -c确认文件没在传输中损坏。大文件跨网段拷贝损坏不是小概率事件我就遇到过一次包解到一半报 gzip 校验错重新传才好的。4. CondaPackError 深度排查手册4.1 报错定位的正确姿势conda-pack 的异常体系不算复杂但报错信息有时候非常长动辄列几百行文件路径直接看第一屏很容易被吓到。我的习惯是先看第一行异常类型再看最后一段总结中间那一大堆文件列表只当佐证材料。conda pack -n myenv -o myenv.tar.gz 2 pack_error.log echo $? tail -50 pack_error.log把 stderr 重定向到文件tail看结尾。conda-pack 的问题描述通常放在最后前面是它扫描出来的问题文件清单。这个操作能帮你省掉大量翻屏时间。从异常类型看主要分这么几类editable packages可编辑安装、deleted/overwritten文件被覆盖或删除、does not appear to be a conda environment不是有效的 conda 环境、Unknown package manager包管理器识别失败以及权限、磁盘、路径相关的系统级错误。下面逐个说。4.2 CondaPackError环境里有 Editable 安装包完整报错形态CondaPackError: Cannot pack an environment with editable packages installed (e.g. from python setup.py develop or pip install -e). Editable packages found: - myproject - utils根因很直接pip install -e .这种可编辑安装装进去的不是真实文件而是一个指向源码目录的链接通常是一个.egg-link文件加一条.pth路径记录。源码目录不在环境里打包自然打不进去。就算强行打进去了目标机器上那个.pth指向的路径也必然不存在。三种处理方式按推荐度排卸载后正式安装最推荐pip uninstall myproject pip install . # 注意没有 -e装完再打一次包问题消失。代价是改了源码需要重装才能生效但离线交付场景本来也不需要在目标机器上改源码。加参数跳过conda pack -n myenv -o myenv.tar.gz --ignore-editable-packages包能打出来但目标机器上就没有这几个包了。如果这几个包是可有可无的辅助工具问题不大如果是核心依赖千万别这么干。源码目录一起拷目标机器单独装把源码目录和环境 tar 包一起传过去在目标机器上先激活环境再pip install ./myproject。这个方式适合那种确实需要经常改的场景。实操心得我一般会在打包脚本里加一句pip list --editable | grep -v ^Package | wc -l结果不为 0 就直接中断打包。前置拦截比事后排查便宜得多。4.3 CondaPackError文件被删除或覆盖最高频报错长这样通常非常长CondaPackError: Files managed by conda were found to have been deleted/overwritten in the following packages: - numpy 1.24.3 missing lib/python3.10/site-packages/numpy/core/_multiarray_umath.cpython-310-x86_64-linux-gnu.so - pandas 2.0.1 overwritten lib/python3.10/site-packages/pandas/__init__.py这是我最常遇到的一类几乎没有之一。根本原因就一个conda 和 pip 混装同一个包。conda 装了 numpy后来 pip 又装了一遍 pandas 或者某个依赖pip 把 conda 装的同名文件覆盖了或者反过来pip 装了之后 conda 又装conda 把 pip 的文件删了。conda 的元数据记的是我当初装的这批文件的指纹一旦指纹对不上conda-pack 就会认为文件被篡改拒绝打包。处理路径分三步走。第一步定位是哪个包的问题。从报错列表里找包名比如上面就是numpy和pandas。第二步用 conda 强制重装把文件恢复成 conda 认可的状态conda install --force-reinstall numpy pandas--force-reinstall会重新下载或从本地 pkgs 缓存取并覆盖安装把文件指纹恢复。装完再打一次包试试。第三步如果重装解决不了或者环境已经乱到没法理清用--ignore-missing-files绕过conda pack -n myenv -o myenv.tar.gz --ignore-missing-files这个参数的含义是文件清单和实际不一致时不报错按实际存在的文件打包。包能出来但你要接受一个事实这个环境的元数据已经是脏的了。后续如果再conda install新包可能会出现更诡异的问题。所以我的建议是能重装就重装实在不行才用--ignore-missing-files并且用完之后立刻在目标机器上跑一遍功能验证。想从根上避免这个问题就一条纪律一个 conda 环境里同一个包不要既用 conda 装又用 pip 装。装新包之前先conda list pkgname查一下有没有装过。查无此包再用 pip。这条纪律听起来简单但在先 conda 装了一堆后来 pip install -r requirements.txt 一把梭的工作流里特别容易破功。requirements.txt 里如果列了 conda 已经装过的包pip 就会毫不犹豫地覆盖一遍。4.4 CondaPackError这不是一个有效的 conda 环境报错形态CondaPackError: Environment /home/user/anaconda3/envs/myenv does not appear to be a conda environment或者CondaPackError: Unknown package manager: unknown根因是 conda-pack 在环境目录下找不到conda-meta目录或者conda-meta下的 json 文件全部损坏、缺失。常见诱因有三个环境是用python -m venv建的只是恰好放在了envs目录下环境是直接cp -r拷过来的conda-meta在拷贝过程中丢了有人手工清理环境目录时把conda-meta当垃圾删了。诊断命令ls -la /home/user/anaconda3/envs/myenv/conda-meta/ | head ls /home/user/anaconda3/envs/myenv/conda-meta/*.json | wc -l正常环境这个目录下应该有几十到几百个 json 文件。如果目录不存在或者文件数为 0就不用纠结了重建环境最省时间conda create -n myenv2 --clone myenv--clone会按 conda 的理解重建一份但前提是 conda 至少部分认识原环境。如果原环境的conda-meta彻底没了--clone也会失败那就只能拿pip freeze的清单在源环境重新装一遍。顺带说一句venv建的环境不要用 conda-pack两者的机制完全不同。venv 环境的迁移有另一套简单办法直接cp -r然后把bin/目录下所有脚本的 shebang 从老路径改成新路径再删掉pyvenv.cfg让它重新生成。虽然土但针对 venv 挺好用。4.5 权限、软链接和文件占用类报错这一类报错信息通常不带CondaPackError前缀直接是 Python 的PermissionError、OSError或FileNotFoundError容易被误以为是环境问题。PermissionError: [Errno 13] Permission denied一般是源环境目录下某些文件属主不对或者输出目录没有写权限。检查方式ls -ld /data/backup find $CONDA_PREFIX -not -readable -ls | head修复就是chmod或者换个有写权限的输出目录。有个坑是如果你的 conda 装在/opt下、由 root 装的普通用户跑conda pack就可能读不到某些文件这时用sudo -u切到对应属主或者干脆用 root 打包打包场景下风险可控。软链接类的报错更隐蔽。打包时如果环境里有指向环境外部的软链接比如有人ln -s /data/models/big.bin $CONDA_PREFIX/lib/xxx默认打包会保留这个链接到目标机器上就断了。带上--dereference能实体化这些链接代价是包变大如果那个链接指向的是个几十 G 的目录包会撑爆所以打包前最好先找出所有外链find $CONDA_PREFIX -type l -exec sh -c echo $(readlink $1) | $1 _ {} \; | grep -v ^$CONDA_PREFIX这条命令列出所有指向环境外部的软链接看一眼心里就有数了。4.6 CondaPackError 速查表把上面这些整理成一张表出问题的时候直接对号入座报错关键字根本原因首选处理备选方案Cannot pack an environment with editable packages存在pip install -e安装卸载后pip install .正式安装--ignore-editable-packagesFiles managed by conda were found to have been deleted/overwrittenconda 与 pip 混装文件被覆盖conda install --force-reinstall pkg--ignore-missing-filesdoes not appear to be a conda environment缺少conda-meta目录重建环境确认是否 venv 环境Unknown package manager元数据损坏重建环境检查 json 文件完整性PermissionError: Errno 13文件属主或目录权限换输出目录 / 调整属主用对应属主用户执行No space left on device目标磁盘满换到大容量分区清理临时文件File name too longWindows 长路径限制用--format zip缩短环境路径解包后软链接失效打包保留了外链重打包加--dereference目标机器手工补链接5. 目标机器上的解包、激活与验证5.1 解包与 conda-unpack 的正确顺序包传过去之后在目标机器上# 1. 选一个最终要长期使用的路径别用临时目录 mkdir -p /opt/envs/myenv # 2. 解包 tar -xzf myenv.tar.gz -C /opt/envs/myenv # 3. 激活注意是包自带的 activate不是 conda 的 source /opt/envs/myenv/bin/activate # 4. 关键一步重写路径前缀 conda-unpack这四步的顺序不能乱第三步和第四步尤其重要。conda-unpack必须在激活之后执行因为它依赖环境内的 Python 来运行自己。如果你在没激活的情况下直接敲conda-unpack系统可能找到的是别的环境里的那个命令那就会把别的环境改坏。还有个容易被忽略的点解包路径一旦确定就别再移动了。因为conda-unpack是把前缀重写成你执行它时所在的路径。你解包到/opt/envs/myenv跑完 unpack再把这个目录mv到/home/app/envs/myenv路径又对不上了所有硬编码路径再次失效。这种问题非常隐蔽因为python还能起来只是某些包里会莫名其妙找不到文件。Windows 上流程类似激活脚本换成.\Scripts\activate conda-unpack5.2 conda-unpack 只能跑一次这条必须单独强调因为它造成的破坏是不可逆的。conda-unpack的工作原理是把当前环境里的字符串 A 替换成字符串 B。第一次执行A 是老路径、B 是新路径替换正确。如果你手抖再执行一遍它会以为当前路径就是老路径再去找一个新路径来替换结果就是把路径改成了一堆乱七八糟的东西或者直接报错退出。这时候没有简单的回滚办法只能重新解包。注意如果实在不确定有没有跑过可以看环境里有没有conda-unpack留下的完成标记。或者最稳的做法是——重新解包到一个干净目录重来一遍。反正解包就几十秒比重装环境便宜太多。还有一种情况环境里有嵌套的软链接或者多层目录conda-unpack跑完之后你发现某几个包的路径还是老的。这种一般是因为那几个文件在打包时就没被纳入管理比如手动塞进去的文件。处理办法是手工grep一下grep -rl /home/user/anaconda3/envs/myenv /opt/envs/myenv/bin /opt/envs/myenv/lib 2/dev/null | head -20把残留的老路径文件列出来逐个用sed替换sed -i s|/home/user/anaconda3/envs/myenv|/opt/envs/myenv|g 文件路径5.3 一份可以抄的验证清单解包和 unpack 都做完了别急着交付按下面的清单过一遍# 1. 解释器路径对不对 which python # 期望输出/opt/envs/myenv/bin/python # 2. Python 版本对不对 python -V # 3. 关键包能不能 import python -c import numpy, pandas, scipy print(numpy, numpy.__version__) print(pandas, pandas.__version__) print(scipy, scipy.__version__) # 4. 装了哪些包数量对不对 pip list | wc -l # 5. 动态库依赖是否齐全 python -c import ctypes; print(ctypes ok) ldd /opt/envs/myenv/lib/python3.10/site-packages/numpy/core/_multiarray_umath*.so | grep not found # 6. 跑一遍真实业务入口 python /path/to/your_main.py --help第四步的包数量最好和源机器上pip list | wc -l的结果对比一下。数量差个一两个正常有些包在打包时被跳过差十几个就说明打包时跳过太多东西了得回头查。第六步最关键。前面五步全是环境能不能起来的检查只有第六步能验证业务能不能跑。我踩过一次坑环境检查全部通过结果业务脚本跑到一半报某个包的数据文件找不到原因是那个包的数据文件不在conda-meta记录里打包时被漏掉了。这种问题只有跑业务才能暴露。6. 跨平台坑、动态库冲突与环境维护6.1 glibc 和 CPU 架构的隐形坑前面提过 glibc 向下兼容这里补一个具体的排查手段。如果目标机器上 import 某个包报这种错ImportError: /lib64/libm.so.6: version GLIBC_2.29 not found先确认目标机器的 glibc 版本ldd --version | head -1再确认报错的那个.so需要什么版本objdump -T /opt/envs/myenv/lib/python3.10/site-packages/numpy/core/_multiarray_umath*.so | grep GLIBC_2如果.so要求的版本高于系统版本唯一的正规解法是换一台 glibc 版本够高的机器或者回到低版本系统重新打包。想去手工替换系统libm.so.6是极其危险的操作容易把系统搞崩不建议。架构问题没有绕路的空间。aarch64的机器只能用aarch64打出来的包。如果你的开发机是 x86目标设备是 ARM那就在 ARM 上搭一套打包环境或者用容器模拟 ARM 环境打包。这条路上没有捷径。6.2 GLIBCXX 版本冲突的处理另一类高频报错ImportError: /usr/lib64/libstdc.so.6: version GLIBCXX_3.4.26 not found这个和上一条不是一回事。GLIBCXX 是 C 标准库的符号版本报错说明系统自带的libstdc.so.6太老而环境里的某个扩展模块需要更新的版本。好消息是这个问题通常有救因为 conda 环境里一般自带一份比较新的libstdcls $CONDA_PREFIX/lib/libstdc.so.6* strings $CONDA_PREFIX/lib/libstdc.so.6 | grep GLIBCXX_3.4.26如果环境里的这一份包含所需版本让动态链接器优先找环境里的即可export LD_LIBRARY_PATH$CONDA_PREFIX/lib:$LD_LIBRARY_PATH再重新 import 试试。要让这个设置在每次激活环境时自动生效可以写进环境激活脚本cat $CONDA_PREFIX/etc/conda/activate.d/env_vars.sh EOF export LD_LIBRARY_PATH$CONDA_PREFIX/lib:$LD_LIBRARY_PATH EOFactivate.d目录下的脚本会在环境激活时自动执行这是 conda 官方支持的扩展方式比在.bashrc里写死环境路径要干净得多。实操心得LD_LIBRARY_PATH设成全局的很容易引发其他程序的问题所以尽量只在这个环境的 activate.d 里设出环境就自动失效。6.3 路径硬编码的排查与修补conda-unpack覆盖了绝大部分路径重写但有几类它管不到手工添加的文本文件比如你手写的一个sitecustomize.py里面写死了绝对路径。二进制文件里编译进去的 RPATH某些自己编译的 C 扩展如果编译时加了-Wl,-rpath,/home/user/anaconda3/envs/myenv/lib这个路径被编译进了 ELF 的 RPATH 段conda-unpack的字符串替换机制未必能覆盖到。pip 安装的包生成的 console_scripts大多数情况下 conda-pack 能处理但如果这些脚本是在打包之后才生成的就没戏了。排查方式grep -rl anaconda3/envs/myenv $CONDA_PREFIX --include*.py --include*.sh --include*.pth 2/dev/null find $CONDA_PREFIX -name *.so -exec sh -c readelf -d $1 2/dev/null | grep -q RPATH\|RUNPATH echo $1 _ {} \;第一条找文本文件里的残留路径第二条找带 RPATH 的二进制文件。文本文件用sed替换就行二进制文件的 RPATH 就得用patchelf处理了patchelf --set-rpath $ORIGIN/../../.. some_module.so$ORIGIN是 ELF 的相对路径记号表示这个文件自己所在的目录。用它来写相对路径可移植性最好环境搬到哪都不会失效。不过这条属于进阶操作不到万不得已不用。6.4 环境迭代后怎么再做一次迁移离线环境不是一次交付就完事的后面大概率要加包、升级版本。这时候不要傻乎乎地在源机器上重新打一个大包。如果目标机器上已经有了环境只是在里面加几个包那就在能联网的机器上把需要的包下成离线包拷过去本地安装# 联网机器上只下载不安装 conda install --download-only -n myenv somepackage # 去 pkgs 缓存目录找到下载好的包 ls ~/anaconda3/pkgs/ | grep somepackage把这个.tar.bz2或.conda文件拷到目标机器本地安装conda install --offline /path/to/somepackage-1.0-xxx.tar.bz2如果是 pip 包同理pip download somepackage -d ./wheels # 拷过去 pip install --no-index --find-links./wheels somepackage只有在大规模变更比如整体升 Python 版本的时候才值得重新走一遍 conda-pack 全流程。另外还有个容易被忽略的维护点conda-pack 打出来的环境在目标机器上是可以继续用 conda 管理的只要你把 conda 本体和 pkgs 缓存也准备好了。但如果没有那就把它当成一个普通的虚拟环境用pip install离线包、python xxx.py都没问题只是别再指望conda install能联网干活了。我个人在几次离线迁移里最大的体会是打包前的十分钟检查能省掉现场两个小时的手忙脚乱。具体来说就是三个动作——跑一遍pip list --editable看有没有可编辑安装跑一遍conda list和pip list交叉比对看有没有混装覆盖跑一遍du -sh看体积有没有异常。这三条做到位CondaPackError 里最烦人的那两类基本就不会出现。至于conda-unpack只能跑一次这条我的做法是在交付文档里用最大号字体写出来并且在解包脚本里加一个标记文件判断防止接手的人重复执行——毕竟这个错误一旦发生除了重新解包没有别的办法。
返回列表