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

嗯,这一章的内容就到这里。环境准备看起来琐碎,但真的值得花时间做好。我见过太多项目,最后上线时因为环境问题延期。把这一步做扎实了,后面的部署会顺畅很多。