argus-cluster/specs/mvp/v2.5/v2.5_dev_plan.md

230 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MVP v2.5 开发计划TDD 驱动)
本文是 v2.5 的**工程化开发计划**强调“先写测试再写实现”TDD并将每个里程碑拆成**可独立验收**的小闭环。
输入依据:
- 路线图:`specs/mvp/mvp_roadmap_v2.md`
- v2.5 设计:`specs/mvp/v2.5/v2.5_design.md`
- v2.5 API 草案:`specs/mvp/v2.5/v2.5_api.md`
- v2.5 验收:`specs/mvp/v2.5/v2.5_acceptance.md`
v2.5 约束(已确认):
- **不扩展 TaskSpec**:沿用 v2.0/v2.1 的 YAML 结构化字段与语义。
- **不支持自定义 reward function / 不支持用户自定义 verl 代码**。
- 训练输入verl 代码、HF cache、datasets统一使用 `/private/common/...`
- 多用户隔离 v2.5 **先只隔离 jobs 输出目录**`/private/users/<uid>/jobs/<ray_submission_id>/...`
---
## 0. TDD 规范(所有功能都遵循)
### 0.1 测试分层
1) **单元测试fast**
- 纯 Python 逻辑DB、鉴权、ID、路径派生、head.json 解析/TTL、watchdog 决策逻辑。
- 目标:不依赖真实 Ray、不依赖 docker、不依赖网络。
2) **组件测试(中等)**
- FastAPI 路由:使用 `fastapi.testclient.TestClient`(现有 v2.0 已采用)。
- 目标:验证 auth/权限隔离、API 行为、状态机。
3) **端到端(慢/手工或脚本)**
-`argus@h1` 上通过 scripts/compose 跑一次“head publish → worker auto-connect → API submit”闭环。
- 目标:验证无状态 worker + watchdog 的真实行为。
### 0.2 测试约定
- 测试目录:`src/mvp/py/tests/`
- 新增功能必须先补齐测试用例,并让其在未实现时失败(红)。
- 实现最小改动让测试变绿(绿)。
- 重构/去重复(重构)。
> 注:现有测试通过 `src/mvp/py/tests/conftest.py` 注入 ray stub确保单测不依赖真实 ray 包v2.5 新增模块也应复用此模式。
---
## 1. 里程碑拆分v2.5 = 4 个可验证闭环)
### M1User 表/Token 表 + 基础鉴权(不影响现有内部 token 兼容)
**目标**
- 引入 user/token 的持久化与鉴权映射token → user_id
- 兼容现有 `Authorization: Bearer <MVP_INTERNAL_TOKEN>` 的“单租户模式”,避免一次性破坏 v2.0 用法:
- v2.5 可以先支持两种 token 模式:
- legacy环境变量 `MVP_INTERNAL_TOKEN`(全局单租户);
- user tokenDB 内签发 token多用户
- admin 能创建用户、签发 token、禁用用户。
**TDD 用例(先写测试)**
单测:
- `test_user_db_create_disable()`
- 创建用户 ACTIVE禁用后状态变为 DISABLED重复创建返回冲突或幂等按最终约定
- `test_token_hashing()`
- 签发 token 时 DB 中只保存 hash不保存明文。
API 测试TestClient
- `test_admin_create_user_and_issue_token()`
- admin token 可创建用户并签发 token明文 token 只返回一次)。
- `test_disabled_user_token_rejected()`
- 用户被禁用后,使用旧 token 调用 API 返回 401/403。
**实现落点(建议模块)**
- `argus.service.auth`token 校验与 user_id 解析(兼容 legacy 模式)
- `argus.service.db`:新增 `users``api_tokens` 表与 CRUD
- `argus.service.app`:新增 user 管理 endpointsadmin scope
- `configs/dev.yaml`:补充 admin token/env 相关配置(保持 YAML 风格)
**验收点**
- `v2.5_acceptance.md`U1 可通过自动化 API 测试覆盖。
---
### M2Task 绑定 user_id + API 可见性隔离(仍不改 TaskSpec
**目标**
- 提交 task 时由 token 推导 `user_id`,写入 `tasks.user_id`
- task 查询/取消/日志默认只允许 owner他人访问返回 404避免泄露存在性
- queue 默认只返回当前用户队列admin 可查询全局队列(可选)。
**TDD 用例(先写测试)**
单测:
- `test_tasks_table_has_user_id()`:创建任务必须落 `user_id`,且 `list_queue(user_id=...)` 只返回该用户任务。
API 测试:
- `test_task_visibility_isolated()`
- user A 创建 taskuser B 查询 `/api/v2/tasks/{id}` 返回 404
- user B cancel/logs 也返回 404。
- `test_queue_isolated()`
- A/B 各自创建 task`GET /api/v2/queue` 只看到自己的。
**实现落点**
- `argus.service.app`:为 task endpoints 增加 user scope
- `argus.service.db`tasks 表增加 user_id 字段、索引、按 user 过滤的查询方法
- `argus.service.scheduler`pick_next_runnable_task 等仍按“全局 FIFO”或“按 user FIFO”
- v2.5 先保持“全局 FIFO”最简单但 API queue 视角是按 user 过滤)。
**验收点**
- `v2.5_acceptance.md`U2 可通过 API 测试覆盖。
---
### M3Jobs 输出目录按 user 隔离(只改输出,不改输入)
**目标**
- Ray Job 的 job_root 目录由服务端统一计算到:
- `/private/users/<uid>/jobs/<ray_submission_id>/...`
- TaskSpec 内与输入相关的路径字段必须是 `/private/common/...`v2.5 输入统一 common
- 任何用户无法通过 TaskSpec 指定输出写到非 user jobs 目录(避免越权写)。
**TDD 用例(先写测试)**
单测:
- `test_job_root_derivation_per_user()`
- 给定 user_id 与 ray_submission_id派生 job_root 固定且正确。
- `test_reject_non_common_inputs()`
- TaskSpec 中 train_file / val_file / code_path / hf 路径等若不以 `/private/common/` 开头则拒绝HTTP 400
API 测试:
- `test_job_dir_written_under_user_jobs()`
- 提交 task 后,在 DB 或 submit payload 中能看到 job_root 在 user jobs 下(可通过 mock RayJobTool.submit 捕获 spec
**实现落点(建议最小侵入)**
- 在 service 层派生 `job_root` 并注入到 RayJobTool/builders而不是让用户从 TaskSpec 指定)。
- RayJobTool `_job_dir()` 改为接收“job_root 生成器”或直接接收 `job_root` 参数(由服务层提供)。
- 目标:保持 RayJobTool 的职责清晰:提交 Ray job路径策略由 service 决定。
**验收点**
- `v2.5_acceptance.md`U3/U4 可通过 API/单测覆盖。
---
### M4Stateless Ray Node Poolhead.json + worker watchdog+ 端到端脚本验证
**目标**
- head 启动后持续写入 `/private/ray/discovery/<cluster_name>/head.json`(包含 TTL
- worker 容器内运行 watchdog或启动脚本 + watchdog无需平台显式传 head 地址:
- 读取 head.json存在且未过期`ray start --address=<head_ip>:<gcs_port>`
- head.json 变化 → `ray stop` + `ray start` 重连
- 在 dev 环境docker compose提供一键脚本复现e2e
**TDD 用例(先写测试)**
单测(不跑真实 ray
- `test_head_json_read_validate_ttl()`
- 文件不存在/过期 → 返回“不可用”
- 未过期 → 返回 head 地址
- `test_watchdog_decision_on_change()`
- head_ip 变化 → 触发重连动作
- only updated_at 变化(地址不变)→ 不重连(或按策略重连,需确定)
组件/脚本级测试(可选):
- 如果 watchdog 用 Python 实现,可对“执行命令”层做 stub不真正跑 `ray start`),只验证会调用什么命令。
端到端脚本(手工/慢):
- 提供脚本 `scripts/run_all_v25_stateless.sh`(命名示例):
1) 起 headRay head + API
2) 启动 head publisher写 head.json
3) 起 2 个 worker每个 4 GPUworker 只跑 watchdog不传 head 地址
4) `ray status` 显示 1 head + 2 worker 且 GPU=8
5) 通过 API 创建用户/签发 token提交 PPO/GRPO/SFT
6) 重启 head或更新 head.json 指向新地址)验证 worker 自动重连
**实现落点(建议实现策略)**
为了可测试性TDD推荐把“读 head.json/判定 TTL/生成 ray start 命令”做成 Python 模块:
- `argus.ray.discovery`read/write head.json原子写、TTL
- `argus.ray.worker_watchdog`watch looppolling + change detection执行命令可注入便于单测 stub
脚本层保持薄:
- `scripts/` 负责 docker exec / compose 编排与进程守护;
- watchdog 进程由容器内 python 模块运行(更可测、更易移植到生产平台的 entrypoint/command
**验收点**
- `v2.5_acceptance.md`A1/A2/A3 主要通过 e2e 脚本 + dashboard/日志验证。
---
## 2. 回归策略(确保 v2.0 不被破坏)
在 v2.5 过程中保留并持续回归以下用例(至少单测覆盖):
- 旧的内部 token 模式仍可访问 `GET /api/v2/queue` 与提交 task若决定保留兼容
- scheduler 的“资源不足 → PENDING_RESOURCES → 延迟重试”行为不变(现有 `test_scheduler.py` 覆盖)。
- `ray entrypoint_resources` 强制 driver 落 worker继续使用 `worker_node` 自定义资源)。
---
## 3. 交付清单(代码/脚本/文档)
### 3.1 代码
- user/tokensDB schema + auth + API endpoints
- tasks绑定 user_id + 权限隔离
- job_root按 user jobs 输出目录派生(输入仍 common
- discovery/watchdoghead.json + worker 自愈
### 3.2 scriptsdev e2e
- head启动 Ray head + head publisher
- workers以无状态方式启动不传 head addr+ watchdog
- `run_all`:一键跑通(含 API submit + 查询 + cancel + 观察队列)
### 3.3 文档
- 更新 `specs/mvp/v2.5/*`(设计/API/验收/开发计划)
- 补充 `src/mvp/README.md` 的 v2.5 使用方式(如需要)
---
## 4. 关键待确认点(开始实现前必须定稿)
1) **legacy token 是否继续兼容**
- 方案 A保留 `MVP_INTERNAL_TOKEN`(单租户)+ 新增 user token多租户
- 方案 Bv2.5 直接切换到 user token破坏兼容但更清晰
2) **调度公平性**
- v2.5 先全局 FIFO简单后续 v3 再引入 per-user 公平调度/配额。
3) **head.json 的生产写入者**
- 方案 A与 API 同进程线程(最少组件)
- 方案 B独立进程更独立、易运维