Conda 环境隔离的原理

在大模型本地部署中,经常需要同时维护多个项目:有的依赖 Python 3.10 + CUDA 11.8,有的需要 Python 3.11 + CUDA 12.1。如何让它们互不干扰?核心工具就是 conda 环境。本文先讲清楚 conda 环境隔离的原理,再给出在指定环境下运行不同 Python 和 CUDA 的完整方法。


一、Conda 环境隔离的原理

一句话概括:conda 的环境隔离不是操作系统级隔离,而是用户态的“目录隔离 + 环境变量重定向 + 路径查找优先级”实现的合作式隔离。 它不虚拟硬件、不隔离内核、不创建 namespace,只让当前 shell 和 Python 优先找到某个环境目录里的东西。

1. 每个 conda 环境就是一个独立目录

执行:

conda create -n llm python=3.11

conda 会创建一个独立目录,例如:

/opt/conda/envs/llm/
├─ bin/          # python、pip、nvcc 等可执行文件
├─ lib/          # libpython、libcudart、libcudnn 等库
├─ include/      # 头文件
├─ share/
└─ conda-meta/   # 记录这个环境装了哪些包

不同环境有各自的 bin、lib、include、site-packages,包不会混在一起。这是隔离的第一层:文件系统目录隔离。

2. 激活环境时,本质是改环境变量

执行:

conda activate llm

conda 主要修改当前 shell 的环境变量:

环境变量 作用
PATH 把 /opt/conda/envs/llm/bin 放到最前面
CONDA_PREFIX 指向 /opt/conda/envs/llm
CONDA_DEFAULT_ENV 变成 llm
LD_LIBRARY_PATH 通过激活脚本可能加入 $CONDA_PREFIX/lib,让动态库优先找环境目录
PKG_CONFIG_PATH 可能加入 $CONDA_PREFIX/lib/pkgconfig,供编译工具找库

最关键的是 PATH。当你输入:

python train.py

Shell 按 PATH 从左到右找第一个 python。激活后:

which python
# /opt/conda/envs/llm/bin/python

所以运行的是 llm 环境里的 Python,而不是系统 Python 或其他环境的 Python。

3. Python 自己也会按环境找包

因为当前 Python 解释器本身就是:

/opt/conda/envs/llm/bin/python

所以它启动后:

import sys
print(sys.prefix)
# /opt/conda/envs/llm

print(sys.path)
# ['', '/opt/conda/envs/llm/lib/python3.11', ...]

它会默认去:

/opt/conda/envs/llm/lib/python3.11/site-packages

找第三方包。因此:

pip install numpy

实际装到:

/opt/conda/envs/llm/lib/python3.11/site-packages/numpy

不会进系统 Python,也不会进其他 conda 环境。

4. 动态库也走路径优先级

Linux 程序运行时,动态链接器会按一定顺序找 .so。如果 LD_LIBRARY_PATH 中优先加入了:

/opt/conda/envs/llm/lib

那么 libcudart.so、libcudnn.so、libcublas.so、libnccl.so 就会优先从这个环境的 lib/ 加载。所以可以做到:

llm 环境:PyTorch 2.3 + CUDA 12.1 + cuDNN 8.9
other 环境:PyTorch 2.0 + CUDA 11.8 + cuDNN 8.6

两个环境的 CUDA 用户态库版本不同,但互不干扰。

5. conda 还能隔离非 Python 依赖

venv 只能隔离 Python 包,而 conda 还能装:

  • cudatoolkit
  • cudnn
  • nccl
  • mkl
  • ffmpeg
  • gcc
  • cmake

这些不是 Python 包,但 conda 会把它们装进环境的 bin/、lib/、include/,再通过 PATH、LD_LIBRARY_PATH、CONDA_PREFIX 让它们生效。这是 conda 在大模型场景里比 venv 更常用的重要原因。

6. 隔离的边界:合作式,不是强隔离

conda 能隔离:Python 解释器、Python 包、非 Python 库、可执行文件、头文件、部分环境变量。

conda 不能隔离:操作系统内核、显卡驱动、系统级服务、网络端口、进程空间、文件系统权限。

所以,conda 环境可以让你在一个环境里用 CUDA 12.1,另一个环境里用 CUDA 11.8,但它们最终都调用宿主机同一个显卡驱动。驱动全局唯一。 而且它是合作式的:如果你显式写绝对路径:

/usr/bin/python3 train.py

那还是会用系统 Python,conda 管不住你。

7. 与容器、venv 的区别

隔离方式 隔离层次 机制 强度
虚拟机 硬件级 Hypervisor 虚拟硬件 最强
容器 操作系统级 namespace + cgroups 强
conda 环境 用户态路径级 目录 + 环境变量 弱
venv 用户态路径级 目录 + 环境变量 弱
  • 容器:共享宿主机内核,但隔离进程、网络、文件系统、用户;
  • conda:不隔离进程、网络、文件系统,只改变路径查找优先级;
  • venv:类似 conda,但只管 Python 包,不管 CUDA、cuDNN 等非 Python 依赖。

总结 conda 隔离原理:

独立目录
  + 激活时改 PATH / CONDA_PREFIX / LD_LIBRARY_PATH 等
  + Shell 优先找到环境里的可执行文件
  + Python 按 sys.prefix 找环境里的 site-packages
  + 动态链接器优先找环境里的 .so
  = 用户态路径级隔离

它隔离的是 Python 包和 CUDA 用户态库的版本组合,不是硬件、内核或驱动。容器提供系统级隔离,conda 提供项目级依赖隔离,两者互补。


二、在指定环境下运行不同的 Python 和 CUDA

核心思路:

Python 版本由环境自己的 bin/python 决定;CUDA 用户态库由环境自己的 lib/ 或 pip wheel 决定;显卡驱动由宿主机全局提供,不能每个环境一个驱动。

也就是说:驱动全局唯一,CUDA Runtime / cuDNN / cuBLAS / NCCL 可以每个 conda 环境一套,Python 也可以每个环境一套。

1. 用 conda 创建不同 Python 版本的环境

conda create -n py311-cu121 python=3.11 -y
conda create -n py310-cu118 python=3.10 -y

每个环境都是独立目录。激活后,PATH 最前面会变成对应环境的 bin/:

conda activate py311-cu121
which python
# /opt/conda/envs/py311-cu121/bin/python
python -V
# Python 3.11.x

这样运行的就是该环境的 Python。

2. 在环境里安装指定 CUDA 版本的 PyTorch

推荐用 pip 安装 PyTorch 官方 wheel。它会自动带上匹配的 CUDA 用户态库,比如 nvidia-cuda-runtime-cu12、nvidia-cudnn-cu12 等,放在该环境的 site-packages/nvidia/*/lib 下。

环境 A:Python 3.11 + CUDA 12.1

conda activate py311-cu121
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install transformers accelerate

环境 B:Python 3.10 + CUDA 11.8

conda activate py310-cu118
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install transformers accelerate

也可以用 conda 安装:

conda activate py311-cu121
conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia

注意:同一个环境里不要 pip 和 conda 混装 PyTorch/CUDA 包,容易冲突。

3. 验证每个环境实际用的 Python 和 CUDA

conda activate py311-cu121
which python
python -V
python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())"
python -c "import torch; print(torch.cuda.get_device_name(0))"

典型输出:

2.3.0 12.1 True
NVIDIA GeForce RTX 4090

再切到另一个环境:

conda activate py310-cu118
python -V
python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())"

你会看到不同的 Python 版本和不同的 torch.version.cuda。它们互不干扰。

4. 如果需要 nvcc 编译 CUDA 扩展

只有编译自定义 CUDA 算子时才需要 CUDA Toolkit。可以装进 conda 环境:

conda activate py311-cu121
conda install -c nvidia cuda-toolkit=12.1
which nvcc
nvcc --version

另一个环境:

conda activate py310-cu118
conda install -c nvidia cuda-toolkit=11.8
nvcc --version

这样两个环境可以有各自的 nvcc,但最终调用 GPU 时,仍然通过宿主机同一个驱动。

5. 用容器隔离整个系统环境

如果不同项目需要彻底不同的 CUDA 版本、系统库、Python 版本,容器更干净。

启动带指定 CUDA 的容器:

docker run --gpus all -it --rm \
  -v $(pwd):/workspace \
  nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04 bash

另一个容器可以用 CUDA 11.8:

docker run --gpus all -it --rm \
  -v $(pwd):/workspace \
  nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04 bash

容器里已经带了对应的 CUDA 用户态库。显卡驱动不在容器里,而是由宿主机的 nvidia-container-toolkit 挂载进去。

在容器里再建 conda 环境:

apt update && apt install -y wget bzip2
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh -b -p /opt/miniconda
export PATH=/opt/miniconda/bin:$PATH

conda create -n llm python=3.11 -y
conda activate llm
pip install torch --index-url https://download.pytorch.org/whl/cu121

这样就是两层隔离:

容器层:隔离文件系统、进程、网络、CUDA 用户态库
conda 层:隔离 Python 版本、Python 包、项目依赖

6. 手动切换 CUDA Toolkit

如果宿主机装了多个 CUDA Toolkit,例如:

/usr/local/cuda-11.8
/usr/local/cuda-12.1

可以在 shell 里切换:

export CUDA_HOME=/usr/local/cuda-11.8
export PATH=$CUDA_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH
nvcc --version

切到 12.1:

export CUDA_HOME=/usr/local/cuda-12.1
export PATH=$CUDA_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH
nvcc --version

但这种方式只适合临时编译。运行 PyTorch 时,更推荐让每个 conda 环境通过 pip wheel 或 conda 包自带 CUDA 用户态库,避免 LD_LIBRARY_PATH 污染。

7. 关键限制:驱动全局唯一

无论你建多少个 conda 环境或容器,宿主机显卡驱动只有一个。

nvidia-smi 右上角的 CUDA Version 表示:当前驱动支持的最高 CUDA Runtime 版本。例如:

Driver Version: 535.129.03
CUDA Version: 12.2

表示这个驱动最高能跑 CUDA 12.2 Runtime。如果你在环境里装了 CUDA 12.4 的 PyTorch,但驱动只支持到 12.2,就可能报错。解决办法是升级宿主机驱动。

常见检查:

nvidia-smi
python -c "import torch; print(torch.version.cuda, torch.cuda.is_available())"
nvcc --version

三者含义不同:

命令 表示
nvidia-smi 的 CUDA Version 驱动支持的最高 CUDA Runtime
nvcc --version CUDA Toolkit 版本
torch.version.cuda PyTorch 编译时使用的 CUDA 版本

它们可以不同,但必须在兼容范围内。

8. 完整示例:两个环境来回切

# 环境 1:Python 3.11 + CUDA 12.1
conda create -n py311-cu121 python=3.11 -y
conda activate py311-cu121
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
python -c "import torch; print('env1', torch.__version__, torch.version.cuda, torch.cuda.is_available())"

# 环境 2:Python 3.10 + CUDA 11.8
conda create -n py310-cu118 python=3.10 -y
conda activate py310-cu118
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
python -c "import torch; print('env2', torch.__version__, torch.version.cuda, torch.cuda.is_available())"

之后运行项目时:

conda activate py311-cu121
python train.py

conda activate py310-cu118
python train.py

各自用各自的 Python 和 CUDA 用户态库。

9. 常见坑

  1. 以为 nvidia-smi 显示 CUDA 12.2,环境里就能随便用 CUDA 12.4
    不是。它只是驱动支持上限。装更高 Runtime 可能不可用。

  2. 在 base 环境里混装所有项目
    容易版本冲突。每个项目一个 conda 环境。

  3. pip 和 conda 混装 PyTorch/CUDA 包
    可能导致库版本混乱。选一种方式。

  4. 以为容器里需要装显卡驱动
    容器通常不装驱动。驱动由宿主机提供,通过 --gpus all 和 nvidia-container-toolkit 挂载。

  5. LD_LIBRARY_PATH 乱设
    可能让程序加载到错误版本的 libcudart.so、libcudnn.so。优先用 conda 环境或 pip wheel 自带机制。


三、总结

conda 环境隔离的原理是“用户态路径级隔离”:

独立目录 + 环境变量重定向 + PATH/sys.prefix/LD_LIBRARY_PATH 优先级

它隔离 Python 包和 CUDA 用户态库的版本组合,但不隔离内核、驱动、进程和网络。

要在指定环境下运行不同 Python 和 CUDA:

Python 版本:
  用 conda create -n env python=3.x
  激活后 PATH 指向 env/bin,which python 就是该环境

CUDA 用户态库:
  用 pip install torch --index-url .../cu121 或 cu118
  或 conda install pytorch-cuda=12.1
  每个环境有自己的 libcudart、cudnn 等

驱动:
  宿主机全局唯一,必须兼容你需要的最高 CUDA Runtime
  容器里由 nvidia-container-toolkit 从宿主机挂载

最终:
  conda activate env
  python train.py
  就用该环境的 Python + 该环境的 CUDA 用户态库 + 宿主机驱动 + GPU

理解这些层次和路径优先级,就能在同一台机器上干净地维护多个大模型项目,互不干扰。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容