4. 容器化基础:Docker安装与配置、常用命令、镜像管理
4.1 为什么AI训练集群需要容器化
在AI训练与推理集群的部署中,环境一致性是最令人头疼的问题之一。训练脚本依赖特定版本的CUDA、cuDNN、Python解释器以及PyTorch/TensorFlow框架,而集群中不同节点可能预装了不同的驱动版本或系统库。若直接在各节点上手动安装依赖,极易出现“在我机器上能跑,到节点上就报错”的窘境。
Docker通过镜像(Image)与容器(Container)的隔离机制,将应用及其全部依赖打包成一个可移植的单元。镜像是一个只读模板,容器则是镜像的运行实例。在AI集群中,我们通常为每个训练任务构建专属镜像,并在任意节点上以相同方式启动容器,从而彻底消除环境差异。
核心价值:容器化使得AI集群的节点可以看作“无状态的计算资源池”,任何节点都能随时运行任意训练任务,无需预先安装特定版本的CUDA或Python库。这为后续的集群调度(如Kubernetes)提供了基础。
4.2 Docker安装与配置
在AI集群节点(通常为Ubuntu 20.04/22.04)上安装Docker,推荐使用官方脚本或apt源安装。安装完成后,必须进行两项关键配置:将当前用户加入docker组(避免每次sudo)以及配置nvidia-container-runtime(使容器能访问GPU)。
# 1. 安装Docker(使用官方apt源)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io
# 2. 将当前用户加入docker组(重新登录生效)
sudo usermod -aG docker $USER
# 3. 安装NVIDIA容器工具包
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 update && sudo apt install -y nvidia-container-toolkit
# 4. 配置Docker使用NVIDIA runtime并重启
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
# 5. 验证GPU可用性
docker run --rm --gpus all nvidia/cuda:12.2-base nvidia-smi
验证要点:执行最后一条命令后,若能看到GPU的nvidia-smi输出,说明Docker已能正确调用GPU。若报错 could not select device driver,请检查第3、4步是否执行成功。
4.3 常用Docker命令与镜像管理
在AI集群运维中,最常用的命令集中在镜像管理与容器生命周期两方面。镜像管理的关键操作包括构建、拉取、标记与推送;容器操作则围绕运行、查看日志、进入容器与清理展开。
| 命令 | 用途 | AI集群典型场景 |
|---|---|---|
docker build -t myimg:tag . |
从Dockerfile构建镜像 | 将训练代码+依赖打包为镜像 |
docker pull pytorch/pytorch:2.1.0-cuda12.2-cudnn8 |
拉取官方预置镜像 | 获取带CUDA的PyTorch基础镜像 |
docker tag src:tag registry:5000/dest:tag |
给镜像打标签 | 标记后推送至私有仓库 |
docker push registry:5000/myimg:tag |
推送镜像到仓库 | 共享镜像给集群其他节点 |
docker run --gpus all -it --rm --name train1 -v /data:/data myimg:tag python train.py |
启动GPU容器并挂载数据 | 启动训练任务,数据目录挂载 |
docker exec -it train1 bash |
进入运行中的容器 | 调试训练进程或查看中间结果 |
docker logs train1 |
查看容器日志 | 检查训练输出与错误信息 |
docker ps -a |
列出所有容器 | 查看已退出容器的状态码 |
docker rm $(docker ps -aq) |
清理所有停止的容器 | 释放磁盘空间 |
docker rmi myimg:tag |
删除本地镜像 | 清理不再使用的镜像 |
4.4 编写高效的Dockerfile(AI训练场景)
一个典型的AI训练镜像Dockerfile需要关注层缓存与CUDA依赖。建议以官方 pytorch/pytorch 或 nvidia/cuda 镜像为基础,避免重复安装CUDA。
# 基于PyTorch官方GPU镜像
FROM pytorch/pytorch:2.1.0-cuda12.2-cudnn8-runtime
# 设置工作目录
WORKDIR /workspace
# 先复制依赖清单,利用层缓存
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 再复制训练代码
COPY ./train.py .
COPY ./models/ ./models/
# 设置默认启动命令
CMD ["python", "train.py"]
常见误区:不要将 COPY . . 放在Dockerfile前部,否则任何代码改动都会使后续所有层缓存失效,导致每次构建都需重新安装依赖。应遵循“先复制依赖清单,再复制源码”的顺序。
4.5 镜像仓库与集群共享
在AI训练集群中,通常需要搭建私有镜像仓库(如Harbor或Registry)来分发镜像。各节点从私有仓库拉取镜像,避免每台节点重复构建。配置私有仓库时,需要在每台节点的Docker守护进程中添加 insecure-registries 或配置TLS证书。
# 编辑 /etc/docker/daemon.json
{
"insecure-registries": ["registry.internal:5000"],
"default-runtime": "nvidia",
"runtimes": {
"nvidia": {
"path": "nvidia-container-runtime",
"runtimeArgs": []
}
}
}
# 重启Docker使配置生效
sudo systemctl restart docker
# 推送镜像到私有仓库
docker tag pytorch/pytorch:2.1.0-cuda12.2-cudnn8-runtime registry.internal:5000/ai/train-base:2.1.0
docker push registry.internal:5000/ai/train-base:2.1.0
4.6 案例:容器化一个分布式训练任务
假设我们需要在集群上运行一个基于PyTorch的分布式训练任务,使用2个GPU节点。通过容器化,我们可以确保两个节点使用完全相同的环境。
- 构建镜像:在管理节点编写Dockerfile并构建,包含训练脚本、数据集路径配置。
- 推送镜像:将镜像推送至私有仓库
registry.internal:5000/ai/bert-train:v1。 - 在计算节点启动容器:每个节点执行
docker run,通过环境变量传入节点角色(master/worker)和通信地址。
# 节点1(master)
docker run --gpus all --rm --network host \
-e MASTER_ADDR=192.168.1.10 -e MASTER_PORT=29500 \
-e RANK=0 -e WORLD_SIZE=2 \
-v /mnt/data:/data \
registry.internal:5000/ai/bert-train:v1
# 节点2(worker)
docker run --gpus all --rm --network host \
-e MASTER_ADDR=192.168.1.10 -e MASTER_PORT=29500 \
-e RANK=1 -e WORLD_SIZE=2 \
-v /mnt/data:/data \
registry.internal:5000/ai/bert-train:v1
使用 --network host 模式可避免端口映射带来的通信延迟,适合分布式训练中高频的NCCL通信。
4.7 误区与复盘
在容器化实践中,以下问题最为常见:
- 容器内CUDA版本与宿主机驱动不匹配:容器内的CUDA运行时版本不能高于宿主机驱动支持的版本。例如宿主机驱动为535(支持CUDA 12.2),容器内不能使用CUDA 12.3。解决方法是使用
nvidia-smi查看驱动支持的CUDA版本,并选择匹配的基础镜像。 - 数据卷权限问题:容器内以root运行,写入挂载目录的文件属主为root,导致宿主机普通用户无法删除。建议在Dockerfile中创建与宿主机UID相同的用户,或使用
--user $(id -u):$(id -g)启动容器。 - 镜像体积过大:包含完整CUDA工具链的镜像可能超过5GB。建议使用
runtime版本镜像而非devel版本,并清理pip缓存。 - 忘记清理无用镜像与容器:长时间运行的集群节点会积累大量
<none>悬空镜像和已退出容器,占用磁盘空间。建议定期执行docker system prune -a(注意会删除未使用的镜像)。