ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Ubuntu下Anaconda虚拟环境全攻略:conda安装、隔离与迁移实践

Ubuntu下Anaconda虚拟环境全攻略:conda安装、隔离与迁移实践

做开发这几年,Ubuntu 一直是我的主力系统,但最早用 Python 的时候,我并没有认真对待“环境管理”这件事。系统全局的 Python 里装了一堆包,今天给 A 项目装 pandas,明天给 B 项目装 tensorflow,半年后自己都分不清哪些是哪个项目的依赖,升级一个包能牵出一串连锁反应,最后只能靠重装系统解决问题。后来切到 Anaconda + conda 虚拟环境,才算把项目依赖的“隔离”和“复现”这两个核心问题真正理顺了。

这篇内容围绕 Ubuntu 下 Anaconda 的安装、虚拟环境创建、激活、包管理、环境迁移以及常见报错排查展开,基本覆盖我日常使用中最频繁操作的完整流程。如果你正准备在 Ubuntu 上搭一个干净的 Python 开发环境,或者已经装了 Anaconda 但一直没搞明白虚拟环境的正确用法,下面的内容可以直接照着抄。

1. conda 虚拟环境到底解决了什么问题

1.1 全局环境混乱的真实代价

先说一个我在某次数据项目里遇到的典型情况:项目依赖 Python 3.8 和旧版 numpy,另一个网页后端项目要求 Python 3.10 且 numpy 必须是最新版。如果两个项目都使用系统全局 Python,两者对 numpy 的版本要求就会直接冲突。我当时的处理方式很粗暴——先装 A 项目,测试通过后再卸载重装依赖跑 B 项目,来回折腾。

这种做法的本质问题是:全局环境只有一个,所有项目的依赖都堆在里面,彼此没有边界。你无法同时维护两套互相冲突的依赖,也无法保证项目在另一台机器上跑出相同结果。所谓“在我电脑上是好的”,往往就是全局环境依赖混乱的产物。

conda 虚拟环境则给每个项目分配一套独立的 Python 解释器和包目录。A 项目在 env_alpha 里装 numpy 1.24,B 项目在 env_beta 里装 numpy 2.x,互不干扰。切换项目时只需切换环境,后续所有安装、卸载、升级操作都在当前环境内生效,全局系统始终保持干净。

1.2 conda 环境在底层是怎么隔离的

conda 虚拟环境本质上是一个目录集合,默认放在~/anaconda3/envs/<环境名>下。每个环境目录里包含独立的bin/(存放可执行文件)、lib/(存放 Python 库)和lib/pythonX.Y/site-packages/(存放第三方包)。

激活环境时,conda 会修改当前 shell 的PATH变量,把该环境的bin目录放到最前面。这样你在终端输入python、pip、conda install时,系统第一个找到的就是当前环境下的可执行文件,而不是全局的/usr/bin/python。这一步是理解后续所有环境切换问题的关键。

1.3 为什么不直接用 venv 或 Docker

Python 自带的 venv 同样能创建虚拟环境,但它只隔离 Python 包,不管理 Python 解释器版本,也不处理非 Python 的系统库依赖。Docker 能做完整的操作系统级隔离,但镜像构建、容器交互的成本对日常开发来说明显偏重。

conda 的定位介于两者之间:它能同时管理 Python 解释器版本、第三方包版本,以及部分带二进制依赖的库(例如科学计算生态),又不像 Docker 那样需要容器编排。对绝大多数数据分析和 Web 开发场景,conda 是投入产出比最高的选择。

2. Ubuntu 下安装 Anaconda 的完整流程

2.1 安装包下载与校验

Anaconda 官方提供 Linux x86_64 的 shell 安装脚本,文件名形如Anaconda3-2024.10-1-Linux-x86_64.sh。下载后建议先做校验,避免文件损坏或来源异常。

# 下载安装脚本(以下是示意命令,实际版本号以官网为准) wget https://repo.anaconda.com/archive/Anaconda3-2024.10-1-Linux-x86_64.sh # 校验 SHA256 哈希,官方页面会给出对应文件的哈希值 sha256sum Anaconda3-2024.10-1-Linux-x86_64.sh

把计算出的哈希值与官方公布值对比,一致再继续安装。这一步虽然简单,但在网络传输不稳定的时候能省掉很多莫名其妙的后续报错。如果你只需要 conda 和 Python,不需要 Anaconda 预装的大量科学计算包,也可以选体积更小的 Miniconda,安装流程完全一致。

提示:不要在下载后直接sh执行,除非你确认脚本来源可信。校验哈希是安全习惯,不是多此一举。

2.2 交互式安装与安装选项

执行安装脚本:

bash Anaconda3-2024.10-1-Linux-x86_64.sh

安装过程会依次询问几个问题,通常按以下方式处理:

  1. 是否查看许可协议:输入no可以直接跳过全文,进入确认环节。
  2. 是否同意许可协议:输入yes。
  3. 安装位置:默认是$HOME/anaconda3。我建议保留默认路径,因为后续所有环境目录、shell 配置都基于这个位置,换路径意味着所有配置都要跟着改,没有必要。
  4. 是否初始化 conda:输入yes。这一步会把 conda 的初始化脚本写入~/.bashrc,让新开终端自动加载 conda 命令。

安装完成后,重新打开终端,或手动执行source ~/.bashrc,然后验证:

conda --version python --version which python

如果which python显示为~/anaconda3/bin/python,说明安装成功且 base 环境已生效。

2.3 conda init 到底往 shell 里写了什么

conda init的核心操作是向~/.bashrc写入一段初始化代码。以 bash 为例,它会把$HOME/anaconda3/bin/conda以及一个名为conda的 shell 函数定义加载到当前会话中。你之后输入conda activate、conda deactivate,调用的其实是这个 shell 函数,而不仅仅是/anaconda3/bin下的二进制文件。

这也是很多人一上来会困惑的地方:直接执行conda activate xxx报错,提示不是命令;但只要source ~/.bashrc之后就好了。原因是 shell 函数只有在 bashrc 被加载后才存在于当前会话。如果你用的是 zsh,需要额外执行conda init zsh,让配置写入~/.zshrc。

如果你当初安装时选择了不初始化,事后补一条:

conda init bash source ~/.bashrc

2.4 安装后立刻要做的基础设置

装完 Anaconda 后,我建议立刻做三件事,能省掉后面大部分网络和路径烦恼:

第一,确认默认 channel 可用。conda 默认从官方源拉取包,国内网络下常常慢得离谱。可以按需配置镜像源,但我更推荐的做法是在下载较大包时临时指定镜像:

conda install numpy -c https://mirrors.xxx.com/anaconda/pkgs/main

第二,关闭自动激活 base。Anaconda 安装后每次打开终端都会自动进入 base 环境,我个人不习惯这个行为:

conda config --set auto_activate_base false

这样新终端保持系统默认 shell,需要使用 conda 时手动conda activate 环境名,逻辑更清晰。

第三,确认pip指向当前环境。执行:

pip --version

如果显示的是~/anaconda3/lib/pythonX.Y/site-packages,说明当前 shell 的 pip 与 conda 的 Python 解释器绑定正确。这部分如果不对,后面会出现“pip 装包后 Python 导入失败”的经典问题。

3. 虚拟环境的创建、切换与删除全流程

3.1 创建环境时如何指定 Python 版本和包

创建环境最常用的命令是:

conda create -n myenv python=3.10

其中-n指定环境名,python=3.10指定该环境下要安装的 Python 版本。conda 会自动寻找与当前平台匹配的 Python 3.10 解释器,并同时安装 pip、setuptools 等基础工具。

你也可以在创建时一并指定需要的包:

conda create -n myenv python=3.10 numpy pandas requests

这样一次性创建环境并安装指定包,省去后续逐条安装。注意,包名后可以带版本约束:

conda create -n myenv python=3.10 numpy=1.24.3 "requests>=2.28,<3.0"

版本约束写在引号里是为了避免 shell 对><符号做重定向解析。这个细节经常被忽略,实际执行时不加引号,命令会走得莫名其妙。

创建完成后,可以看到类似# To activate this environment, use: conda activate myenv的提示,同时环境目录已经落在~/anaconda3/envs/myenv。

3.2 激活、退出与切换的易错点

激活环境:

conda activate myenv

激活后,终端提示符前缀会从(base)或空变成(myenv),这是最直观的“我现在在哪个环境”的标识。退出当前环境:

conda deactivate

切换到另一个环境,不需要先 deactivate,直接conda activate 另一个环境名即可。conda 会自动替换PATH中的环境目录。

易错点集中在两点:

第一,conda activate只对当前 shell 会话有效。你用conda activate myenv之后,再开一个新终端,它默认不在 myenv 里。环境切换不是全局状态,理解这一点能避免很多“环境明明切换了为什么命令不对”的困惑。

第二,在 shell 脚本或 crontab 中执行conda activate会失败,因为非交互式 shell 默认不加载~/.bashrc。脚本里需要先手动引入 conda 配置,再激活环境:

source ~/anaconda3/etc/profile.d/conda.sh conda activate myenv python run.py

3.3 查看环境列表、删除环境与重命名

查看当前有哪些环境:

conda env list

输出第一列是环境名(base是 Anaconda 自带的初始环境),第二列是环境目录。也可以使用conda info --envs,效果相同。

删除环境,要把环境及其内部所有包一起移除:

conda remove -n myenv --all

注意:conda remove -n myenv如果不加--all,只会移除该环境的包,而不会删除环境本身。我见过有同事执行后环境名还在,里面包空了,还以为是命令失效。

关于环境重命名,conda 没有提供直接的 rename 命令,但可以通过克隆再删除实现:

conda create -n newname --clone oldname conda remove -n oldname --all

克隆会完整复制原环境的 Python 版本和已安装包,数据量大的情况下耗时会长一些,但结果可靠。

4. 依赖管理的进阶组合拳

4.1 conda install 与 pip install 的选用逻辑

很多初学者会混淆这两个工具。它们都能安装 Python 包,但来源和管理机制不同:

对比项conda installpip install
包来源conda 源(Anaconda 官方 channel)PyPI
依赖解析conda 解析器生成完整软件包依赖只解析 Python 包依赖
非 Python 依赖能带上部分二进制库一般无法处理
环境外影响不会影响其他环境pip 默认装进激活的 conda 环境
适用场景科学计算、带系统库的包PyPI 独有、更新的包

实际项目中,我的原则是:能用 conda 装的核心包优先用 conda,conda 源里没有或版本太旧的包再用 pip。默认情况下,只要你激活了某个 conda 环境,pip install会把包装进该环境对应的site-packages,不会污染系统 Python。

但有一个隐蔽陷阱需要注意:如果当前 shell 没有激活任何 conda 环境,系统PATH里可能只有一个全局 pip。此时执行pip install,包会装到全局目录或某个默认 Python 的 site-packages 里,之后回到 conda 环境里当然找不到。解决办法永远是先conda activate 目标环境,再用pip install,装完可执行pip list确认包出现在当前环境列表中。

4.2 同源安装的混用风险

不同源的包如果混装同一个库的不同版本,容易出现“pip 报告已安装,conda list 看不到”或反之的割裂状态。比如 conda 环境里已装 numpy 1.24,但你再用 pip 强行升级到 2.1,conda 的依赖解析器并不知道这个变化,后续 conda 安装其他包时,可能会因为依赖冲突而重新安装 conda 版 numpy,覆盖 pip 的版本。

我的处理策略:环境创建时就一次性把核心依赖用 conda 装完,之后再用 pip 补少量 conda 源缺失的包。尽量不要同一个包两边交替安装,更不要在同一环境里对同一个包反复用 conda 和 pip 切换版本。

4.3 使用导出文件复现环境

跨机器复现环境是虚拟环境最核心的价值之一。conda 提供了两种导出方式:

# 导出当前环境的包列表(含具体版本号) conda env export -n myenv > environment.yml # 只导出显式安装的核心包 conda list -n myenv --explicit > spec-file.txt

environment.yml更常见,它除了包列表,还记录了 conda 源、pip 安装的包以及依赖树中的传递依赖。在另一台机器上重建:

conda env create -f environment.yml

注意,conda env export导出的是当前平台下的包列表,换操作系统或 CPU 架构时可能需要调整部分包版本。

如果用 pip 管理的是补充包,可以单独导出:

pip freeze > requirements.txt

需要复现时先创建 conda 环境并激活,再执行pip install -r requirements.txt。

4.4 克隆环境与批量更新依赖

克隆环境前面已经提到,常用于“我要在这个环境的副本上做实验”的场景。批量更新某个环境内所有包可以执行:

conda update -n myenv --all

这个命令会按照 conda 依赖规则升级所有可更新的包。实际执行前我建议先备份environment.yml,因为--all有时候会顺手升级一些不期望变化的传递依赖,尤其是在项目进行到一半的时候。更稳妥的做法是只更新特定包:

conda update -n myenv numpy pandas

5. 我踩过的高频坑及其排查流程

5.1 新终端里找不到 conda 命令

现象:安装完成后,打开新终端输入conda,提示command not found。

排查链路:

  1. 先检查~/.bashrc末尾是否包含 conda 初始化代码。如果安装时选择了不初始化,或后来手动删过该段落,就会出现这种情况。
  2. 执行ls ~/anaconda3/bin/conda,确认安装目录下确实存在 conda 可执行文件。
  3. 重新执行conda init bash,然后source ~/.bashrc。
  4. 如果你用的是 zsh,则需要conda init zsh,并确认~/.zshrc中加载了对应配置。

这类问题九成以上是 shell 配置缺失,而不是 conda 安装失败。

5.2 pip 装了包,但 Python 里 import 不到

现象:某个环境下执行pip install requests成功,但进入 Python 后import requests报 ModuleNotFoundError。

排查链路:

  1. 在当前 shell 执行which pip和which python,对比两者路径。
  2. 如果which pip指向全局路径,比如/usr/bin/pip,而which python指向~/anaconda3/envs/myenv/bin/python,说明 pip 与当前环境解释器不一致。
  3. 在环境内重新安装 pip:python -m pip install --upgrade pip,之后用python -m pip install 包名代替直接输入pip install。

python -m pip这种方式能保证 pip 始终运行在当前 Python 解释器对应的模块上下文中,是规避 pip 指向错乱最稳妥的写法。从踩坑之后我就一直用这种形式装包。

5.3 创建环境总是卡在网络请求上

现象:conda create -n newenv python=3.10长时间停留在Solving environment或下载阶段。

排查链路:

  1. 确认网络能否访问 conda 源。可以用curl -I <镜像地址>判断连通性。
  2. 检查当前 conda 配置了哪些 channel:conda config --show channels。
  3. 如果默认走官方源且速度很慢,可临时指定反映更快的镜像源执行创建。
  4. 如果局域网内有缓存仓库或代理,也可以配置proxy_servers,但更常见的做法还是换一个与当前网络环境匹配的镜像。

另外,若Solving environment阶段卡住很久,也可能是因为 conda 默认的 channel_priority 策略在解析复杂依赖时开销大。可以临时执行conda config --set channel_priority strict或改用--strict-channel-priority参数,减少候选版本数量,加快解析速度。

5.4 conda 目录膨胀与缓存清理

运行一段时间后,~/anaconda3目录会越占越大,主要来源是包缓存、下载缓存和旧版本包。定期清理:

# 清理索引缓存和下载缓存 conda clean -a # 清理不带 --all 的所有无用包缓存 conda clean -p

但是清理缓存过后,环境中有些包可能需要重新下载才能升级或重建,所以在清理前,建议先确认当前项目已处于稳定阶段。还有一点,conda 环境本身是“重”目录,如果你频繁创建和删除大环境,磁盘碎片和剩余缓存都会累积,我通常是每个季度做一次conda clean -a,再检查一次du -sh ~/anaconda3/envs/*,看看有没有异常庞大的环境可以删除。

6. 如何规划一套干净且可复现的环境方案

6.1 环境命名与 Python 版本选择

环境命名我建议遵循一条规则:项目或业务代号加 Python 主版本,例如:

webapi-py310 dataproj-py38

这样看到名字就能知道是哪个项目、用什么 Python 版本。Python 版本选择上,我的原则是优先选项目依赖生态支持较成熟的版本,不要一味追求最新版。很多科学计算包对新版 Python 的适配有滞后,选太新的版本会遇到“conda 里找不到兼容的 wheel”这类问题。稳妥的做法是:先查你要用的核心包支持哪些 Python 版本,再取一个中间值。

6.2 环境配置也要纳入版本管理

环境隔离只是第一步,如果环境文件本身不被管理,换机器或换人时依然无法复现。我的习惯是:把environment.yml和requirements.txt放在项目的仓库根目录,和代码一起提交。

代码迭代过程中依赖发生变化时,同步更新环境文件,并在提交说明里注明“更新了某个依赖版本”。这样团队其他成员git pull后,直接conda env create -f environment.yml就能搭建一致环境,不需要相互传截屏和口头沟通。

6.3 不要一直蹲在 base 环境里

一个最常被忽略的建议是:不要总是用 base 环境跑项目。base 是 Anaconda 自带的初始环境,装进里面的包会被所有其他环境共享间接影响。把它当成“元管理环境”来用,只保留 conda、必要的基础工具,而每个项目单独建独立环境。

你可以把 base 当作“找不到包时兜底的地方”,但不要在里面长期安装大量项目依赖。否则它不仅变得臃肿,还会在conda update --all时引入各种意想不到的版本跳动。

6.4 团队协作时的环境一致性补充

如果团队成员需要完全一致的环境,建议在environment.yml之外再加一份requirements.txt,前者负责 conda 核心包,后者负责 pip 补充包,并约定“先用 conda 建环境,再用 pip 装补充包”。这样双轨制可以最大化覆盖两种来源的包,同时避免单一工具依赖解析不到的问题。

从创建第一个 conda 环境到现在,这套流程应付过的项目已经不少了。过程中踩得最深的一次,就是在一个项目里用 pip 和 conda 反复切换同一个核心库的版本,最终导致环境中出现两份不完全一致的二进制文件,排查了很久才定位到是混源安装的锅。后来我把所有项目都改成“先 conda 固定核心,再用 pip 极小范围补充”,此后就再没遇到类似问题。Ubuntu 下 conda 虚拟环境的学习曲线并不陡峭,只需要把创建环境、切换环境、依赖迁移和理解 PATH 机制这四件事弄通,日常开发就基本能顺畅跑下去了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进