项目Docker环境部署与代码测试复盘
任务:在GPU服务器上使用 Docker 搭建代码运行环境,并测试一个模型是否可以正常运行。
项目目录中包含代码压缩包、预训练模型压缩包和说明文档。目标不是立刻复现README 中的完整指标,而是先完成以下几件事:
- 理清项目目录结构;
- 解压代码和模型文件;
- 根据 README 确认环境依赖;
- 使用 Docker 创建可复现运行环境;
- 跑通数据读取、训练、模型保存和推理测试流程;
这篇文章主要记录整个 Docker 部署和排障过程,也作为一次实践复盘。
为什么要用 Docker
在深度学习这类项目中,经常会遇到一个问题:
代码本身没问题,但环境很难配。
比如一个项目可能要求:
Python == 3.9
CUDA == 12.x
PyTorch == 2.x
transformers == 4.x
scikit-learn
dpkt
libpcap
如果直接在服务器系统里安装这些依赖,可能会出现很多问题:
- 服务器上原来已经有别人的 Python 环境;
- 不同项目需要不同版本的 PyTorch;
- CUDA、驱动、PyTorch 版本可能不匹配;
- pip、conda 安装依赖容易污染系统环境;
- 这次能跑,换一台机器又跑不起来;
- 多个人共用服务器时,互相改环境容易影响别人。
Docker 的价值就在于:
把运行代码需要的系统环境、Python 环境、依赖包、启动方式封装到一个独立容器里,让代码在一个相对隔离、可复现的环境中运行。
简单说,Docker 可以让我们做到:
同一份镜像
↓
在不同机器启动相似环境
↓
减少“环境不一致”导致的问题
Docker 概念
在实际使用 Docker 之前,先理解几个基础概念。
镜像 Image
Docker 镜像可以理解为:
一个打包好的运行环境模板。
比如:
python:3.9-slim
nvidia/cuda:11.8.0-devel-ubuntu20.04
nvcr.io/nvidia/pytorch:25.03-py3
ufoym/deepo:all-jupyter
这些都是镜像。镜像里通常包含:
操作系统基础环境
Python
CUDA
PyTorch
系统依赖
部分常用工具
你可以把镜像理解成“安装包”或者“系统快照”。
但镜像本身不会运行,真正运行的是容器。
容器 Container
容器是由镜像启动出来的运行实例。
比如有一个镜像:
ufoym/deepo:all-jupyter
用下面命令启动:
docker run -it ufoym/deepo:all-jupyter bash
就会进入一个容器环境。可以理解为:
镜像 = 模板
容器 = 根据模板启动出来的运行环境
类似于:
类 → 对象
镜像 → 容器
一个镜像可以启动多个容器,每个容器之间相互隔离。
Dockerfile
Dockerfile 是用来描述如何构建镜像的文件。例如:
FROM python:3.9-slim
WORKDIR /workspace/project
RUN pip install torch transformers scikit-learn
CMD ["bash"]
它表示:
- 基于
python:3.9-slim镜像; - 设置工作目录为
/workspace/project; - 安装 Python 依赖;
- 默认启动 bash。
然后通过:
docker build -t my-project:latest .
就可以构建出一个新镜像。
docker build 和 docker run
这两个命令很重要。docker build用于根据 Dockerfile 构建镜像:
docker build -f Dockerfile.gpu -t traffic-test:py39 .
含义:
-f Dockerfile.gpu 指定 Dockerfile 文件
-t traffic-test:py39 给镜像起名字和标签
. 当前目录作为构建上下文
docker run用于根据镜像启动容器:
docker run -it --rm traffic-test:py39 bash
含义:
-it 交互式终端
--rm 容器退出后自动删除
bash 进入 bash 命令行
挂载目录 Volume
容器内部和宿主机是隔离的。如果代码在宿主机:
/home/user/project
容器里默认看不到这个目录。所以需要用 -v 做挂载:
docker run -it --rm \
-v /home/user/project:/workspace/project \
-w /workspace/project \
image_name \
bash
意思是:
把宿主机 /home/user/project
挂载到容器里的 /workspace/project
这样容器里就可以访问宿主机代码。
本次实践中,项目目录通过类似方式挂载:
-v /project_root/work:/workspace
-w /workspace/code
这样容器里看到的 /workspace/code 实际上就是宿主机上的项目代码目录。
GPU 容器
普通 Docker 容器默认不能直接使用 GPU。
如果要在容器里使用 NVIDIA GPU,需要服务器安装 NVIDIA 驱动和 nvidia-container-runtime,然后启动容器时加:
--gpus all
或者指定某一张 GPU:
--gpus '"device=1"'
例如:
docker run -it --rm \
--gpus '"device=1"' \
image_name \
bash
进入容器后,可以用下面命令验证 GPU 是否可用
python -c "import torch; print(torch.cuda.is_available())"
如果输出:
True
说明 PyTorch 在容器中可以调用 GPU。
Docker 网络
Docker 默认使用 bridge 网络。
有些服务器环境里,Docker 默认网络可能无法正常解析域名,比如:
Temporary failure resolving 'archive.ubuntu.com'
Temporary failure resolving 'security.ubuntu.com'
这时可以尝试使用宿主机网络:
--network host
例如:
docker build --network=host -f Dockerfile.gpu -t image_name .
或者:
docker run -it --rm --network host image_name bash
本次实践中,Docker build 过程中就遇到了默认 bridge 网络 DNS 解析失败,最终通过 --network=host 定位问题。
docker commit
docker commit 可以把一个正在运行的容器保存成新的镜像。
比如你进入容器后,手动安装了一些依赖、修改了一些环境,然后想把当前状态保存下来,就可以在宿主机执行:
docker commit 容器名 新镜像名:标签
例如:
docker commit traffic_test traffic-test:debug
以后就可以直接用这个新镜像启动,不用重复安装依赖。注意:
docker commit要在宿主机执行,不是在容器内部执行。
宿主机提示符一般类似:
user@server:~$
容器内部提示符一般类似:
root@container:/workspace/project#
2.接下来实践:了解初始目录结构
/project_root
├── code_package
│ └── code.tar.gz
└── pretrained_model
├── pretrained_model.tar.gz
└── 说明文档.pdf
第一步不是直接写 Dockerfile,而是先查看压缩包内部结构。
tar -tzf code_package/code.tar.gz | head -50
tar -tzf pretrained_model/pretrained_model.tar.gz | head -50
通过查看发现,代码包中包含:
README.md
说明文档.pdf
process_pcap.py
process_txt.py
data_process_multiview.py
count_classes.py
inference/
models/
uer/
模型包中包含预训练模型、标签文件和数据文件。
为了避免污染原始目录,我新建了一个 work 目录用于解压和测试:
mkdir -p work
tar -xzf code_package/code.tar.gz -C work
tar -xzf pretrained_model/pretrained_model.tar.gz -C work
3. 阅读 README,确认环境要求
进入代码目录后,先查看 README:
cd work/code
sed -n '1,200p' README.md
README 中给出的环境要求大致如下:
Python == 3.9
CUDA == 12.x
torch == 2.x
transformers == 4.x
scikit-learn
scipy
dpkt
同时 README 中给出了完整的数据处理和测试流程。
4. 确认数据和模型文件
在正式构建 Docker 环境之前,需要确认代码中是否已经包含测试数据。
find work/code/inference -maxdepth 3 -type f | head -100
可以看到类似文件:
processed_data/train.txt
processed_data/valid.txt
processed_data/encrypted_test.txt
processed_data/plaintext_test.txt
processed_data/attack_encrypted_open.txt
processed_data/attack_plaintext_open.txt
继续查找模型文件:
find work -name "*.bin" -o -name "*.pt" -o -name "*.pth" -o -name "*.ckpt"
发现主要模型包括:
models/pre-trained_model.bin
pretrained_model/dataset_xxx.pt
其中:
pre-trained_model.bin是预训练模型;- 后续训练会生成一个 fine-tuned 模型;
5. 检查服务器 Docker 和 GPU
在宿主机上检查 Docker:
docker --version
docker ps
检查 GPU:
nvidia-smi
确认服务器上有 NVIDIA GPU,并且驱动版本支持当前 CUDA。之后还需要验证 Docker 容器能否访问 GPU。
6. 第一次尝试:基于 CUDA 镜像写 Dockerfile
一开始根据 README 里的 CUDA 要求,尝试基于 CUDA 镜像构建 Docker 环境。
创建 requirements.txt:
cat > requirements.txt <<'EOF'
scikit-learn
scipy
torch
transformers
dpkt
numpy
tqdm
six
regex
tokenizers
packaging
matplotlib
pandas
EOF
创建 Dockerfile.gpu:
FROM nvidia/cuda:12.x-runtime-ubuntuXX.XX
ENV DEBIAN_FRONTEND=noninteractive
ENV PYTHONUNBUFFERED=1
ENV PIP_NO_CACHE_DIR=1
WORKDIR /workspace/project
RUN apt-get update && apt-get install -y \
python3.9 \
python3.9-dev \
python3.9-distutils \
python3-pip \
gcc \
g++ \
make \
libpcap-dev \
tcpdump \
iputils-ping \
net-tools \
vim \
less \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
RUN ln -sf /usr/bin/python3.9 /usr/bin/python && \
ln -sf /usr/bin/pip3 /usr/bin/pip
COPY requirements.txt /tmp/requirements.txt
RUN python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple && \
python -m pip install -r /tmp/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
CMD ["bash"]
构建镜像:
docker build -f Dockerfile.gpu -t traffic-test:cuda-py39 .
遇到的问题
构建时出现 DNS 解析失败:
Temporary failure resolving 'archive.ubuntu.com'
Temporary failure resolving 'security.ubuntu.com'
Temporary failure resolving 'developer.download.nvidia.com'
E: Unable to locate package python3.9
E: Unable to locate package gcc
E: Unable to locate package curl
初步判断原因是:Docker 默认 bridge 网络下 DNS 解析失败,导致 apt-get update 没有正常执行,后续所有软件包都无法找到。
7. 解决 Docker 构建网络问题
先测试容器在 host 网络下 DNS 是否正常:
docker run --rm --network host nvidia/cuda:12.x-runtime-ubuntuXX.XX \
bash -lc "cat /etc/resolv.conf && getent hosts archive.ubuntu.com || true"
测试发现,在 --network host 模式下可以正常解析域名。因此后续构建镜像时需要加:
--network=host
例如:
docker build --network=host -f Dockerfile.gpu -t traffic-test:cuda-py39 .
这一步解决了 Docker 默认网络 DNS 异常的问题。
8. 第二个问题:无法拉取 Python 镜像
为了避免在 CUDA Ubuntu 镜像里折腾 Python 3.9,我又尝试换成 Python 官方基础镜像:
FROM python:3.9-slim
但是构建时报错:
docker.io/library/python:3.9-slim: 403 Forbidden
也尝试过使用内部镜像仓库中的 Python 镜像,但出现匿名 token 获取失败:
failed to fetch anonymous token
因此可以确认:当前服务器无法稳定拉取外部 Docker 镜像,继续依赖远程镜像不现实。
最终策略调整为:
不再从外部拉取新镜像,而是基于服务器本地已有镜像创建环境。
9. 使用服务器本地已有镜像
查看本地镜像:
docker images | grep -E "cuda|python|pytorch|deepo|nvidia"
发现服务器上已有多个镜像
为了快速验证代码流程,先使用已有的 镜像启动容器:
docker run -it --rm \
--gpus '"device=1"' \
--network host \
--name traffic_test \
-v /project_root/work:/workspace \
-w /workspace/code \
ufoym/deepo:all-jupyter \
bash
进入容器后检查环境:
python --version
pip --version
python -c "import torch; print(torch.__version__); print(torch.version.cuda); print(torch.cuda.is_available())"
输出显示:
Python 3.8.x
torch 1.x + CUDA
True
虽然这个环境不是 README 中要求的 Python 3.9 + torch 2.x,但它至少证明:
- Docker 容器可以启动;
- 项目目录可以挂载;
- 容器内可以访问 GPU;
- 可以先用于代码流程快速验证。
10. 运行脚本
先不直接跑大模型训练,而是运行最轻量的脚本:
cd /workspace/code
python count_classes.py --paths \
./inference/processed_data/plaintext_test.txt \
./inference/processed_data/encrypted_test.txt
继续统计 open / attack 数据:
python count_classes.py --paths \
./inference/processed_data/attack_encrypted_open.txt \
./inference/processed_data/attack_plaintext_open.txt
11. 第一次运行推理脚本:缺少 fsspec
接下来运行核心脚本:
cd /workspace/code/inference
python run_classifier_multiview.py \
--seq_length 128 \
--labels_num 200 \
--threshold 1 \
--lambda_epochs 1 \
--packet_num 4 \
--packet_len 128 \
--device_id 0 \
--dataset_name dataset \
--train_dataset ./processed_data/train.txt \
--valid_dataset ./processed_data/valid.txt \
--test_dataset ./processed_data/encrypted_test.txt \
--open_dataset ./processed_data/attack_encrypted_open.txt
报错:
ModuleNotFoundError: No module named 'fsspec'
尝试使用 pip 安装:
pip install fsspec -i https://pypi.tuna.tsinghua.edu.cn/simple
但容器中访问 pip 源出现 SSL 错误:
SSLError(SSLEOFError)
RemoteDisconnected
更换清华源、阿里源、官方源后仍然失败。
12. 处理 fsspec 导入问题
通过搜索代码:
grep -R "fsspec" -n .
发现 fsspec 只在几个 opts*.py 文件第一行被导入:
from fsspec.registry import default
继续检查后发现项目并未实际使用这个 default 变量,因此判断它是无用导入。
为了继续推进测试,先备份文件,然后注释该导入:
cp uer/opts.py uer/opts.py.bak
cp uer/opts_origin.py uer/opts_origin.py.bak
cp uer/opts_multiview.py uer/opts_multiview.py.bak
cp uer/opts_twoview.py uer/opts_twoview.py.bak
sed -i '1s/^/# /' uer/opts.py
sed -i '1s/^/# /' uer/opts_origin.py
sed -i '1s/^/# /' uer/opts_multiview.py
sed -i '1s/^/# /' uer/opts_twoview.py
这一步属于临时排障处理,正式环境中更推荐通过固定 requirements 或内部 pip 源安装依赖。
13. 第二次运行:fine-tuned 模型缺失
再次运行脚本后,程序可以正常读取数据:
total: 60579
total: 19623
total: 7863
total: 300000
Batch size: 1024
The number of training instances: 20
Test set evaluation.
但随后报错:
FileNotFoundError:
../models/dataset_finetuned_model_4packet_128bytes_multiview.bin
检查模型目录:
ls -lh models
发现只有预训练模型:
pre-trained_model.bin
没有 fine-tuned 模型。
继续查看代码:
sed -n '630,680p' inference/run_classifier_multiview.py
发现训练代码已经被注释,脚本直接进入 evaluation 阶段加载 fine-tuned 模型。
README 中有一句说明:
微调一次后可以将训练相关代码注释,后续只进行推理。
这说明当前是首次运行,fine-tuned 模型还没有生成,因此必须先恢复训练代码,跑一次微调保存模型。
14. 恢复训练代码
备份脚本:
cp inference/run_classifier_multiview.py inference/run_classifier_multiview.py.bak_before_train
恢复训练代码,即取消训练阶段的注释,使代码重新执行:
print("Start training.")
for epoch in tqdm.tqdm(range(1, args.epochs_num + 1)):
train_loss, train_acc = train_one_epoch(...)
val_acc = evaluate(...)
if val_acc > best_result:
best_result = val_acc
save_model(model, args.output_model_path)
恢复后重新运行脚本。
15. 第三次运行:CUDA OOM
恢复训练后,程序成功进入训练阶段:
Start training.
但默认 batch_size=1024 时出现显存不足:
RuntimeError: CUDA out of memory
Tried to allocate xxx MiB
查看原因:
- 当前 GPU 本身已有其他进程占用显存;
- 默认 batch size 太大;
- Transformer attention 计算显存占用较高;
- 每条样本使用多视角输入,进一步放大显存占用。
解决方案是降低 batch size:
PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128 python run_classifier_multiview.py \
--seq_length 128 \
--labels_num 200 \
--threshold 1 \
--lambda_epochs 1 \
--epochs_num 1 \
--batch_size 16 \
--packet_num 4 \
--packet_len 128 \
--device_id 0 \
--dataset_name dataset \
--train_dataset ./processed_data/train.txt \
--valid_dataset ./processed_data/valid.txt \
--test_dataset ./processed_data/encrypted_test.txt \
--open_dataset ./processed_data/attack_encrypted_open.txt
此时输出:
Batch size: 16
The number of training instances: 1263
Start training.
说明训练流程已经正常启动。
16. 快速训练完成
使用快速验证参数
epochs_num=1
lambda_epochs=1
batch_size=16
训练完成:
Start training.
100%|███████████████████████████████████████████████| 1/1 [04:08<00:00, 248.16s/it]
Test set evaluation.
Closed Test result:
Acc. (Balanced) 0.0050
Open Test result:
Acc. (Balanced) 0.0000
因此这次运行的意义是:
验证 Docker 环境、GPU、数据读取、训练循环、模型保存和测试流程均可跑通。
17. fine-tuned 模型生成
训练完成后检查模型目录:
ls -lh models | grep finetuned
可以看到生成了 fine-tuned 模型:
dataset_finetuned_model_4packet_128bytes_multiview.bin
模型大小约 1GB 以上。
这说明模型保存流程正常。
18. 只推理模式验证
生成 fine-tuned 模型后,恢复只推理版本代码:
cp inference/run_classifier_multiview.py.bak_before_train inference/run_classifier_multiview.py
再次执行测试命令。
这次日志中没有出现:
Start training.
而是直接进入:
Test set evaluation.
Closed Test result:
Acc. (Balanced) 0.0050
Open Test result:
Acc. (Balanced) 0.0000
说明:
- fine-tuned 模型可以被正常加载;
- 只推理流程跑通;
- 从训练、模型保存到推理测试的完整链路已验证完成。
19. 固化当前 Docker 环境
由于当前容器中已经做过一些临时环境调整和代码修改,为避免退出容器后环境丢失,在宿主机中执行:
docker ps | grep traffic_test
找到容器名后执行:
docker commit traffic_test traffic-test:debug
输出类似:
sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
之后可以通过以下命令复用环境:
docker run -it --rm \
--gpus '"device=1"' \
--network host \
--name traffic_test \
-v /project_root/work:/workspace \
-w /workspace/code \
traffic-test:debug \
bash
这一步完成了当前可运行环境的固化。
20. 当前阶段结论
截至目前,已经完成以下工作:
- 完成项目目录梳理和代码/模型解压;
- 确认核心脚本;
- 确认数据集;
- 确认 Docker 默认网络存在 DNS 问题,构建时需要使用
--network=host; - 确认服务器无法稳定拉取外部 Python 镜像,改用本地已有镜像;
- 确认容器中 GPU 可用;
- 处理了
fsspec缺失问题; - 处理了 fine-tuned 模型缺失问题;
- 处理了默认 batch size 导致 OOM 的问题;
- 完成 1 epoch 快速训练;
- 成功生成 fine-tuned 模型;
- 成功跑通只推理测试流程;
- 已将当前容器固化为可复用镜像。
22. 这次实践中的几个 Docker 经验
1. 不要一开始就写复杂 Dockerfile
深度学习项目环境复杂,建议先确认:
README 环境要求
代码入口
数据路径
模型路径
服务器 GPU 状态
本地已有镜像
再决定 Dockerfile 方案。
2. Docker build 失败不一定是 Dockerfile 写错
本次主要问题其实是:
DNS 解析失败
镜像仓库访问受限
pip 源 SSL 异常
而不是代码本身错误。
3. --network=host 是内网服务器排障常用手段
当 Docker build 中出现:
Temporary failure resolving ...
可以先测试:
docker run --rm --network host 镜像 bash -lc "getent hosts archive.ubuntu.com"
如果 host 网络可用,则构建时可以加:
docker build --network=host ...
4. 优先利用本地已有镜像
当外网镜像拉取失败时,继续尝试不同远程镜像意义不大。更实际的做法是:
docker images
看看本地已有 PyTorch、CUDA、deepo、NGC 镜像,先基于可用镜像跑通流程。
5. 先跑轻量脚本,再跑训练脚本
不要一开始就跑大模型训练。推荐顺序是:
python --version
torch.cuda.is_available()
count_classes.py
小 batch / 1 epoch 训练
只推理模式
完整训练配置
6. 训练结果低不一定是模型问题
快速验证时为了节省时间和避免 OOM,经常会使用:
epochs_num=1
batch_size=16
这种配置的结果只能说明流程是否跑通,不能用于评价模型性能。
24. 总结
这次 Docker 环境部署的核心收获是:深度学习代码的环境问题往往不是单一依赖安装问题,而是由服务器网络、镜像仓库、GPU 显存、代码路径、模型文件和数据格式共同决定的。
先理解目录和 README
再确认数据和模型
再测试 Docker / GPU
远程镜像失败后改用本地已有镜像
先跑轻量脚本验证数据
再处理依赖和源码小问题
恢复训练生成 fine-tuned 模型
降低 batch size 解决 OOM
最后完成训练、推理和镜像固化更多推荐



所有评论(0)