mcpField
T
tao-run-on-docker

tao-run-on-docker

github.com/nvidia云与 DevOps3.8k69 次安装今天更新
代码仓库

关于 tao-run-on-docker

tao-run-on-docker 是用于通过 Docker 执行模型和数据工作流的 TAO 技能,面向单节点 GPU 环境。它统一了本地及 SSH 连接的 Docker 守护进程上的提交、状态查询、日志和取消操作。

主要功能

  1. 01四种作业生命周期操作
  2. 02本地及 SSH 连接的 Docker
  3. 03GPU 选择与容器配置
  4. 04绑定挂载或 S3 数据暂存
  5. 05作业记录与状态映射

适用场景

  1. 01在本地 GPU 主机运行模型工作流
  2. 02向远程 GPU 主机提交作业
  3. 03查看日志和状态或取消作业

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nvidia/skills --skill tao-run-on-docker

安装后在 Claude Code 中输入/tao-run-on-docker确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

BENCHMARK.md
eval.config
skill-card.md
skill.oms.sig
config/
skillspector-baseline.yaml
evals/
evals.json
references/
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 文档。

前置条件

  1. 主机 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。模型要求会覆盖该工作流的默认值。
  2. Docker — docker --version 必须返回 ≥ 20.10。安装地址:https://docs.docker.com/engine/install/。
  3. 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
  1. 暂存输入,使用 tao-data-io:它会选择存储层级,并返回挂载参数和计算帧路径。Docker 通常使用层级 A(绑定挂载主机目录,-v /host/data:/data),也可使用层级 C(传递 S3 凭据,由容器获取数据)。在 <stage>/spec.yaml 中编写 spec 文件,并填入这些计算帧路径。
  2. 检查组装好的命令 — redact_secrets.py lint 必须通过(不得包含内联密钥;凭据需通过不带值的 -e VAR 传递)。
  3. 打开记录 — 此步骤会生成 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")
    
  4. 以分离模式启动,使用 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>)
    
  5. 记录 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 / restartingPENDING
running / pausedRUNNING
exited,代码为 0COMPLETE
exited,代码 ≠ 0ERROR
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 是必需项,而非装饰:任意 --user UID 在镜像的 /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/O
  • WANDB_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 操作调用的 计算帧验证关卡。

模型和数据技能负责生成规范包(内容);本技能负责运行它(方式)。

评论

支持 Markdown