5.2 KiB
Raw Blame History

MVP v2.0 API 设计(最小可用)

v2.0 的 API 目标是:把 v1.1 的“脚本提交”变成“服务化提交”,并在服务侧实现队列/重试/状态聚合。

约束:

  • 内部 token 鉴权(简单即可)。
  • Ray Job 提交必须使用 Ray Python SDKJobSubmissionClient),不使用 requests 手写 HTTP。
  • 输出与状态必须落盘到 NFS容器内 /private)。

1. 鉴权

  • HeaderAuthorization: Bearer <INTERNAL_TOKEN>
  • v2.0 不做用户体系与权限隔离token 只是“防误用”。
  • 配置建议:复用 src/mvp/v1.1/py/configs/dev.yaml 并在 v2.auth.token_env 指定 token 环境变量名。

1.1 运行位置dev 示例)

  • 服务进程运行在 Ray head 容器(便于访问 Ray Job server
  • 宿主机侧用脚本控制(docker exec
    • src/mvp/v2.0/scripts/20_start_api.sh
    • src/mvp/v2.0/scripts/21_stop_api.sh
    • src/mvp/v2.0/scripts/22_status_api.sh
  • 远程机目录约定(示例):argus@h1:/home2/argus/infra/mvp/v2/,容器内挂载到 /workspace/mvp/v2/

2. 资源与 ID 约定

2.1 task_id服务层主 ID

  • 格式建议:mvp2-<workload>-<YYYYMMDD>-<HHMMSS>-<suffix>
    • 示例:mvp2-ppo-20251223-143201-7f3a

2.2 ray_submission_idattempt 级 ID

  • 由 service 派生:<task_id>--a<NN>
    • 示例:mvp2-ppo-20251223-143201-7f3a--a01

好处:

  • Ray 的 submission id 自带 task_id可直接从 Ray dashboard 反查到服务侧任务。
  • /private/jobs/<ray_submission_id>/... 目录天然隔离且可读。

3. JobSpec请求体

v2.0 要求 JobSpec 使用 v1.1 同款 YAML(字段与语义保持一致),服务端接收 YAML 文本并解析后入库(同时原样保存 jobspec_yaml 便于审计/复现)。

最小字段(示例 YAML

workload: "ppo"
submission_id: ""            # v2.0 服务端会忽略/覆盖(由 task_id 派生 ray_submission_id
code_path: "/private/common/code/verl/verl_repo"
model_id: "Qwen/Qwen2.5-0.5B-Instruct"
train_file: "/private/datasets/gsm8k/train.parquet"
val_file: "/private/datasets/gsm8k/test.parquet"
nnodes: 2
n_gpus_per_node: 4
total_epochs: 1
total_training_steps: 10
save_freq: 10
test_freq: -1
trainer_device: null         # 仅 sft 使用(通常 "cpu"

说明:

  • trainer_device 仅对 sft 生效(通常为 cpu,避免 driver 无 GPU
  • val_file 可为 null(例如 SFT

4. API 端点

4.1 提交任务

POST /api/v2/tasks

Request body

  • raw JobSpec YAML(与 v1.1 jobspec YAML 结构一致)

Headers

  • Content-Type: application/yaml(或 text/yaml

Response

{
  "task_id": "mvp2-ppo-20251223-143201-7f3a",
  "state": "QUEUED"
}

4.2 查询任务(聚合状态)

GET /api/v2/tasks/{task_id}

Response示例

{
  "task_id": "mvp2-ppo-20251223-143201-7f3a",
  "workload": "ppo",
  "state": "RUNNING",
  "desired_resources": {"nnodes": 2, "n_gpus_per_node": 4, "total_gpus": 8},
  "latest_attempt": {
    "attempt_no": 1,
    "ray_submission_id": "mvp2-ppo-20251223-143201-7f3a--a01",
    "ray_status": "RUNNING",
    "start_time": "2025-12-23T14:32:10+08:00"
  },
  "error_summary": null
}

4.3 列出 attempts

GET /api/v2/tasks/{task_id}/attempts

Response

{
  "task_id": "mvp2-ppo-20251223-143201-7f3a",
  "attempts": [
    {
      "attempt_no": 1,
      "ray_submission_id": "mvp2-ppo-20251223-143201-7f3a--a01",
      "ray_status": "FAILED",
      "failure_kind": "INSUFFICIENT_RESOURCES",
      "message": "Total available GPUs 0 is less than total desired GPUs 8",
      "start_time": "...",
      "end_time": "..."
    }
  ]
}

4.4 取消任务

POST /api/v2/tasks/{task_id}:cancel

行为:

  • 若 task 处于 SUBMITTED/RUNNING:调用 Ray Jobs SDK stop_job(ray_submission_id) 并标记 CANCELED
  • 若处于 QUEUED/PENDING_RESOURCES:直接标记 CANCELED(不提交)

Response

{"task_id":"...","state":"CANCELED"}

4.5 获取日志

GET /api/v2/tasks/{task_id}/logs?attempt=latest&tail=2000

返回:

  • text/plain(直接透传 Ray Job logs tail

说明:

  • v2.0 先用 Ray SDK get_job_logs()
  • 若需要更稳定的归档,可在 scheduler 定期抓取并落盘v2.1+)。

4.6 列出队列(运维/调试)

GET /api/v2/queue

Response

{
  "pending": [{"task_id":"...","state":"PENDING_RESOURCES","next_run_at":"..."}],
  "running": [{"task_id":"...","ray_submission_id":"..."}]
}

5. 错误码(最小)

  • 400jobspec 缺字段/非法
  • 401token 不正确
  • 404task 不存在
  • 409:状态冲突(例如已终态又 cancel
  • 500:服务内部错误

6. SQLite 持久化API 可见性)

v2.0 服务端使用 SQLite 持久化保存:

  • taskstask_idstatejobspec_yamlnext_run_atlatest_attempt_no 等)
  • attemptsray_submission_idray_status、失败原因等)

因此:

  • GET /api/v2/tasks/{task_id} 的数据来自 SQLite再叠加 Ray 状态同步的结果)。
  • 进程重启后,队列可恢复,PENDING_RESOURCES 的任务会在 next_run_at 到期后继续尝试提交。