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

9.7 KiB
Raw Blame History

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、不依赖网络。
  1. 组件测试(中等)
  • FastAPI 路由:使用 fastapi.testclient.TestClient(现有 v2.0 已采用)。
  • 目标:验证 auth/权限隔离、API 行为、状态机。
  1. 端到端(慢/手工或脚本)
  • 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.authtoken 校验与 user_id 解析(兼容 legacy 模式)
  • argus.service.db:新增 usersapi_tokens 表与 CRUD
  • argus.service.app:新增 user 管理 endpointsadmin scope
  • configs/dev.yaml:补充 admin token/env 相关配置(保持 YAML 风格)

验收点

  • v2.5_acceptance.mdU1 可通过自动化 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 各自创建 taskGET /api/v2/queue 只看到自己的。

实现落点

  • argus.service.app:为 task endpoints 增加 user scope
  • argus.service.dbtasks 表增加 user_id 字段、索引、按 user 过滤的查询方法
  • argus.service.schedulerpick_next_runnable_task 等仍按“全局 FIFO”或“按 user FIFO”
    • v2.5 先保持“全局 FIFO”最简单但 API queue 视角是按 user 过滤)。

验收点

  • v2.5_acceptance.mdU2 可通过 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.mdU3/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.discoveryread/write head.json原子写、TTL
  • argus.ray.worker_watchdogwatch looppolling + change detection执行命令可注入便于单测 stub

脚本层保持薄:

  • scripts/ 负责 docker exec / compose 编排与进程守护;
  • watchdog 进程由容器内 python 模块运行(更可测、更易移植到生产平台的 entrypoint/command

验收点

  • v2.5_acceptance.mdA1/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破坏兼容但更清晰
  1. 调度公平性
  • v2.5 先全局 FIFO简单后续 v3 再引入 per-user 公平调度/配额。
  1. head.json 的生产写入者
  • 方案 A与 API 同进程线程(最少组件)
  • 方案 B独立进程更独立、易运维