二、环境准备:Docker基础、Dockerfile编写、Docker Compose编排、GPU驱动与CUDA环境配置

好,咱们正式开始动手了。这一章是地基,地基不稳,后面全白搭。我见过太多同学在模型部署时被环境问题折磨得死去活来——说白了,就是Docker没玩明白,CUDA版本对不上,驱动没装好。今天咱们就把这些坑一个个填平。

2.1 Docker基础:为什么非它不可?

你想想看,模型部署最头疼的是什么?是「在我电脑上跑得好好的,到你那就崩了」。Docker就是来解决这个问题的。它把你的整个运行环境——操作系统、依赖库、Python版本、CUDA工具包——全部打包成一个镜像。别人拿到这个镜像,一键就能跑起来。

核心概念速览:

  • 镜像(Image):一个只读的模板,包含运行环境的所有文件。好比一个ISO安装盘。
  • 容器(Container):镜像运行起来的实例。你可以启动、停止、删除它。好比从ISO盘安装好的操作系统。
  • 仓库(Repository):存放镜像的地方。Docker Hub就是最大的公共仓库。

我个人习惯把镜像比作「类」,容器比作「对象」。你写一个类,可以new出无数个对象。同理,你构建一个镜像,可以启动无数个容器,互不干扰。

小技巧:刚接触Docker时,别急着背命令。先记住三板斧:docker pull(拉镜像)、docker run(跑容器)、docker ps(看容器)。这三个命令能覆盖你80%的日常操作。

2.2 Dockerfile编写:从零构建你的部署镜像

Dockerfile就是一份「烹饪说明书」。你告诉Docker:先装什么系统,再装什么依赖,最后放什么代码。它照着做,生成一个镜像。

我在项目中遇到过最典型的错误:把Dockerfile写得又大又慢。每次改一行代码,都要重新下载所有依赖,等得让人抓狂。后来我学乖了——把不常变的东西放在前面,常变的东西放在后面。这样Docker可以利用缓存,只重新构建变化的部分。

来看一个实际可用的Dockerfile,专门为模型部署准备的:

# 基础镜像:选择CUDA 11.8 + Ubuntu 20.04
FROM nvidia/cuda:11.8.0-cudnn8-devel-ubuntu20.04

# 设置工作目录
WORKDIR /app

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    python3.9 \
    python3-pip \
    git \
    wget \
    && rm -rf /var/lib/apt/lists/*

# 设置Python软链接
RUN ln -s /usr/bin/python3.9 /usr/bin/python

# 先复制requirements.txt,利用缓存
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 再复制项目代码(这部分经常变)
COPY . .

# 暴露端口
EXPOSE 8000

# 启动命令
CMD ["python", "app.py"]

注意:千万别把COPY . .写在前面!否则每次代码变动,Docker都会重新执行后面的所有步骤,包括安装依赖。我曾经因为这个低级错误,浪费了整整一个下午的构建时间。

2.3 Docker Compose编排:多容器协同作战

一个完整的模型部署系统,往往不止一个服务。比如:模型推理服务 + Redis缓存 + Nginx反向代理。你难道要手动启动三个容器?还要操心它们之间的网络怎么连?

Docker Compose就是干这个的。你写一个docker-compose.yml文件,把所有的服务、网络、卷都定义好。然后一行命令docker-compose up,全部启动。

来看一个实际例子:

version: '3.8'

services:
  # 模型推理服务
  model-server:
    build: ./model
    ports:
      - "8000:8000"
    volumes:
      - ./models:/app/models
    environment:
      - CUDA_VISIBLE_DEVICES=0
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  # Redis缓存
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data

  # Nginx反向代理
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - model-server

volumes:
  redis-data:

避坑指南:我曾经在depends_on上吃过亏。它只保证容器启动顺序,不保证服务已经就绪。比如model-server启动了,但模型还没加载完,nginx就开始转发请求了。解决方案是加一个healthcheck,或者用wait-for-it.sh脚本等待端口可用。

2.4 GPU驱动与CUDA环境配置:让Docker用上显卡

模型部署离不开GPU。但Docker默认是看不到显卡的。你需要做两件事:

  1. 宿主机安装NVIDIA驱动——这是基础,驱动都没装,啥都白搭。
  2. 安装NVIDIA Container Toolkit——这是桥梁,让Docker容器能调用宿主的GPU。

安装步骤其实很简单:

# 1. 检查宿主机驱动
nvidia-smi
# 你应该能看到GPU信息,比如RTX 3090,驱动版本525.x

# 2. 安装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

# 3. 重启Docker服务
sudo systemctl restart docker

# 4. 验证:跑一个测试容器
docker run --rm --gpus all nvidia/cuda:11.8.0-base nvidia-smi

关键点:

  • 宿主机驱动版本必须 >= 容器内CUDA版本要求的驱动版本。比如CUDA 11.8要求驱动版本 >= 520.x。
  • 容器内的CUDA版本可以低于宿主机驱动支持的版本,但不能高于。
  • --gpus all参数让容器使用所有GPU,或者--gpus '"device=0,1"'指定特定GPU。

嗯,这里要注意一个常见的坑:很多人以为装了nvidia-docker2就万事大吉了。其实你还需要在Docker Compose里显式声明GPU资源。就像上面那个docker-compose.yml里写的deploy.resources.reservations.devices那段。不加的话,容器里是检测不到GPU的。

2.5 实战:一键部署你的第一个模型

理论说完了,咱们来点实际的。假设你有一个PyTorch模型,想用Flask提供REST API服务。咱们用Docker把它打包部署。

项目结构长这样:

my-deploy-project/
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── app.py
└── models/
    └── model.pth

requirements.txt内容:

flask==2.3.3
torch==2.0.1
numpy==1.24.3

app.py核心代码:

from flask import Flask, request, jsonify
import torch
import numpy as np

app = Flask(__name__)

# 加载模型
model = torch.jit.load('models/model.pth')
model.eval()

@app.route('/predict', methods=['POST'])
def predict():
    data = request.json['data']
    tensor = torch.tensor(np.array(data), dtype=torch.float32)
    with torch.no_grad():
        output = model(tensor)
    return jsonify({'prediction': output.tolist()})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)

然后执行:

# 构建并启动
docker-compose up --build

# 测试
curl -X POST http://localhost:8000/predict \
  -H "Content-Type: application/json" \
  -d '{"data": [[1.0, 2.0, 3.0]]}'

个人经验:第一次构建会很慢,因为要下载CUDA基础镜像(大概3-4GB)。但别急,第二次构建就快了,因为Docker会缓存基础层。我建议你把这个基础镜像nvidia/cuda:11.8.0-cudnn8-devel-ubuntu20.04提前docker pull下来,省得构建时干等。

好了,环境准备这块就讲到这里。你可能会觉得内容有点多,但相信我——把这些基础打扎实了,后面30个章节你会走得非常顺畅。下一章咱们开始讲模型转换和优化,那才是真正有意思的部分。