# MVP Roadmap(V1 → V2 → … → 训练平台) 本文档在 `specs/mvp/milestones.md` 的草稿基础上做**扩展与细化**:把目标拆成可迭代的版本(MVP v1/v2/…),保证每个版本都能**独立运行、可验证验收**,并且在上一版本基础上演进。 > 总目标(North Star):产出一套**基于 Native Ray 集群(无 K8s 底座)**的训练平台,面向多用户,支持 `verl` 各类训练/评测/Serving 工作负载,提升集群利用率,并通过可观测系统实现资源统计、监控告警,最终形成运维 SOP 并可接入运维智能体做自动化运维。 --- ## 0. 关键原则(贯穿所有版本) 1) **版本可独立运行**:每个版本都能从“空环境”按文档跑起来(不依赖未来能力)。 2) **验收可客观验证**:每个里程碑必须有明确的 DoD(Definition of Done)与可复现步骤。 3) **强制产物落盘**:模型/数据/日志/ckpt 必须可追踪、可复用、可审计(基于共享存储/NFS)。 4) **Head 不参与计算**:Head 只承担控制面(GCS/Dashboard/Job server),避免训练抢占控制面资源。 5) **按 submission id 组织作业**:作业输出目录与 Ray submission id 绑定,方便检索、回收、归档。 6) **“先把 RL 跑稳”,再扩 workload**:先 PPO(已验证),再 GRPO/SFT/Serving。 --- ## 0.1 里程碑总览(建议交付顺序) | 版本 | 定位 | 关键交付 | 核心验收点 | |---|---|---|---| | v1 | 可复现实验闭环 | Ray 集群 + PPO 跑通 + 持久化 | driver 不在 head;产物落盘 | | v1.1 | 实验工程化 | JobSpec 模板 + 新增 1 个 workload | 可回归、可定位、可扩展 | | v2.0 | 服务化入口 | API + Ray Jobs SDK | API 提交/查询/停止可用 | | v2.1 | 节点纳管 | SSH 注入 + 资源池/标签 | 节点上线/下线、gang 约束 | | v3.0 | 平台雏形 | 队列 + 超时 + 最小多用户 | pending→running 自动调度 | | v3.1 | 可扩展平台 | 自定义代码/reward + 多版本 | 多版本并存、插件可用 | | v4.0 | 可运营平台 | Prom/Grafana + W&B | 资源核算/告警/归档 | | v4.1 | 可交接平台 | SOP + 自动化运维接口 | 非开发可按 SOP 运维 | | v5.0 | 长期形态 | Serving + Pipeline | 训练→发布推理闭环 | ## 1. 当前基线:MVP v1(已完成/已验证) ### 1.1 目标 在单机(或同一宿主机)用 3 个容器跑通: - Ray head(无 GPU,CPU=0/GPU=0) - 2 个 Ray worker(每个 4 GPU) - 通过 **head 上的 `ray job submit`** 提交 `verl` PPO(`total_epochs=1`) - 通过 **entrypoint 自定义资源**强制 driver 在 worker 上 - 数据/模型/日志/ckpt 全部持久化 ### 1.2 交付物(repo 中已存在) - 脚本与 compose:`src/mvp/v1/` - 行动与验收文档:`specs/mvp/v1/v1_action.md` - 共享目录约定:`shared/datasets`、`shared/hf`、`shared/jobs` 等(与 NFS 对齐) ### 1.3 验收口径(摘要) - `ray job list` 的 `driver_info.node_ip_address` ∈ worker IP,且 ≠ head IP - 训练输出落在 `/mnt/shared/jobs//...` - checkpoint 按 `save_freq` 产生(避免爆磁盘) --- ## 2. MVP v1.1(Hardening + 多 workload 可行性验证) > 目标:把 v1 从“实验脚本”升级成“可长期回归的最小系统”,并验证更多 workload 的可行性边界。 ### 2.1 主要能力 - Workload 扩展(可选顺序): - PPO(回归金标) - GRPO on Ray(可运行验证) - SFT on Ray(可运行验证:`llamafactory` 或 `verl` 相关 SFT 路径) - 作业模板化(最小实现): - 统一 JobSpec(YAML/JSON)描述:workload 类型、资源(nnodes/n_gpus_per_node)、数据、模型、输出目录、超时 - 仍然用 `ray job submit`,但把 entrypoint 组装逻辑标准化 - checkpoint 策略与磁盘保护: - 默认 `save_freq` ≥ 10(或按训练总 steps 的比例) - 明确保留策略(至少提供“保留最后 N 个 ckpt”的配置建议/脚本) - “失败可定位”: - 统一收敛日志入口(Ray job logs + hydra 日志目录 + 关键参数快照) - 失败时能定位:是资源不足 / NCCL / 数据 / 模型 / 配置错误 ### 2.2 验收(DoD) - 同一套脚本在同一台机器能连续跑 3 次 PPO 回归,产物目录不互相覆盖 - 至少新增 1 个 workload(GRPO 或 SFT)可以跑通 “启动→训练→落盘” 闭环 - 作业目录内包含: - `config/submit_cmd.txt`(或 job spec 快照) - `logs/`(可追踪) - `checkpoints/`(按策略生成) --- ## 3. MVP v2.0(Control Plane 服务化:API + Ray Jobs SDK) > 目标:从“人跑脚本”升级为“服务提交任务”。依然是 Native Ray 集群,但引入一个最小控制平面服务。 ### 3.1 系统形态 - Control Plane(建议部署在 head/CPU 机器): - FastAPI 服务(REST) - Job 管理:用 Ray Jobs **Python SDK** 提交/查询/停止(不再依赖 CLI 文本解析) - 节点视图:读取 Ray state(nodes, actors, placement groups) - Data Plane: - 仍然是预先启动的 worker 节点加入集群(先不做 SSH 动态纳管也可) ### 3.2 API(MVP 级别) - `POST /v1/jobs`:提交 JobSpec(ppo/grpo/sft) - `GET /v1/jobs`:列表(含状态、资源、开始/结束时间) - `GET /v1/jobs/{id}`:详情(含输出目录、driver node) - `POST /v1/jobs/{id}:stop`:停止作业 ### 3.3 验收(DoD) - API 提交 PPO,返回 submission id;输出目录为 `/mnt/shared/jobs//...` - API 查询 job 状态与 driver node(必须是 worker) - 停止 job 后,资源释放、状态可见 --- ## 4. MVP v2.1(SSH 纳管 + 资源池 + Gang 约束) > 目标:对齐你草稿里“SSH 纳管”的约束与需求:控制面能纳管 GPU 节点,形成可运营的资源池。 ### 4.1 节点纳管(SSH Provisioner) - 控制面保存 NodeSpec(ip/user/port/labels/gpu_count) - 通过 SSH 执行: - `ray start --address=:6379 --resources=...` - `ray stop`(drain/下线) - 维护节点状态机:`pending → online → draining → offline` ### 4.2 资源池与 gang(All-or-nothing) - 资源池最小模型: - pool 标签(如 `pool_a`、`h20`、`ib_domain_1`) - 提交 job 时指定 pool 约束 - Gang 约束(MVP 实现方式): - job spec 明确 `trainer.nnodes` + `trainer.n_gpus_per_node` - 提交前检查 Ray 可用资源是否满足,不满足则进入 pending 队列(见 v3.0) ### 4.3 验收(DoD) - 通过 API 注册 2 个 worker(SSH 注入 ray start)后,`ray status` 可见节点上线 - 通过 API 下线节点,节点被标记不可调度且不再分配新 job - gang 不满足时 job 不提交(或提交后一直 pending),满足后可运行 --- ## 5. MVP v3.0(调度与多用户:队列 + 超时 + 最小权限) > 目标:平台开始“像个平台”:多用户、队列、超时、审计。仍然不做复杂配额/公平调度。 ### 5.1 作业队列(简单但可用) - FIFO 队列:无优先级 - “资源满足就调度”:谁先满足谁先跑(可接受非严格 FIFO) - job 超时:Ray 原生不支持统一 timeout(草稿已指出),因此控制面需: - 记录 start_time - 定期扫描超时 job → `stop` ### 5.2 多用户最小闭环 - 认证(MVP):token 或 basic auth(先不做复杂 RBAC) - 归属与隔离(文件层): - `/mnt/shared/users//datasets/` - `/mnt/shared/users//models/` - `/mnt/shared/jobs//` 记录 user/metadata ### 5.3 验收(DoD) - 2 个用户可各自提交 job,能看到自己的 job 列表与输出目录 - 超时策略可触发(模拟短 timeout),job 被停止且状态标记为 timeout - 队列在资源不足时保持 pending,资源释放后自动运行 --- ## 6. MVP v3.1(可扩展性:自定义代码/Reward、多版本 VERL) > 目标:把“平台内置 workload”升级成“用户可提交自定义代码与 reward”,并支持多版本并存。 ### 6.1 自定义代码提交(最小实现) 两种方式二选一(建议先做 A): - A:`working_dir` 指向 NFS 上的代码快照目录(用户自己准备/上传) - B:上传 zip(控制面落到 NFS 并解压为 code snapshot) ### 6.2 多版本 VERL 并存 约束前提:**基础镜像保持同一个**(生产环境容器由算力平台创建时已固定镜像标签)。 目标:在同一 Ray 集群内,不同 job 可以使用不同版本的 `verl`(例如不同分支/commit 或用户魔改版)。 已确认优先方案(A):**必须通过 Ray Job 的 `runtime_env.env_vars` 透传 `PYTHONPATH`**,让 job 粒度优先 import 指定代码快照。 建议方案(以 NFS 为中心,最小可行实现): - 在共享存储上以“不可变快照”的方式存放代码版本(推荐 commit hash 命名): - `${SHARED_ROOT}/common/code/verl//...` - `${SHARED_ROOT}/users//code/verl//...`(用户魔改版) - JobSpec 增加 `code_path`(指向上述目录),控制面在提交 job 时注入(必须走 runtime_env): - `runtime_env.env_vars.PYTHONPATH = ":$PYTHONPATH"`(把 code_path 放最前面,确保 import 优先级) 示例(概念性,实际以 `${SHARED_ROOT}` 为准): ```bash CODE_PATH="${SHARED_ROOT}/common/code/verl/" ray job submit \ --address="http://127.0.0.1:8265" \ --submission-id="" \ --runtime-env-json='{"env_vars": {"PYTHONPATH": "'"${CODE_PATH}"':$PYTHONPATH"}}' \ -- \ python3 -m verl.trainer.main_ppo ... ``` 需要验证的关键点(作为 v3.1 的 DoD 之一): - 同时运行两个 job: - jobA 使用 ``,jobB 使用 `` - 互不影响,且各自训练/日志/ckpt 正常 - job 粒度是否能做到“依赖隔离”(至少做到 `verl` 版本隔离;第三方依赖冲突可先假设镜像内一致) > 备注:当前 v1 的做法是容器内全局 `pip install -e /workspace/verl`,这会让所有 job 默认使用同一份 `verl`。要实现多版本并存,必须让 job 的 import 优先使用 `code_path`(或为每个 job 单独创建 venv/安装 wheel;后者更重,建议后置)。 ### 6.3 自定义 reward function - JobSpec 支持 `reward_fn_path`(Python 模块路径) - `reward_fn_path` 可指向共享存储中用户自定义代码目录(例如 `${SHARED_ROOT}/users//code/...`) - 约束:代码必须在 job runtime 中可 import(由 `working_dir`/`PYTHONPATH` 或 runtime_env 保障) - 控制面校验模块可导入(basic lint/安全白名单可后置) ### 6.4 验收(DoD) - 同时运行两个 job:使用不同的 `verl` 代码版本(或用户魔改版本),互不影响 - 用户可在 JobSpec 中替换 reward function 并跑通一个最小训练闭环 --- ## 7. MVP v4.0(可观测性:Prometheus/Grafana + W&B 集成) > 目标:平台可运营:能回答“谁在用多少资源、跑了多久、利用率如何、是否空占 GPU”。 ### 7.1 指标与监控 - Ray 指标接入 Prometheus(节点/任务/actor) - GPU 指标:nvidia exporter 或 DCGM exporter - Dashboard:Grafana(至少 3 张核心面板) - 集群总 GPU/CPU 使用率、空闲率 - 每 job 的 GPU 时间、峰值显存、运行时长 - 节点健康(心跳/掉线)与告警 ### 7.2 W&B(或等价)集成验证 - 最小可行:单机 self-host W&B server 可用性验证 - JobSpec 支持启用/关闭 W&B,并传入 project/run name ### 7.3 验收(DoD) - Grafana 上能看到集群与 job 资源视图 - 某个 job GPU 利用率异常(模拟)能触发告警规则(邮件/IM/日志即可) - W&B 指标能按 job 维度归档(至少 PPO 能上报) --- ## 8. MVP v4.1(运维化:SOP + 自动化运维接口) > 目标:把平台变成“可交接”的系统:运维动作标准化,并为智能体留出接口。 ### 8.1 SOP 与自动化入口 - SOP 文档: - 节点上线/下线 - 故障定位(Ray session、Ray job、NCCL、OOM) - 资源回收(停止 job、清理 ckpt) - 自动化接口(最小): - `/v1/ops/drain_node` - `/v1/ops/restart_ray_head`(谨慎:需要保护与权限) - `/v1/ops/cleanup_job_artifacts` ### 8.2 验收(DoD) - 按 SOP,非开发人员可完成一次“节点上线→跑任务→下线→清理” - 自动化接口至少能完成 1 个高频动作(如清理/停止/下线) --- ## 9. MVP v5.0(Serving 与 Pipeline,偏长期) > 目标:训练-部署一体化:支持 model serving,并在平台内串联训练→评测→发布。 ### 9.1 Serving - Ray Serve(或等价)部署模型推理服务 - Serving 与训练共用模型库与权限(按 user/project) ### 9.2 Pipeline(草稿里标为高级) - Pipeline 是对多个 job 的封装(训练→merge→eval→publish) - 可先实现最小 DAG(两步串联)作为验证 ### 9.3 验收(DoD) - 训练产物一键发布为一个可访问的推理 endpoint - Pipeline 能自动串联并产出最终 artifact(可回滚/可追踪) --- ## 10. 并行技术验证(建议尽早做) 这些属于“跨版本”风险项,建议在 v1.1 ~ v2.0 期间尽早做: ### 10.1 网络(IB / RoCEv2) - 确认环境是否支持 IB(H100)或 RoCEv2(H20) - 跑最小 NCCL 通信验证(all-reduce / bandwidth) - 将必要的 NCCL 环境变量注入到 job runtime_env ### 10.2 Ray + 多节点容器约束 - 多容器同宿主机时的 Ray node_ip/临时目录冲突规律(已踩坑,需固化规范) - 端口范围与防火墙策略(Ray worker 端口、dashboard、metrics) --- ## 11. 已确认的约束与假设(来自讨论结论) 这些会直接影响 v2.1(SSH 纳管)与后续多用户/存储设计: 1) **最终形态仍以“每节点容器”运行**(不是裸机 systemd)。 - H20 开发环境:我们可在宿主机用 `docker compose` 自建容器,并通过 SSH 进入容器调试/纳管。 - H100 生产环境:容器由算力平台创建/回收;平台侧控制面只能 **SSH 进入这些容器** 做纳管(执行 `ray start/stop`、注入 env 等)。 2) **认证**:内部 token 即可(MVP 阶段不对接 SSO)。 3) **存储**:只考虑 NFS。 - 开发环境:NFS/共享目录可通过宿主机 bind mount 提供给容器。 - 生产环境:所有容器挂载相同 NFS,容器内共享根路径为 `/private/`(需要在实现时把“共享根路径”做成可配置项,而不是写死 `/mnt/shared`)。 4) **网络拓扑约束**:暂不做按 IB 域/机架/拓扑的强约束调度(第 10.1 仍需验证 IB/RoCE 是否可用与配置方式,但调度不引入拓扑维度)。 5) **共享目录分层**:在 `users//...` 之外增加一个可读写的 `common/` 目录用于共享数据/模型/代码: - `${SHARED_ROOT}/common/datasets/` - `${SHARED_ROOT}/common/models/` - `${SHARED_ROOT}/common/code/` - 权限(MVP):先默认“所有内部 token 用户可读写”,后续再细化只读/受控写。 --- ## 12. 仍需你确认/讨论的问题(剩余不确定项) 1) `runtime_env.env_vars` 注入对“子进程/训练框架内部启动进程”的覆盖范围是否足够? - 需要确认 `verl`/`sglang` 等子进程是否继承 driver 的环境变量(通常会继承,但建议在 v3.1 验收时明确验证)。