2. 环境准备:Docker基础、NVIDIA Docker与CUDA环境配置、Python虚拟环境管理
说实话,模型训练出来只是第一步。真正让人头疼的,是把模型部署到生产环境里跑起来。我见过太多团队,模型在开发机上跑得飞起,一到线上就各种报错——CUDA版本不对、依赖冲突、系统库缺失……说白了,就是环境没管好。
这一章,我们就来解决这个问题。我会带你从零搭建一套「可复现、可迁移、可隔离」的部署环境。嗯,这也是我这些年踩坑踩出来的经验。
2.1 Docker基础:为什么我们需要容器化?
先问一个问题:你有没有遇到过「在我机器上能跑啊」这种尴尬?
我遇到过,而且不止一次。有一次帮客户部署一个OCR模型,本地测试一切正常,结果到客户服务器上,OpenCV的版本不对,图像解码直接崩了。排查了整整半天,最后发现是系统自带的libjpeg版本太老。
Docker就是来解决这个问题的。它把应用和它的依赖打包在一起,形成一个「集装箱」。不管底层系统是Ubuntu还是CentOS,只要装了Docker,就能跑。
核心概念速览:
- 镜像(Image):一个只读的模板,包含运行环境的所有文件。比如一个装了Python 3.9和PyTorch 1.12的镜像。
- 容器(Container):镜像的运行实例。你可以启动、停止、删除它。每个容器是隔离的。
- Dockerfile:构建镜像的「配方」文件。写清楚要装什么、怎么配。
- 仓库(Registry):存放镜像的地方。最常用的是Docker Hub,企业内部也可以用私有仓库。
我个人习惯,每个部署项目都从Dockerfile开始。这样团队里任何人拉下来,build一下,跑出来的结果一模一样。这才是真正的「可复现」。
2.1.1 安装Docker
安装其实很简单。以Ubuntu 20.04为例:
# 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc
# 安装依赖
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
# 添加Docker官方GPG密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 设置稳定版仓库
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
# 验证安装
sudo docker run hello-world
看到「Hello from Docker!」就说明成功了。嗯,这里有个小坑——默认情况下,运行docker命令需要sudo。我建议你把当前用户加到docker组里:
sudo usermod -aG docker $USER
# 然后退出重新登录,或者执行 newgrp docker
小技巧: 如果你在国内,拉取镜像可能会很慢。建议配置国内镜像加速器,比如阿里云、中科大的。在 /etc/docker/daemon.json 里加上:
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}
然后重启Docker:sudo systemctl restart docker
2.2 NVIDIA Docker:让容器用上GPU
做深度学习部署,GPU是绕不开的。但普通Docker容器默认是看不到GPU的。你想想看,容器里跑模型,结果只能用CPU,那速度得多慢?
NVIDIA Docker就是来解决这个问题的。它让容器能直接调用宿主机的GPU驱动和CUDA运行时。
2.2.1 安装NVIDIA Container Toolkit
注意,老版本的nvidia-docker已经不再维护了。现在官方推荐的是NVIDIA Container Toolkit。安装步骤如下:
# 配置仓库
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
# 安装
sudo apt-get update
sudo apt-get install -y nvidia-docker2
# 重启Docker
sudo systemctl restart docker
# 测试GPU是否可用
sudo docker run --rm --gpus all nvidia/cuda:11.8-base nvidia-smi
如果能看到GPU信息,就说明配置成功了。我曾经在配置这一步卡了很久,最后发现是内核模块没加载。执行一下 sudo modprobe nvidia 就好了。
2.2.2 CUDA环境配置的坑
这里我要重点说一下CUDA版本的问题。很多新手会问:「我宿主机装的是CUDA 11.7,容器里是不是也得装11.7?」
答案是:不一定。容器里的CUDA版本可以和宿主机不同,只要容器内的CUDA版本不高于宿主机的驱动版本就行。说白了,驱动是向下兼容的。
举个例子:宿主机驱动版本是525.60.13(支持CUDA 12.0),那容器里可以用CUDA 11.8、11.7,甚至10.2。但反过来就不行。
避坑指南: 我曾经在生产环境遇到过一个问题——容器里用的CUDA 11.8,但宿主机驱动只支持到11.4。结果模型推理时直接报「CUDA driver version is insufficient」。所以,启动容器前一定要确认驱动版本。用 nvidia-smi 查看右上角的CUDA Version,那就是驱动支持的最高版本。
常用的CUDA基础镜像有这些:
| 镜像名 | 说明 | 适用场景 |
|---|---|---|
nvidia/cuda:11.8.0-runtime-ubuntu20.04 |
仅运行时,不含开发工具 | 模型推理部署 |
nvidia/cuda:11.8.0-devel-ubuntu20.04 |
包含开发工具(nvcc等) | 需要编译CUDA代码 |
nvidia/cuda:11.8.0-base-ubuntu20.04 |
最小化安装 | 自定义构建 |
我个人习惯,部署场景用runtime就够了。devel镜像太大,动不动就3、4个G,没必要。
2.3 Python虚拟环境管理
说完Docker,再聊聊Python虚拟环境。你可能会问:「有了Docker,还需要虚拟环境吗?」
我的答案是:需要。Docker解决的是系统级依赖的隔离,而虚拟环境解决的是Python包级别的隔离。两者是互补关系,不是替代关系。
举个例子:你在一个项目里用PyTorch 1.12,另一个项目用PyTorch 2.0。如果不用虚拟环境,这两个版本会冲突。用Docker当然也能隔离,但每次改个包都要重新build镜像,太慢了。虚拟环境可以秒级切换。
2.3.1 conda vs venv vs pipenv
市面上Python虚拟环境工具很多,我简单对比一下:
| 工具 | 优点 | 缺点 | 我的推荐 |
|---|---|---|---|
| conda | 能管理非Python依赖(如CUDA、cuDNN) | 启动慢,环境文件大 | 数据科学、深度学习开发 |
| venv | Python内置,轻量 | 只能管理Python包 | 纯Python项目 |
| pipenv | 自动管理依赖树 | 速度慢,有时锁文件解析失败 | 不推荐 |
在部署场景下,我推荐用conda。为什么?因为深度学习项目经常需要特定版本的cuDNN、NCCL这些底层库,conda能一并管理。venv搞不定这个。
2.3.2 实战:用conda创建部署环境
假设我们要部署一个基于PyTorch的图像分类模型。环境要求:Python 3.9、PyTorch 1.12、CUDA 11.8。
# 创建环境
conda create -n deploy_env python=3.9
# 激活环境
conda activate deploy_env
# 安装PyTorch(注意指定CUDA版本)
conda install pytorch=1.12.0 torchvision=0.13.0 torchaudio=0.12.0 cudatoolkit=11.8 -c pytorch -c conda-forge
# 安装其他依赖
pip install flask gunicorn onnxruntime-gpu
# 导出环境配置(方便复现)
conda env export > environment.yml
小技巧: 导出环境时,我建议用 conda env export --no-builds。这样导出的yml文件不包含具体的build版本号,跨平台兼容性更好。不然你在Linux上导出的环境,到Windows上可能装不上。
2.3.3 环境管理的避坑指南
我曾经犯过一个错误:在base环境里直接装包,结果把系统Python搞坏了。从那以后,我定了个规矩——永远不要在base环境里装任何项目依赖。base环境只用来装conda本身。
另外,有个常见的坑是pip和conda混用。如果你先pip install了一个包,然后又用conda install,可能会把依赖搞乱。我的建议是:优先用conda,conda没有的再用pip。而且尽量在环境创建初期就把所有依赖定好,不要频繁切换。
最后,分享一个我常用的环境清理命令:
# 查看所有环境
conda env list
# 删除不需要的环境
conda env remove -n old_env
# 清理缓存(释放磁盘空间)
conda clean -a
嗯,这一章的内容就到这里。环境准备看起来琐碎,但真的值得花时间做好。我见过太多项目,最后上线时因为环境问题延期。把这一步做扎实了,后面的部署会顺畅很多。