在大模型本地部署中,经常需要同时维护多个项目:有的依赖 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 还能装:
cudatoolkitcudnnncclmklffmpeggcccmake
这些不是 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. 常见坑
以为
nvidia-smi显示 CUDA 12.2,环境里就能随便用 CUDA 12.4
不是。它只是驱动支持上限。装更高 Runtime 可能不可用。在 base 环境里混装所有项目
容易版本冲突。每个项目一个 conda 环境。pip 和 conda 混装 PyTorch/CUDA 包
可能导致库版本混乱。选一种方式。以为容器里需要装显卡驱动
容器通常不装驱动。驱动由宿主机提供,通过--gpus all和nvidia-container-toolkit挂载。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
理解这些层次和路径优先级,就能在同一台机器上干净地维护多个大模型项目,互不干扰。