关于 tao-run-on-docker
tao-run-on-docker 是用于通过 Docker 执行模型和数据工作流的 TAO 技能,面向单节点 GPU 环境。它统一了本地及 SSH 连接的 Docker 守护进程上的提交、状态查询、日志和取消操作。
主要功能
- 01四种作业生命周期操作
- 02本地及 SSH 连接的 Docker
- 03GPU 选择与容器配置
- 04绑定挂载或 S3 数据暂存
- 05作业记录与状态映射
适用场景
- 01在本地 GPU 主机运行模型工作流
- 02向远程 GPU 主机提交作业
- 03查看日志和状态或取消作业
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ nvidia/ skills --skill tao-run-on-docker安装后在 Claude Code 中输入/tao-run-on-docker确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
BENCHMARK.mdeval.configskill-card.mdskill.oms.sigconfig/skillspector-baseline.yamlevals/evals.jsonreferences/skill_info.yaml详细介绍
NVIDIA GPU 工作负载的 Docker
单独安装? 如果此会话并非由 TAO skill bank 插件初始化,请先运行
tao-setup技能(主机预检、凭据、跨技能发现)。
Docker 执行平台:一个使用方,通过 Docker CLI 实现四个动词(submit/status/logs/cancel),运行模型/数据技能的 spec-bundle。它可运行在本地守护进程上,也可通过 DOCKER_HOST=ssh:// 在远程 GPU 主机上。这些动词(§ Execution)基于本文其余部分介绍的 Docker 约定——GPU 标志、挂载、NGC 身份验证、检查和错误模式——这些内容是模型/数据技能所依赖的实现方式。仅支持单节点;多节点请使用 SLURM 或 Kubernetes。
来源:Docker 官方 CLI 参考(https://docs.docker.com/reference/cli/docker/)和 NVIDIA Container Toolkit 文档。
前置条件
- 主机 GPU 运行时 — 默认要求 NVIDIA 驱动程序
>=580、CUDA Toolkit>=13.0,以及 NVIDIA Container Toolkit>=1.19.0。如果所选模型的references/skill_info.yaml声明了runtime_requirements.gpu_host,请改为将这些值传递给tao-setup-nvidia-gpu-host。模型要求会覆盖该工作流的默认值。 - Docker —
docker --version必须返回 ≥ 20.10。安装地址:https://docs.docker.com/engine/install/。 - NGC API 密钥,用于拉取
nvcr.io/*。获取地址:https://ngc.nvidia.com/。
set -a; source /path/to/.env; set +a # omit if already exported
SB="${TAO_SKILL_BANK_PATH:-${TAO_SKILL_BANK_ROOT:-$PWD}}"
SETUP_SCRIPT="${SB}/skills/platform/tao-setup-nvidia-gpu-host/scripts/setup-nvidia-gpu-host.sh"
bash "$SETUP_SCRIPT" --backend docker --check-only || {
echo "MISSING: TAO GPU host runtime is not ready."
echo "After user approval, run (append --yes for non-interactive agent runs):"
echo " bash \"$SETUP_SCRIPT\" --backend docker --install"
exit 1
}
docker --version
docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi
[ -n "$NGC_KEY" ] || echo "NGC_KEY unset — cannot pull nvcr.io images"
如果所选模型声明了 runtime_requirements.gpu_host,请将对应的 --min-driver-version、--min-cuda-version 和 --min-container-toolkit-version 值追加到检查命令和任何获准的安装命令中。不要将一个模型的覆盖值用于无关工作流。
NGC 身份验证
set -a; source /path/to/.env; set +a # omit if already exported
echo "$NGC_KEY" | docker login nvcr.io -u '$oauthtoken' --password-stdin
此设置会在重启后保留于 ~/.docker/config.json 中。遇到 unauthorized 错误时请重新运行。
执行 — 四个动词
运行 spec-bundle 时,只能通过这四个动词操作,并且只能修改作业记录。状态值采用 tao-artifacts 中固定的词汇表(PENDING RUNNING COMPLETE ERROR CANCELED UNKNOWN);下方列出了原生 Docker 状态的映射,并会在状态转换的 message 中保留原始状态。$BANK = ${TAO_SKILL_BANK_PATH}。
submit
- 暂存输入,使用
tao-data-io:它会选择存储层级,并返回挂载参数和计算帧路径。Docker 通常使用层级 A(绑定挂载主机目录,-v /host/data:/data),也可使用层级 C(传递 S3 凭据,由容器获取数据)。在<stage>/spec.yaml中编写 spec 文件,并填入这些计算帧路径。 - 检查组装好的命令 —
redact_secrets.py lint必须通过(不得包含内联密钥;凭据需通过不带值的-e VAR传递)。 - 打开记录 — 此步骤会生成 ID,并在启动前绑定
results_dir:JOB_ID=$("$BANK/scripts/tao_job_record.py" open \ --platform docker --image "$IMAGE" \ --network-arch "$ARCH" --action "$ACTION" \ --storage-tier "$TIER" --results-root "$RESULTS_ROOT") - 以分离模式启动,使用 ID 为容器命名,以便其他动词能够找到它(不要启用
--rm,以便退出的容器仍可检查):set -a; source /path/to/.env; set +a # omit if already exported CID=$(docker run -d --name "$JOB_ID" --label "tao-job=$JOB_ID" \ --gpus "$GPUS" --shm-size=8g \ -v "$STAGE:/workspace" \ -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e HF_TOKEN -e NGC_KEY \ "$IMAGE" <bundle command, reading /workspace/spec.yaml>) - 记录 RUNNING:
"$BANK/scripts/tao_job_record.py" mark "$JOB_ID" --state RUNNING --backend-ref "$CID"。
如果提交时跳过步骤 3,就没有 ID,因此无法启动——这就是先记录再启动的不变量。
status
read -r st code < <(docker inspect --format '{{.State.Status}} {{.State.ExitCode}}' "$JOB_ID" 2>/dev/null) || st=missing
| docker 状态 | 词汇表状态 |
|---|---|
created / restarting | PENDING |
running / paused | RUNNING |
exited,代码为 0 | COMPLETE |
exited,代码 ≠ 0 | ERROR |
dead / 缺失 | UNKNOWN(通过 docker ps -a 确认) |
处于终态时,使用 mark 记录;对于层级 C,请在执行 docker rm 之前使用 tao-data-io 上传结果(容器是唯一副本)。
logs
docker logs --tail "${N:-200}" "$JOB_ID" # add -f to follow in-turn
cancel
docker rm -f "$JOB_ID"
"$BANK/scripts/tao_job_record.py" mark "$JOB_ID" --state CANCELED --source agent
本地与远程(DOCKER_HOST)
没有单独的“远程 Docker”——将守护进程指向可通过 SSH 访问的主机:
export DOCKER_HOST=ssh://user@gpu-host。上文中的每个动词都完全相同;
docker CLI 会通过 SSH 封送请求(复用你的密钥,避免对命令进行嵌套引号转义)。由此带来的一个结果是:-v 绑定挂载的源路径指向的是
远程主机的文件系统,而不是启动器所在主机——请在那里准备数据(A 类),
或在容器内获取数据(C 类)。仅支持 sudo docker 的主机可使用备用方式:
ssh host 'sudo docker …'(技能相同,前缀不同)。
docker run — 标准标志
set -a; source /path/to/.env; set +a # omit if already exported
HOST_RESULTS=/host/results
HOST_UID="$(id -u)"
HOST_GID="$(id -g)"
HOST_USER_NAME="$(id -un)"
[ "$HOST_UID" -ne 0 ] || { echo "Refusing writable Docker launch as UID 0" >&2; exit 1; }
HOST_IDENTITY_ARGS=(--user "$HOST_UID:$HOST_GID")
for group_id in $(id -G); do
[ "$group_id" = "$HOST_GID" ] || HOST_IDENTITY_ARGS+=(--group-add "$group_id")
done
mkdir -p "$HOST_RESULTS/.tao-runtime/home/.cache"/{huggingface,torch,triton,torchinductor,matplotlib}
docker run \
--gpus all \
--rm \
--shm-size=8g \
"${HOST_IDENTITY_ARGS[@]}" \
-v /host/data:/data \
-v "$HOST_RESULTS:/results" \
-e HOME=/results/.tao-runtime/home \
-e USER="$HOST_USER_NAME" -e LOGNAME="$HOST_USER_NAME" \
-e XDG_CACHE_HOME=/results/.tao-runtime/home/.cache \
-e HF_HOME=/results/.tao-runtime/home/.cache/huggingface \
-e TORCH_HOME=/results/.tao-runtime/home/.cache/torch \
-e TRITON_CACHE_DIR=/results/.tao-runtime/home/.cache/triton \
-e TORCHINDUCTOR_CACHE_DIR=/results/.tao-runtime/home/.cache/torchinductor \
-e MPLCONFIGDIR=/results/.tao-runtime/home/.cache/matplotlib \
-e HF_TOKEN -e NGC_KEY \
<image> \
<command>
说明:
--gpus '"device=0,1"'— 在任何共享主机上都按 ID 选择 GPU,而不是按数量选择(双引号转义)。按数量请求时会分配前 N 个设备,因此--gpus 1永远只能分配到 GPU 0:如果 GPU 0 正忙,所有作业都会在那里发生 OOM,而其他 GPU 却闲置;也没有办法指定其他 GPU——-e NVIDIA_VISIBLE_DEVICES会被--gpus覆盖。读取当前占用情况(nvidia-smi --query-gpu=index,memory.used --format=csv)并传入空闲 ID。ID 也可以是 GPU UUID。未安装 nvidia-container-toolkit 时会报错:could not select device driver "" with capabilities: [[gpu]]。--rm— 容器退出时清理容器;如果想在退出后使用docker logs,则省略此标志。--shm-size=8g— 否则 torchrun + PyTorch DataLoaders 会耗尽默认的 64 MB/dev/shm;请根据多 GPU 训练设置大小,如果仍然遇到Bus error,则增大(例如16g)。--user "$(id -u):$(id -g)"— 只要绑定挂载可写,默认就必须使用此标志。它可避免生成由 root 所有、提交作业的主机用户无法清理的检查点目录树。- 对标准可写绑定挂载路径,拒绝 UID
0。如果启动器本身以 root 运行,请明确获取已验证的非 root 提交用户 UID:GID;绝不要根据输出目录的所有者推断。 --group-add <gid>— 保留对共享数据集和工作区的主机辅助组访问权限。标准数组会添加主 GID 以外的所有主机组。HOME、USER、LOGNAME和缓存重定向——防止框架在用户覆盖设置后写入/root等由镜像所有的路径。启动前在可写挂载点上准备这些目录。USER/LOGNAME是必需项,而非装饰:任意--userUID 在镜像的/etc/passwd中都没有对应条目,而 torch 2.x 会在导入时调用getpass.getuser()(torch/_dynamo→ inductor 缓存目录设置)——如果这两个环境变量都未设置,容器会在任何工作负载代码运行前因KeyError: 'getpwuid(): uid not found: <uid>'崩溃。任何非空名称都符合要求;该名称无需在镜像中存在。-v host:container— 绑定挂载;命令中只引用容器路径。-e VAR— 从父 Shell 传递环境变量(如果已设置,无需指定值)。密钥请使用此形式。
容器名称冲突
如果名为 X 的容器已存在,docker run --name X 就会失败。再次使用名称前的防护模式:
docker stop my-worker 2>/dev/null; docker rm my-worker 2>/dev/null
docker run --name my-worker ...
分离运行 + exec 模式
对于在同一容器上执行多步的工作流(下载 → 运行 → 后处理),请避免重启开销:
HOST_RESULTS=/host/results
HOST_UID="$(id -u)"
HOST_GID="$(id -g)"
[ "$HOST_UID" -ne 0 ] || { echo "Refusing writable Docker launch as UID 0" >&2; exit 1; }
HOST_IDENTITY_ARGS=(--user "$HOST_UID:$HOST_GID")
for group_id in $(id -G); do
[ "$group_id" = "$HOST_GID" ] || HOST_IDENTITY_ARGS+=(--group-add "$group_id")
done
mkdir -p "$HOST_RESULTS/.tao-runtime/home/.cache"/{huggingface,torch,triton,torchinductor,matplotlib}
docker run -d --name <worker> \
--gpus all --shm-size=8g \
"${HOST_IDENTITY_ARGS[@]}" \
-v <host-data>:/data \
-v "$HOST_RESULTS:/results" \
-e HOME=/results/.tao-runtime/home \
-e USER="$(id -un)" -e LOGNAME="$(id -un)" \
-e XDG_CACHE_HOME=/results/.tao-runtime/home/.cache \
-e HF_HOME=/results/.tao-runtime/home/.cache/huggingface \
-e TORCH_HOME=/results/.tao-runtime/home/.cache/torch \
-e TRITON_CACHE_DIR=/results/.tao-runtime/home/.cache/triton \
-e TORCHINDUCTOR_CACHE_DIR=/results/.tao-runtime/home/.cache/torchinductor \
-e MPLCONFIGDIR=/results/.tao-runtime/home/.cache/matplotlib \
--entrypoint sh \
<image> -c "tail -f /dev/null"
docker exec <worker> <step_1>
docker exec <worker> <step_2>
docker stop <worker> && docker rm <worker>
缺失时拉取模式
docker image inspect <image> >/dev/null 2>&1 || docker pull <image>
用于发现的标签
为容器添加标签,以便之后按条件筛选列表:
docker run --label tao-toolkit ...
docker ps --filter 'label=tao-toolkit'
挂载模式
容器会按照镜像定义的惯用路径(通常为 /data、/results、/workspace/checkpoints)查找数据。主机侧路径可以任意指定。docker run 内的命令只引用容器路径。
可写挂载的所有权不变量
默认情况下,每个可写绑定挂载都以提交作业的宿主机 UID:GID 运行。
如果 root 容器可以创建更深层的 0755 目录,仅预先创建挂载根目录并不足够:删除操作由父目录权限控制,因此这些子树对宿主机用户来说仍会变得无法访问。
容器的 --rm 和 docker rm 只会移除容器状态;两者都不会删除或修复绑定挂载的检查点。
只有当镜像文档或预检证明以宿主机用户身份运行不兼容时,镜像才可以以 root 身份运行。将此视为明确的启动例外。隔离其可写输出,并在每次终止退出或取消后规范所有权,然后再开始另一项实验。对于包含 /bin/sh 和 chown 的镜像,运行后的修复命令为:
HOST_UID="$(id -u)"
HOST_GID="$(id -g)"
docker run --rm --user 0:0 --entrypoint /bin/sh \
-v /host/results:/owned-output \
<same-approved-image> \
-c 'chown -R "$1:$2" /owned-output' sh "$HOST_UID" "$HOST_GID"
对每个可写输出/缓存挂载应用修复。如果 Agent 无法运行或验证所有权规范化,则不得使用需要 root 的例外。切勿用 chmod 777 代替常规修复。
环境变量约定
TAO 风格工作负载常见的透传变量(调用该技能的组件会声明所需变量):
NGC_KEY— 用于从nvcr.io拉取镜像;某些运行时也会在运行时读取HF_TOKEN— 用于下载受限的 HuggingFace 模型AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_ENDPOINT_URL— 用于容器内的 S3 I/OWANDB_API_KEY— 可选的 W&B 日志记录
如果变量已存在于父 Shell 中,请使用 -e VAR(不带 =value)。避免在命令行中放置密钥。
替代 GPU 选择方式:使用 -e NVIDIA_VISIBLE_DEVICES=0,1(或 all)和 -e NVIDIA_DRIVER_CAPABILITIES=all,而不是 --gpus。在标准 x86 主机上优先使用 --gpus 标志;环境变量形式较旧,也是 runtime=nvidia(Tegra/Jetson)所需的方式。
容器检查
docker ps # running containers only
docker ps -a # all containers, including exited
docker ps --filter status=running --format '{{.Names}} {{.Image}}'
docker logs <name_or_id> # stdout/stderr
docker logs -f <name_or_id> # follow (tail -f equivalent)
docker logs --tail 100 <name_or_id> # last N lines
docker inspect <name_or_id> # full config, mounts, env, network, state (JSON)
docker inspect --format '{{.State.Status}}' <name_or_id>
docker stats # live CPU/mem/network/block I/O
docker stats --no-stream # one snapshot, non-interactive
docker inspect 是查询容器挂载、环境变量、命令、网络和退出代码的权威信息来源。可用它调试容器行为异常的原因。
镜像管理
docker pull <image>
docker image ls
docker system df # Docker-managed image/layer/volume usage
每台主机只需拉取一次;docker run 会复用缓存的镜像。NVIDIA 镜像通常为 5-40GB。
拆分磁盘的数据根目录迁移
一些云 GPU 提供商提供较小的根卷和较大的临时卷。Docker 默认将数据写入根卷上的 /var/lib/docker,大型镜像会将其占满。检查:
df -h / # root volume size/free
lsblk # all block devices and mount points
如果 / 的空间小于镜像所需总空间,且其他位置挂载了更大的磁盘,请在拉取镜像之前迁移:
sudo systemctl stop docker
sudo mkdir -p <large_volume_path>/docker
sudo rsync -aP /var/lib/docker/ <large_volume_path>/docker/
sudo mv /var/lib/docker /var/lib/docker.old
sudo tee /etc/docker/daemon.json <<'EOF'
{ "data-root": "<large_volume_path>/docker" }
EOF
sudo systemctl start docker
docker info | grep 'Docker Root Dir'
sudo rm -rf /var/lib/docker.old
网络(多容器模式)
对于按名称互相通信的微服务容器,请创建一个 Docker 网络并将容器连接到该网络:
docker network create tao-net
docker run --network tao-net --name api ...
docker run --network tao-net --name worker ... # can resolve `api` by name
大多数 TAO 训练工作负载不需要此操作——每个作业使用单个容器。
常见错误模式
could not select device driver "" with capabilities: [[gpu]] — 缺少 NVIDIA Container Toolkit,或 Docker 未配置为使用 NVIDIA 运行时。获得用户批准后,运行 tao-setup-nvidia-gpu-host 并附加 --backend docker --install(非交互式 Agent 运行时附加 --yes),然后重启 Docker。
docker pull 时出现 unauthorized: authentication required — NGC 密钥无效或缺失。重新运行 docker login nvcr.io。
no space left on device — 首先确定哪个文件系统和存储
类别已满;绑定挂载的训练输出不会计入 docker system df,清理 Docker 镜像也无法解决:
df -h / /var/lib/docker <results_root>
docker system df
docker inspect <tao-container> --format '{{json .Mounts}}'
du -xhd1 <results_root> 2>/dev/null | sort -h
find <results_root> -maxdepth 3 -printf '%u:%g %m %s %p\n' 2>/dev/null | head
对于绑定挂载,只清理记录处于终止状态的作业目录
(tao_job_record.py get "$JOB_ID"),并通过经过审核的所有权修复进行清理;切勿假设
docker system prune 会处理这些目录。对于 Docker 自身的根目录,按
上述说明迁移 data-root。docker system prune -a --volumes 具有破坏性,可能会删除其他工作流
所属的未使用镜像和卷,因此只有在获得用户明确批准并审核 docker system df 清单后才能运行。
Bus error / DataLoader worker exited unexpectedly — /dev/shm 太小。使用 --shm-size 增加共享内存(例如 --shm-size=16g)。
绑定挂载路径上出现 permission denied — 容器 UID ≠ 主机 UID,或 HOME/框架缓存仍指向镜像所有的目录。使用上述规范的主机 UID:GID 映射和可写的 HOME/缓存重定向。对于文档说明需要 root 的镜像,重试前必须完成运行后的所有权规范化。
导入 torch/torchvision 时出现 KeyError: 'getpwuid(): uid not found: <uid>' — 容器以 --user 指定的 UID 运行,但 /etc/passwd 中没有对应条目,也未设置 USER/LOGNAME 环境变量,因此导入时 getpass.getuser() 会回退到 pwd.getpwuid()。仅使用 -e HOME=... 无法解决。保留 UID:GID 映射,并使用规范的身份环境变量块启动(-e USER=... -e LOGNAME=... + 可写的 HOME + 缓存重定向)。不要通过以 root 身份运行来绕过此问题,否则会再次导致输出文件归 root 所有。
docker run -d 后出现 Error: No such container: <name> — 容器启动时崩溃。docker ps -a 会显示容器已退出;使用 docker logs <name> 查看原因。调试时去掉 --rm。
范围边界
此技能既会在 Docker 上运行 TAO 作业(§ 执行),也会说明其他技能所依赖的 Docker 操作方法。相关技能:
tao-skill-bank:tao-run-on-brev— 配置 Brev 实例,然后将容器操作方法委托给 本技能中的同一组 Docker 操作。tao-skill-bank:tao-launch-workflow— 接收与路由入口,以及本技能实现的 平台无关四操作契约。tao-skill-bank:tao-data-io— 存储层级决策 + 暂存 +submit操作调用的 计算帧验证关卡。
模型和数据技能负责生成规范包(内容);本技能负责运行它(方式)。