# 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/<submission_id>/...`  
- 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/<submission_id>/...`  
- 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=<head>: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/<user>/datasets/`
  - `/mnt/shared/users/<user>/models/`
  - `/mnt/shared/jobs/<submission_id>/` 记录 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/<commit>/...`
  - `${SHARED_ROOT}/users/<user>/code/verl/<commit>/...`（用户魔改版）
- JobSpec 增加 `code_path`（指向上述目录），控制面在提交 job 时注入（必须走 runtime_env）：
  - `runtime_env.env_vars.PYTHONPATH = "<code_path>:$PYTHONPATH"`（把 code_path 放最前面，确保 import 优先级）

示例（概念性，实际以 `${SHARED_ROOT}` 为准）：

```bash
CODE_PATH="${SHARED_ROOT}/common/code/verl/<commit>"

ray job submit \
  --address="http://127.0.0.1:8265" \
  --submission-id="<submission_id>" \
  --runtime-env-json='{"env_vars": {"PYTHONPATH": "'"${CODE_PATH}"':$PYTHONPATH"}}' \
  -- \
  python3 -m verl.trainer.main_ppo ...
```

需要验证的关键点（作为 v3.1 的 DoD 之一）：

- 同时运行两个 job：
  - jobA 使用 `<commitA>`，jobB 使用 `<commitB>`
  - 互不影响，且各自训练/日志/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/<user>/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/<user>/...` 之外增加一个可读写的 `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 验收时明确验证）。
