| 名称 | 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 配方时,请遵照本手册。在执行操作前先验证当前状态(kubectl、git 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.submitter、launch.{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/topology、kai.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=DRIVER 且 submission_id=null 表示 exec 提交器运行(无 dashboard 日志端点——改用 nrl-k8s job logs)。type=SUBMISSION 有 submission_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 yaml、kubectl logs和kubectl port-forward+ Ray dashboard API 绕过。
12. 宣布运行成功前的检查清单
报告启动成功之前,验证:
kubectl get rayjob/raycluster -n default显示预期对象。nrl-k8s job list(或curl /api/jobs/)显示作业处于RUNNING/SUCCEEDED状态。- 驱动日志包含
wandb.ai/<project>/runs/<id>(如果启用 wandb)——与用户分享 URL。 - 至少出现一行
Processed prompts: 100%(确认生成已接通)。 - 仅
--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-workspacePVC 挂载到/mnt/rl-workspace - 设置
USER环境变量为nrl-k8s用户名(这样即使以 root 运行,$USER和getpass.getuser()也能正确工作) - 首次启动时安装
kubectl、rclone(如果配置) - 通过每个用户的 K8s Secret 上的
envFrom注入 SSH 密钥和 token
pod 的 default 服务账户需要在命名空间中有 edit RoleBinding,kubectl 才能在内部工作。dev connect 会检查这一点,如果缺少则打印所需 YAML。
14. 仓库中的位置
- CLI 代码:
infra/nrl_k8s/src/nrl_k8s/(cli.py、orchestrate.py、manifest.py、rayjob.py、k8s.py、submitters/、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/…。