测试环境:两台 Windows 11 主机,每台配备一张 NVIDIA GeForce RTX 5080。
1. 准备 Windows 11 与 WSL 2 环境
在两台主机上分别完成以下操作。
-
打开 任务管理器 → 性能 → CPU,确认 CPU 虚拟化已启用。
-
在命令行中检查 WSL 状态:
powershellwsl --status -
若提示"WSL 2 无法启动,因为此计算机上未启用虚拟化",执行:
powershellwsl.exe --install --no-distribution执行完成后重启主机。
-
重启后再次验证:
powershellwsl --status -
确保已安装 Linux 发行版,例如 Ubuntu。
2. 准备 Docker 与网络环境
2.1 安装 Docker
在 Ubuntu 中执行:
bash
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fL \
https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
ls -lh /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" |
sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt update
sudo apt install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
修复 Docker 用户权限
如果执行 docker ps 时出现以下错误,但使用 sudo docker ps 可以正常运行:
text
permission denied while trying to connect to the docker API at unix:///var/run/docker.sock
执行以下命令,将当前用户加入 docker 用户组:
bash
getent group docker || sudo groupadd docker
sudo usermod -aG docker "$USER"
newgrp docker
2.2 配置 WSL 镜像网络
为 Ubuntu 配置镜像网络,使 Windows 主系统与 WSL 使用一致的网络地址,便于后续搭建集群。
在 Windows PowerShell 中执行:
powershell
notepad $env:USERPROFILE\.wslconfig
写入以下配置:
ini
[wsl2]
networkingMode=mirrored
dnsTunneling=true
autoProxy=true
[experimental]
hostAddressLoopback=true
保存文件后,在 PowerShell 中关闭 WSL:
powershell
wsl --shutdown
重新启动 Ubuntu,使配置生效。
2.3 配置本地镜像库
配置本地镜像库,以便各节点拉取已经构建完成的训练镜像。镜像构建文件参见本文附录。
3. 准备工作目录与 SSH 密钥
以下目录仅为示例,可根据实际挂载方式调整:
bash
mkdir -p \
app \
data \
output \
.cache/rfdetr-models \
"$HOME/.rfdetr-cluster"
目录用途如下:
| 目录 | 用途 |
|---|---|
app |
存放训练与执行脚本 |
data |
存放训练数据 |
output |
存放训练输出、检查点等文件 |
.cache/rfdetr-models |
缓存模型预训练权重 |
$HOME/.rfdetr-cluster |
存放 SSH 密钥、known_hosts 等集群访问文件 |
共享存储建议
前四个目录均可挂载到共享存储,其中
output应挂载到所有训练节点均可访问的公共存储。
3.1 在第一台主机上生成 SSH 密钥
bash
ssh-keygen \
-t ed25519 \
-N "" \
-f "$HOME/.rfdetr-cluster/id_ed25519"
touch "$HOME/.rfdetr-cluster/known_hosts"
chmod 700 "$HOME/.rfdetr-cluster"
chmod 600 "$HOME/.rfdetr-cluster/id_ed25519"
chmod 644 "$HOME/.rfdetr-cluster/id_ed25519.pub"
chmod 644 "$HOME/.rfdetr-cluster/known_hosts"
3.2 同步密钥与目录
在第二台主机上创建相同的目录结构。
如果希望任意一台主机都能作为主节点(Master Node),将第一台主机生成的密钥对复制到第二台主机的相同路径,并保持相同的文件权限。
4. 安装 NVIDIA Container Toolkit
在两台主机的 Ubuntu 环境中分别执行以下命令,以确保 Docker 容器能够访问 GPU:
bash
sudo apt-get update
sudo apt-get install -y curl gpg
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| sudo gpg --dearmor \
-o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
| sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
| sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
5. 测试节点互通
5.1 写入 SSH 主机指纹
在两台主机上分别启动容器,并将两个节点的 SSH 主机指纹写入 known_hosts:
bash
ssh-keyscan \
-p 2222 \
-H "192.168.1.xxx" "192.168.1.yyy" \
> "$HOME/.rfdetr-cluster/known_hosts"
其中:
192.168.1.xxx:第一台主机的 IP 地址;192.168.1.yyy:第二台主机的 IP 地址。
5.2 双向测试 SSH 与 GPU 访问
在 192.168.1.xxx 节点上执行:
bash
docker exec \
-u trainer \
rfdetr-node \
ssh -p 2222 192.168.1.yyy nvidia-smi -L
随后在 192.168.1.yyy 节点上执行反向测试,确保两个节点之间均可通过 SSH 访问,并能够识别远端 GPU。
6. 验证容器训练环境
进入训练容器:
bash
docker exec -it \
-u trainer \
rfdetr-node \
bash
进入容器后,执行以下脚本,验证 PyTorch、DeepSpeed、CUDA、BF16 和 GPU 矩阵运算:
bash
python - <<'PY'
import torch
import deepspeed
print("PyTorch:", torch.__version__)
print("DeepSpeed:", deepspeed.__version__)
print("PyTorch CUDA:", torch.version.cuda)
print("CUDA available:", torch.cuda.is_available())
assert torch.cuda.is_available(), "PyTorch 无法访问 CUDA"
print("GPU:", torch.cuda.get_device_name(0))
print("Compute capability:", torch.cuda.get_device_capability(0))
print("BF16 supported:", torch.cuda.is_bf16_supported())
x = torch.randn(2048, 2048, device="cuda")
y = x @ x
torch.cuda.synchronize()
print("GPU calculation: OK")
print("Result checksum:", y.sum().item())
PY
7. 执行双节点训练测试
7.1 创建 DeepSpeed Hostfile
调整好数据和输出目录的挂载位置后,在主节点容器中写入本次参与训练的节点 IP。Hostfile 中需要同时包含主节点自身。
bash
printf '%s\n' \
'192.168.1.xxx slots=1' \
'192.168.1.yyy slots=1' \
> /tmp/deepspeed-hostfile
7.2 启动训练
确保两个节点上存在完全相同的训练脚本,然后在主节点执行:
bash
deepspeed \
--hostfile /tmp/deepspeed-hostfile \
--ssh_port 2222 \
--master_addr 192.168.1.xxx \
--master_port 29500 \
/workspace/app/train_seg_cluster.py
参数说明:
| 参数 | 说明 |
|---|---|
--hostfile |
DeepSpeed 节点与 GPU 插槽配置文件 |
--ssh_port |
节点容器的 SSH 端口 |
--master_addr |
主节点 IP 地址 |
--master_port |
分布式通信端口 |
/workspace/app/train_seg_cluster.py |
训练入口脚本 |
附录
A. Dockerfile
dockerfile
FROM pytorch/pytorch:2.12.1-cuda13.0-cudnn9-devel@sha256:ac63aaae09996612bdaf12bbf6d5fe840af6bed3100d6dc15fcb5fd1f4f957c4
ARG DEBIAN_FRONTEND=noninteractive
ENV VIRTUAL_ENV=/opt/venv \
PATH=/opt/venv/bin:${PATH} \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PDSH_RCMD_TYPE=ssh
RUN apt-get update \
&& apt-get install --yes --no-install-recommends \
build-essential \
ca-certificates \
git \
iproute2 \
iputils-ping \
ninja-build \
openssh-client \
openssh-server \
pdsh \
python3-venv \
&& rm -rf /var/lib/apt/lists/*
RUN python -m venv --system-site-packages "${VIRTUAL_ENV}" \
&& "${VIRTUAL_ENV}/bin/python" -m pip install --no-cache-dir --upgrade pip \
&& DS_BUILD_OPS=0 "${VIRTUAL_ENV}/bin/python" -m pip install --no-cache-dir \
"rfdetr[train,loggers]==1.8.3" \
"pytorch-lightning==2.6.5" \
"deepspeed==0.19.3"
RUN set -eux; \
existing_group="$(getent group 1000 | cut -d: -f1 || true)"; \
if [ -z "${existing_group}" ]; then \
groupadd --gid 1000 trainer; \
elif [ "${existing_group}" != trainer ]; then \
groupmod --new-name trainer "${existing_group}"; \
fi; \
existing_user="$(getent passwd 1000 | cut -d: -f1 || true)"; \
if [ -z "${existing_user}" ]; then \
useradd --create-home --uid 1000 --gid 1000 --shell /bin/bash trainer; \
elif [ "${existing_user}" != trainer ]; then \
usermod --login trainer --home /home/trainer --move-home --shell /bin/bash "${existing_user}"; \
fi; \
passwd --delete trainer; \
install -d -m 0755 -o trainer -g trainer /workspace/app /workspace/data /workspace/output; \
install -d -m 0700 -o trainer -g trainer /home/trainer/.ssh; \
printf '%s\n' \
'Host *' \
' User trainer' \
' IdentityFile /home/trainer/.ssh/id_ed25519' \
' IdentitiesOnly yes' \
> /etc/ssh/ssh_config.d/99-cluster.conf; \
printf '%s\n' \
'PubkeyAuthentication yes' \
'PasswordAuthentication no' \
'KbdInteractiveAuthentication no' \
'PermitRootLogin no' \
'AllowUsers trainer' \
> /etc/ssh/sshd_config.d/99-cluster.conf; \
ssh-keygen -A
WORKDIR /workspace/app
B. compose.yaml
yaml
name: rfdetr-cluster
services:
node:
image: <image name>
container_name: rfdetr-node
hostname: rfdetr-node
network_mode: host
ipc: host
init: true
restart: unless-stopped
working_dir: /workspace/app
command:
- /bin/sh
- -c
- |
install -d -m 0755 -o root -g root /run/sshd
exec /usr/sbin/sshd -D -e -p 2222
environment:
HOME: /home/trainer
PDSH_RCMD_TYPE: ssh
PDSH_SSH_ARGS_APPEND: "-p 2222"
NCCL_IB_DISABLE: "1"
NCCL_SOCKET_FAMILY: AF_INET
NCCL_SOCKET_IFNAME: eth0
NCCL_DEBUG: WARN
ulimits:
memlock: -1
stack: 67108864
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
volumes:
- ./app:/workspace/app
- ./data:/workspace/data
- ./output:/workspace/output
- .cache/rfdetr-models:/home/trainer/.roboflow/models
- $HOME/.rfdetr-cluster/id_ed25519:/home/trainer/.ssh/id_ed25519
- $HOME/.rfdetr-cluster/id_ed25519.pub:/home/trainer/.ssh/authorized_keys
- $HOME/.rfdetr-cluster/known_hosts:/home/trainer/.ssh/known_hosts
C. train_seg_cluster.py
python
import os
import socket
# DeepSpeed launcher 使用 CROSS_RANK 表示节点编号;
# Lightning 默认读取 NODE_RANK 或 GROUP_RANK。
if "CROSS_RANK" in os.environ:
os.environ["NODE_RANK"] = os.environ["CROSS_RANK"]
os.environ["GROUP_RANK"] = os.environ["CROSS_RANK"]
import torch
from pytorch_lightning.strategies import DeepSpeedStrategy, StrategyRegistry
from rfdetr import RFDETRSegLarge
DATASET_DIR = "/workspace/data/task1"
OUTPUT_DIR = "/workspace/output/task1"
STRATEGY_NAME = "deepspeed_stage_0"
def main() -> None:
if not torch.cuda.is_available():
raise RuntimeError("CUDA is not available.")
StrategyRegistry.register(
STRATEGY_NAME,
DeepSpeedStrategy,
description="DeepSpeed with ZeRO Stage 0",
stage=0,
)
print(f"Using CUDA device: {torch.cuda.get_device_name(0)}")
model = RFDETRSegLarge(amp=False)
model.train(
dataset_dir=DATASET_DIR,
output_dir=OUTPUT_DIR,
epochs=100,
batch_size=1,
grad_accum_steps=4,
num_workers=2,
device="cuda",
strategy=STRATEGY_NAME,
devices=1,
num_nodes=2,
tensorboard=True,
progress_bar="tqdm",
checkpoint_interval=50,
eval_interval=1,
use_ema=False,
)
if __name__ == "__main__":
main()