5.2 KiB
5.2 KiB
MVP v2.0 API 设计(最小可用)
v2.0 的 API 目标是:把 v1.1 的“脚本提交”变成“服务化提交”,并在服务侧实现队列/重试/状态聚合。
约束:
- 内部 token 鉴权(简单即可)。
- Ray Job 提交必须使用 Ray Python SDK(
JobSubmissionClient),不使用requests手写 HTTP。 - 输出与状态必须落盘到 NFS(容器内
/private)。
1. 鉴权
- Header:
Authorization: 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.shsrc/mvp/v2.0/scripts/21_stop_api.shsrc/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_id(attempt 级 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 SDKstop_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. 错误码(最小)
400:jobspec 缺字段/非法401:token 不正确404:task 不存在409:状态冲突(例如已终态又 cancel)500:服务内部错误
6. SQLite 持久化(API 可见性)
v2.0 服务端使用 SQLite 持久化保存:
- tasks(
task_id、state、jobspec_yaml、next_run_at、latest_attempt_no等) - attempts(
ray_submission_id、ray_status、失败原因等)
因此:
GET /api/v2/tasks/{task_id}的数据来自 SQLite(再叠加 Ray 状态同步的结果)。- 进程重启后,队列可恢复,
PENDING_RESOURCES的任务会在next_run_at到期后继续尝试提交。