K8s上NeMo-RL启动调试Skill launch-nemo-rl

该技能是一份详细的操作手册,指导用户通过nrl-k8s CLI在Kubernetes集群上启动、监控、停止和调试NeMo-RL训练任务。内容包括临时RayJob模式与长生命周期RayCluster模式的区别、配方和基础设施文件的选择、相关命令行标志、端到端工作流程、常见陷阱及排错方法。该技能适用于需要高效管理和迭代大规模强化学习训练任务的场景。搜索关键词:NeMo-RL、Kubernetes、RayCluster、RayJob、nrl-k8s、训练调度、调试、分布式训练。

强化学习训练 0 次安装 1 次浏览 更新于 9/7/2026
名称 launch-nemo-rl
开源协议 Apache-2.0
描述 通过 nrl-k8s CLI 在 Kubernetes 集群上启动、监控、停止和调试 NeMo-RL 配方的操作手册。涵盖临时模式与长生命周期 RayCluster 模式、迭代运行以及调试挂起或失败的训练作业。 when_to_use: - “在 k8s 上运行此配方” - “在集群上启动” - “提交训练作业” - “拆除集群” - “重新提交为 rayjob” - “为什么运行卡住了” - “如何获取作业 X 的日志” - “让集群恢复运行” allowed-tools: Bash Read Grep Glob Edit Write

launch-nemo-rl —— 通过 nrl-k8s 在 Kubernetes 上运行 NeMo-RL 配方

这是 infra/nrl_k8s/nrl-k8s CLI 的操作手册。当用户要求在 Kubernetes 集群上启动/迭代/调试 NeMo-RL 配方时,请遵照本手册。在执行操作前先验证当前状态(kubectlgit log、配方与基础设施文件)——集群是共享的,错误操作的代价很高。

1. 一个命令,两种模式

有一个顶层提交命令:nrl-k8s run。它有两种生命周期模式。

模式 调用方式 使用场景 集群之后状态?
临时(默认) nrl-k8s run 一次性任务。KubeRay 应用 RayJob,运行,然后拆除集群。适合大多数运行。 否(自动)
长生命周期 nrl-k8s run --raycluster 开发循环。复用匹配的活跃集群,若不存在则应用,若有漂移则警告并复用(传入 --recreate 替换)。然后提交守护进程和训练任务。迭代首选此类。

问一句:运行后我是否还需要这个集群? 如果需要,使用 --raycluster。否则使用默认的临时模式。

其余 CLI 命令用于可观测性 / 分阶段控制:

命令 目的
nrl-k8s check 校验配方与基础设施对;可选地把完全解析的清单写入 -o 文件。
nrl-k8s status 按角色查看 RayCluster 状态、head pod 阶段、worker pod 阶段、守护进程作业状态。
nrl-k8s cluster up/down/list/dashboard 独立于运行管理 RayCluster(例如用 --dry-run 渲染清单)。
nrl-k8s job list/logs/stop 对已提交到角色集群的 Ray Jobs 进行观测。
nrl-k8s logs 跟踪角色 pod / 守护进程日志,无需提交 ID。

2. 配方 + 基础设施对

每次启动都需要两个文件。用 --infra 传入基础设施文件,不要合并内联:

nrl-k8s run infra/nrl_k8s/examples/<recipe>.yaml \
  --infra infra/nrl_k8s/examples/<recipe>.<profile>.infra.yaml
  • 配方 (例如 qwen3_30b_math_8n_4gpu.yaml) —— NeMo-RL 配置:模型、GRPO/SFT 参数、cluster.{gpus_per_node,num_nodes}。使用 defaults:examples/configs/recipes/llm/... 继承默认值。
  • 基础设施 (例如 *.<profile>.infra.yaml) —— K8s/Ray 形态:命名空间、镜像、服务账户、kuberay: 下的 RayCluster 规范、可选的 deployments: 下的 Deployments、submit.submitterlaunch.{mode,codeSource,codePath,entrypoint}。配对命名遵循 <recipe>.<profile>[.prod].infra.yaml,其中 <profile> 指定硬件目标(例如 gb300)。

示例对位于 infra/nrl_k8s/examples/ —— 读取相邻文件以了解目标 profile 的当前约定。

3. 长生命周期模式标志

有三个独立维度。--mode 是选择默认值的宏;单独标志可覆盖它。

--mode interactive   → --submitter portForward  --code-source upload  (跟踪日志)
--mode batch         → --submitter exec         --code-source image   (nohup 后返回)
  • 提交方式 (Submitter)portForward 使用 kubectl port-forward + Ray Job SDK(获取 dashboard 跟踪的 submission_id)。exec 使用 kubectl exec + head pod 上的 nohup(无 submission_id;驱动在 dashboard 中显示为 type=DRIVER)。
  • 代码来源 (Code source)upload 从笔记本电脑暂存 working_dir(Ray 100 MiB 上限)。image / lustre 期望代码在 pod 的文件系统上——与 --code-path 配对(通常为 /opt/nemo-rl),它是标准基础设施示例中共享文件系统 PVC 挂载的 subPath。
  • 等待 (Wait)--wait 跟踪日志直到终止;--no-wait 在驱动运行后立即返回。

其他仅长生命周期模式可用的标志:

  • --replace —— 在提交新作业前,先停止任何运行中的训练/守护进程作业(为守护进程 submissionIds 加时间戳后缀,以便 Ray 接受重新提交)。
  • --recreate —— 删除并重新应用与实时规范有漂移的 RayCluster(默认是警告并复用)。
  • --skip-daemons —— 启动所有声明的集群,但只提交训练。用于 gym/generation 已健康的分解式配方。

陷阱:在入口点执行 cd /opt/nemo-rl(或镜像内 / Lustre 路径)并从那里加载配方的 infra 上,--code-source upload 不会覆盖 pod 上的配方 —— 上传的 working_dir 位于 /tmp/ray/...,但入口点 cd 到别处。要真正测试本地配方的更改,要么将你的编辑同步到挂载进 pod 的共享文件系统,要么在入口点中调整 Hydra 覆盖。

4. 临时模式标志 (--rayjob)

当设置 --rayjob 时,run 分支进入 RayJob 代码路径。相关标志:

  • --rayjob-name NAME —— RayJob 元数据名称(默认使用训练集群名称)。
  • --shutdown / --no-shutdown —— 默认为 true:Ray Job 达到终止状态后 KubeRay 删除 RayCluster。
  • --ttl SECONDS —— 默认 3600s:运行结束后保留 RayJob 对象一段时间,以便事后获取日志。
  • --wait / --no-wait —— 默认为 wait:轮询 jobDeploymentStatus 直到 Complete/Failed。--no-wait 在 RayJob 应用后立即返回。
  • --timeout SECONDS —— 默认 86400s(24h):限制 --wait 轮询。
  • --dry-run —— 渲染 RayJob 清单并打印;不应用。

--replace / --recreate / --skip-daemons--rayjob 模式下被静默忽略(KubeRay 拥有生命周期)。

5. 在不接触共享文件系统的情况下迭代配置

当 pod 文件系统上的配方值不适合你的实验时,使用入口点上的 Hydra 覆盖,而不是分叉配方。模式:

entrypoint: |
  set -eu
  cd /opt/nemo-rl
  RUN_ID="\${RAY_JOB_SUBMISSION_ID:-\${NRL_K8S_RUN_ID:-$(date -u +%Y%m%d-%H%M%S)}}"
  python -u examples/run_grpo.py \
    --config infra/nrl_k8s/examples/<recipe>.yaml \
    logger.wandb_enabled=true \
    logger.wandb.project=<project> \
    "logger.wandb.name=<run-name>-\${RUN_ID}"

用反斜杠转义 ${…}. 否则 OmegaConf 会将其解释为插值,并在 shell 风格 ${VAR:-default} 上报错。RUN_ID 解析为 RAY_JOB_SUBMISSION_ID(rayjob 模式下由 KubeRay 注入)→ NRL_K8S_RUN_ID(长生命周期模式下由 CLI 注入)→ 本地时间戳——所以在任意路径下名称都是唯一的。

6. 每种 profile 的注意事项(硬件 + 调度器 + DRA)

每个基础设施 YAML 都编码了硬件/调度器 profile。infra/nrl_k8s/examples/ 中的具体示例对它们面向的 profile 具有权威性——在编写新 profile 前请阅读相邻的基础设施文件。常见的差异包括:

  • 单节点 GPU 数(例如 4 vs 8)——必须与配方中的 cluster.gpus_per_node 匹配,否则 workers 保持 Pending
  • 节点选择器——head pod 通常落在纯 CPU 节点池上;GPU worker 匹配 nvidia.com/gpu.product 或节点组标签。
  • 调度器——KAI(schedulerName: kai-scheduler + kai.scheduler/queue 标签)配合拓扑注解(kai.scheduler/topologykai.scheduler/topology-required-placement)将 workers 帮派调度到一个 clique 中。没有它,pod 可能分散到不同机架,NVLink/RoCE 无法跨越它们。
  • DRA 声明——ComputeDomain 和 RoCE 通过 resourceClaims 引用 ResourceClaimTemplate 来附加。当 worker pod spec 包含 DRA 声明引用时,CLI 会自动创建/删除它们——无需手动设置。
  • 密钥——始终通过 secretKeyRef(wandb-api-key、image pull secret)引用。绝不内嵌。
  • 共享文件系统挂载——通常一个 Lustre PVC 挂载两次:一次在代码路径(例如 /opt/nemo-rl,带用户作用域的 subPath),一次在工作区根目录(例如 /mnt/rl-workspace),用于数据集、HF 缓存和检查点。

在应用某基础设施前,验证目标命名空间中存在所需资源:

kubectl get pvc <workspace-pvc>
kubectl get secret <wandb-secret> <image-pull-secret>
kubectl get sa <service-account>

7. 端到端工作流

7a. 全新一次性运行 (rayjob)

# 从 NeMo-RL 仓库根目录执行:
nrl-k8s check <recipe> --infra <infra>                               # 先验证
nrl-k8s run <recipe> --infra <infra> --rayjob --dry-run              # 渲染 RayJob 清单
nrl-k8s run <recipe> --infra <infra> --rayjob --no-wait              # 应用,快速返回

查看状态和拆除过程(即使你的笔记本电脑断连也可继续,因为 KubeRay 拥有生命周期):

kubectl get rayjob -n default <name> -w
kubectl get raycluster -n default                                    # 空 = 拆除成功

7b. 开发循环(长生命周期)

nrl-k8s run <recipe> --infra <infra> --run-id $(date +%Y%m%d-%H%M%S)
# 配方有改动?重新运行即可——复用现有集群。
# Pod spec 变了?加 --recreate 删除并重新应用。
# 分解式配方且 gym/gen 已健康?使用 --skip-daemons。

7c. 首次分解式启动

nrl-k8s run <recipe> --infra <disagg-infra> --mode batch --code-source image

7d. 仅集群生命周期

nrl-k8s cluster up   <recipe> --infra <infra> --target kuberay.training --wait
nrl-k8s cluster up   <recipe> --infra <infra> --target kuberay.training --dry-run   # 渲染清单
nrl-k8s cluster down <recipe> --infra <infra> --target kuberay.training --wait
nrl-k8s cluster down <recipe> --infra <infra>                                       # 拆除全部
nrl-k8s cluster list -n default
nrl-k8s cluster dashboard <cluster-name>                                  # port-forward + 浏览器

7e. Deployments(例如 nemo-skills 沙箱)

# 只启动 deployment
nrl-k8s cluster up <recipe> --infra <infra> --target deployments.nemo_skills
# 只拆除 deployment
nrl-k8s cluster down <recipe> --infra <infra> --target deployments.nemo_skills
# 拆除所有内容(RayClusters + Deployments)
nrl-k8s cluster down <recipe> --infra <infra>

infra YAML 中的 deployments: 部分声明了与 RayClusters 一起管理的 Kubernetes Deployments。CLI 从顶层 infra 键(与 RayClusters 相同)修补 image、imagePullSecrets 和 serviceAccountName。Deployments 与集群启动并行开始——无顺序依赖。

8. 监控运行

# 状态
nrl-k8s status <recipe> --infra <infra>
kubectl get rayjob,raycluster -n default

# 跟随驱动
nrl-k8s job list <recipe> --infra <infra> --role training
nrl-k8s job logs <run-id> <recipe> --infra <infra> --role training -f

nrl-k8s job logs -f 子进程死亡(kubectl port-forward 空闲约 15 分钟后 I/O 超时),只需重新运行。训练作业会继续。

要获取已终止作业(SUCCEEDED/FAILED)或 RayJob 通过 dashboard API 获取驱动日志:

RC=$(kubectl get rayjob -n default <rayjob-name> -o jsonpath='{.status.rayClusterName}')
kubectl port-forward -n default svc/${RC}-head-svc 18266:8265 &
curl -s http://localhost:18266/api/jobs/                              # 列出作业,找到 submission_id
curl -s "http://localhost:18266/api/jobs/<submission_id>/logs"        # 完整驱动日志

type=DRIVERsubmission_id=null 表示 exec 提交器运行(无 dashboard 日志端点——改用 nrl-k8s job logs)。type=SUBMISSIONsubmission_id/api/jobs/<id>/logs 可用。

Wandb URL 在首次 wandb.init 调用时出现在驱动日志中;用 grep -oE 'https://wandb\.ai/[A-Za-z0-9_./-]+' 抓取。

9. 停止操作

停止什么 命令
单个训练运行 nrl-k8s job stop <run-id> <recipe> --infra <infra> --role training
集群上所有运行中的 Ray 作业(+ 提交新的) nrl-k8s run <recipe> --infra <infra> --replace
长生命周期 RayCluster nrl-k8s cluster down <recipe> --infra <infra> --target kuberay.training --wait
RayJob(临时) kubectl delete rayjob <name> -n default —— 仅当 shutdownAfterJobFinishes 未触发时使用

删除共享基础设施前请确认。对他人集群执行 cluster down 的代价很高。

10. 验证 RayJob 拆除

run --rayjob 完成后使用 --shutdown(默认),KubeRay 应删除 RayCluster:

kubectl get rayjob   -n default <rayjob-name>                        # jobDeploymentStatus = Complete
kubectl get raycluster -n default | grep <rayjob-name>               # 无输出 = 已拆除

RayJob 对象本身会保留 --ttl 秒(默认 3600s),以便仍能获取日志。

11. 常见陷阱

  • OmegaConf 插值会吃掉 recipe/infra YAML 中的 ${VAR}。用 \${VAR} 转义 shell 变量,以便 OmegaConf 原样传给 pod shell。
  • Megatron 优化器配置不携带 foreach / fused。像 ~policy.optimizer.kwargs.foreach ~policy.optimizer.kwargs.fused(对 DTensor 配置有效)这样的覆盖在 Megatron 配方上会失败。对 Megatron 省略它们。
  • DTensor vs Megatron——MoE 配方通常使用 megatron_cfg.enabled=true;确保继承默认值中的 dtensor_cfg.enabled=false
  • 共享文件系统与 git 分叉——codeSource: image|lustre 从 pod 文件系统读取。如果你的本地编辑不在 pods 挂载的共享文件系统上,那么运行测试的是磁盘上的版本,而不是你的。要么通过辅助 pod 同步(head pod exec 通常被阻止),要么通过 Hydra 标志覆盖。
  • 临时存储 + readinessProbe由 kuberay/CDI webhook 在 pod 应用时注入。不要把它们加到内联 RayCluster spec 中。
  • 节点污点因集群而异。workers 上的 tolerations: [{operator: Exists}] 是防御性的,值得保留。
  • Dashboard 空白页——Ray 2.52 默认将 dashboard 资源安装为符号链接;nrl-k8s cluster dashboard <name> 自动重新安装 ray[default] --link-mode=copy 来修复。在镜像中设置 ENV UV_LINK_MODE=copy 可完全避免此问题。
  • kubectl exec 在自动化中通常被阻止——用 kubectl get ... -o yamlkubectl logskubectl port-forward + Ray dashboard API 绕过。

12. 宣布运行成功前的检查清单

报告启动成功之前,验证:

  1. kubectl get rayjob/raycluster -n default 显示预期对象。
  2. nrl-k8s job list(或 curl /api/jobs/)显示作业处于 RUNNING / SUCCEEDED 状态。
  3. 驱动日志包含 wandb.ai/<project>/runs/<id>(如果启用 wandb)——与用户分享 URL。
  4. 至少出现一行 Processed prompts: 100%(确认生成已接通)。
  5. --rayjob 模式:jobDeploymentStatus=Complete 后,确认 kubectl get raycluster | grep <name> 为空(拆除成功)。

13. 开发 pod

nrl-k8s dev 管理集群上的轻量级 CPU pod,用于代码同步、调试以及在集群内部运行 kubectl/nrl-k8s

# 一次性设置:配置密钥(HF token、wandb、SSH 密钥、rclone)
nrl-k8s dev setup-secrets --ssh-key ~/.ssh/id_rsa --add-rclone

# 创建 pod 并 exec 进入(幂等——复用已有 pod)
nrl-k8s dev connect

# 切换镜像(必须先停止——镜像变化会警告,但不会自动应用)
nrl-k8s dev stop
nrl-k8s dev connect --image nvcr.io/nvidian/nemo-rl:v0.7.0

# 拆除
nrl-k8s dev stop

dev pod:

  • 运行在纯 CPU 节点上(与 GPU 节点反亲和)
  • 将共享 rl-workspace PVC 挂载到 /mnt/rl-workspace
  • 设置 USER 环境变量为 nrl-k8s 用户名(这样即使以 root 运行,$USERgetpass.getuser() 也能正确工作)
  • 首次启动时安装 kubectlrclone(如果配置)
  • 通过每个用户的 K8s Secret 上的 envFrom 注入 SSH 密钥和 token

pod 的 default 服务账户需要在命名空间中有 edit RoleBinding,kubectl 才能在内部工作。dev connect 会检查这一点,如果缺少则打印所需 YAML。

14. 仓库中的位置

  • CLI 代码:infra/nrl_k8s/src/nrl_k8s/cli.pyorchestrate.pymanifest.pyrayjob.pyk8s.pysubmitters/schema.py)。
  • 测试:infra/nrl_k8s/tests/unit/——从 infra/nrl_k8s/ 运行 uv run --extra test pytest -x -q
  • 配方与基础设施示例:infra/nrl_k8s/examples/
  • 此工具包装的基础配方:examples/configs/recipes/llm/…examples/nemo_gym/…