argus-cluster/specs/mvp/v3.0/v3.0_design.md

359 lines
20 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 v3.0 详细设计方案(基于 v2.5
## 0. 结论摘要v3.0 要交付什么)
v3.0 = v2.5 + **WebUI** + **用户数据上传/下载SFTPGo**,形成第一个可对外发布的版本:
- 用户可以通过 **SFTP** 上传数据/模型/代码(至少数据),落到 GPFS容器内 `/private`)并对 Ray worker 可见。
- 用户可以通过 API/WebUI 提交训练任务,任务读取自己上传的数据。
- 用户可以下载训练产物checkpoints/logs 等),最小闭环跑通。
## 1. 范围与原则
### 1.1 继承 v2.5 的前提(不回退)
- **Stateless Ray Node Pool**head 写 `head.json`worker watchdog 自动 join/自愈。
- **User Management**token 鉴权、任务可见性隔离(跨用户 404 不泄漏)。
- **作业产物隔离**Ray job 目录落到 `/private/users/<user_id>/jobs/<ray_submission_id>/...`
- **API server 短期运行方式**:代码在宿主机,挂载到 head 容器,在 head 容器内启动(保持现状)。
### 1.2 v3.0 新增目标
1) **Data ManagementSFTPGo**
- 提供用户上传/下载入口SFTP 为主)。
- 数据落到 GPFSdev 环境 NFS/GPFS生产环境 GPFS训练 job 在 worker 容器内可直接读取。
2) **WebUI**
- 用户可视化创建任务、查看队列/状态/日志、查看“数据路径约定”和自己的 SFTP 信息。
- 目标是 “可用而非豪华”,支持核心工作流。
3) **权限闭环**
- 用户只能使用自己目录下的数据(`/private/users/<user_id>/...`)或公共目录(`/private/common/...`)。
- 防止用户提交任务读取其他用户的文件路径。
### 1.3 v3.0 明确不做(留给 v3.5
- 不做 “自定义 reward function / 自定义 verl 代码 / 多版本 verl 共存”(路线图 v3.5)。
- 不做复杂 Serving/训推一体(路线图 v3.5)。
- 不做 IB 网络/拓扑优化(路线图 v3.5)。
- 不做系统级可观测性平台(路线图 v4.0)。
## 2. 架构概览
参考 `roadmap_v3.0.png`v3.0 的控制面与数据面:
### 2.1 控制面Control Plane
- **API ServerFastAPI**
- v2.5 的任务队列/调度/重试 + 用户管理能力继续复用
- 新增:数据管理能力(与 SFTPGo 对接) + WebUI
- **WebUI**
- 通过 API 使用 token 登录
- 提供任务/日志/数据入口(不直接运行训练)
- **Ray Head状态节点**
- 仍在 head 容器内(或单独节点)
- job server/dashbaord 提供 job submit/status/logs 能力
### 2.2 数据面Data Plane
- **GPFS容器内挂载 `/private`**
- 存放 common 与 users 两大根目录
- **Ray Worker Node无状态**
- 自动连接 head执行训练
- 读取 `/private/users/<user>/...` 的数据
### 2.3 新增组件SFTPGoData Management
- 作为独立服务运行(容器化优先),后端存储使用 **filesystem**GPFS 挂载路径)。
- 用户的 home directory 指向 `/private/users/<user_id>`(或其子目录)。
## 3. 存储与目录规范v3.0 统一约定)
### 3.1 目录层级
统一以容器内 `/private` 作为根路径dev/prod 对齐):
- `/private/common/`:公共资源
- `hf/`HF cache
- `datasets/`:公共数据集(可选)
- `code/`:公共代码(例如公共 verl repo snapshot
- `db/`SQLite队列、用户、token
- `logs/`API/supervisor/watchdog 日志
- `/private/users/<user_id>/`用户空间v3.0 重点)
- `datasets/`:用户上传的数据集(推荐)
- `models/`:用户保存/上传的本地模型(允许;也用于“把 job 产物移动到长期保存目录”)
- `code/`用户上传的代码v3.0 **不支持执行**;仅存放/下载)
- `jobs/`:训练任务产物(已在 v2.5 落地)
- `tmp/`:临时文件(可选)
### 3.2 Jobs Retention两段式3 天移入回收站7 天后永久删除)
v3.0 引入 **jobs 目录两段式保留策略**
- 第 1 阶段soft-deletejob 结束后 **3 天**,将该 job 目录从 `jobs/` **移动到用户回收目录**
- 第 2 阶段hard-delete进入回收目录后再过 **7 天**,从回收目录 **永久删除**
目录约定(建议):
- jobs 根目录:`/private/users/<user_id>/jobs/<ray_submission_id>/...`
- 回收目录:`/private/users/<user_id>/trash/jobs/<ray_submission_id>/...`
计时规则:
- 以 job 进入 terminal 状态SUCCEEDED/FAILED/CANCELED的结束时间为起点
- “3 天”用于从 `jobs/` 移入 `trash/jobs/`
- “7 天”用于从 `trash/jobs/` 永久删除(即总共最多 10 天窗口)。
用户保留关键产物的方式(无需 keep 标记):
- 在 “3 天窗口”内把需要长期保存的文件从 `jobs/<submission_id>/...` **移动/复制**到 `models/`(例如权重)或 `datasets/`(例如评估输出数据);
- 即便已被移动到回收目录,用户仍可在 “7 天窗口”内从 `trash/jobs/<submission_id>/...` 把需要的文件移到 `models/` / `datasets/`
- janitor 只管理 `jobs/``trash/jobs/`,不会触碰 `models/``datasets/`
这里的“清理程序”我们称为 **janitor**
- 定义:一个后台清理执行器,按固定周期扫描“已结束且已过期”的 job 目录并删除
- v3.0 目标实现“3 天移入回收站 + 7 天后删除”这一条产品规则(不提供 keep/延长保留标记)
实现建议(按你的偏好):
- **janitor 作为 API server 内置后台线程**运行:
- 优点:天然可访问 SQLite任务状态、结束时间、user_id、ray_submission_id并能把清理结果写回 events 表用于审计
- 部署更简单:不额外引入 cronjob/独立服务
- 删除/移动动作建议 **直接在 GPFS/NFS 文件系统上操作**API server 运行在 head 容器,已挂载 `/private`
- 第 1 阶段:`os.rename`(同文件系统原子移动)把 `jobs/<sid>` 移到 `trash/jobs/<sid>`
- 若跨文件系统(理论上不应发生),则降级为 copy+delete
- 移动前做严格路径前缀校验(必须在 `.../users/<u>/jobs/` 下)。
- 第 2 阶段:对 `trash/jobs/<sid>` 执行递归删除(例如 `shutil.rmtree`),同样做路径前缀校验(必须在 `.../users/<u>/trash/jobs/` 下)。
- 为什么不依赖 SFTPGo APISFTPGo 只是用户访问协议层SFTP/Web目录物理就在同一份文件系统文件系统直连更简单、也不依赖 SFTPGo 在线。
- 如果你强烈希望“通过 SFTPGo API 删除”:
- 可以作为可选实现/补充(例如用于统一审计或未来接入配额/策略但不建议作为唯一手段SFTPGo 停机不应阻塞清理)。
### 3.3 用户在 SFTPGo 内移动/整理文件(确认点)
支持用户在 SFTPGo 中进行“移动/重命名/整理”(例如把权重从 `jobs/` 移动到 `models/`
- 前提SFTPGo 用户权限允许对其 home 目录进行 `rename/mkdir/remove` 等操作v3.0 默认可写)。
- 行为:用户可以把 `jobs/` 下某些文件移动到 `models/``datasets/`,用于长期保存权重/评估产物等。
- 与 retention 的关系:只要文件被移动出 `jobs/`,就不会被 jobs 清理逻辑删除。
### 3.4 路径权限规则API 侧校验)
v2.5 约束是 “只允许 `/private/common/...`”。
v3.0 需要升级为:
- 允许:
- `/private/common/...`
- `/private/users/<current_user_id>/...`
- 禁止:
- 任何其他绝对路径(例如 `/private/users/other/...``/etc/...`
并把该规则应用到 TaskSpec 的相关字段(至少):
- `train_file` / `val_file`
- `code_path`:仍仅允许 `/private/common/...`v3.0 不支持执行用户 code
- 本地模型路径字段:允许 `/private/users/<me>/models/...`确认v3.0 允许)
## 4. SFTPGo 方案设计Data Management
### 4.1 运行形态
推荐用容器运行 SFTPGo与 Ray/API 解耦),挂载同一份 `/private`
- `sftpgo` 容器挂载 `../../shared:/private`
- 对外暴露:
- SFTP 端口(建议 2022
- WebAdmin/API 端口(建议 8081仅内网或管理员访问
#### 4.1.1 镜像来源(现成 Docker 镜像)
SFTPGo 有现成可用的 Docker 镜像(无需自建):
- v3.0 推荐优先使用官方/上游发布的 `sftpgo` 镜像作为运行基座
- 我们在 v3.0 里不需要定制 SFTPGo 代码,只需要:
- 正确挂载 GPFS/NFS容器内 `/private`
- 配置管理员账号(用于 API server 联动创建/禁用用户、重置密码)
- 配置每用户 home/chroot
> 注意:具体镜像名/tag 在不同环境可能有差异(官方/镜像仓库策略会变动)。落地时建议在 `argus@h1` 上 `docker search sftpgo` 或由你们内部镜像仓库提供固定版本v3.0 设计只要求“使用现成镜像”,不强依赖某个 tag。
#### 4.1.2 docker-compose 服务草案(示意)
下面给出一个**示意**(最终以实际镜像名/tag 与你们端口规划为准):
```yaml
services:
sftpgo:
image: sftpgo/sftpgo:latest # 示例:使用现成镜像
container_name: argus-sftpgo
ports:
- "2022:2022" # SFTP
- "8081:8080" # WebAdmin/API建议仅内网/管理员)
volumes:
- ../../shared:/private
- ../../shared/common/sftpgo:/var/lib/sftpgo # 持久化 SFTPGo 元数据(可选/建议)
environment:
# 管理员账号/密码(示意,具体变量名以镜像文档为准)
SFTPGO_ADMIN_USERNAME: "admin"
SFTPGO_ADMIN_PASSWORD: "${SFTPGO_ADMIN_PASSWORD}"
```
与 v3.0 的配合点:
- API server 使用 `data.sftpgo.admin_api_base` + admin 凭据联动创建用户
- 用户 home/chroot 统一指向 `/private/users/<user_id>`
### 4.2 用户隔离
每个用户在 SFTPGo 中的 home dir 绑定到:
- `/private/users/<user_id>`chroot用户只能读写自己的目录。
### 4.3 用户创建与凭据管理(两种实现,建议先做 A
**方案 Av3.0 推荐API Server 负责“联动创建 SFTPGo 用户”**
- 在 v2.5 的 `POST /api/v2/users` 成功后:
- API server 调用 SFTPGo 管理 API 创建同名用户
- 设置 home dir = `/private/users/<user_id>`
- 设置权限(默认可写;是否只读可配置)
- 认证方式:
- v3.0 最小可用:用户名+密码确认v3.0 先 passwordAPI 生成一次性密码,用户首次登录后要求改密)
-SSH public keyWebUI 允许上传 public keyAPI 写入 SFTPGo
**方案 B更强但复杂SFTPGo 外部认证**
- SFTPGo 把认证委托给 API servertoken/SSOSFTP 也走内部 token。
- 复杂度高,建议 v3.0 不做,放到 v3.5 或更后。
### 4.4 用户上传/下载体验
用户通过 SFTP 上传:
- `datasets/...`(训练数据)
- `models/...`(本地模型,可选)
下载:
- `jobs/<ray_submission_id>/...`checkpoints/logs
WebUI/文档提供 “路径如何写进 TaskSpec” 的指引。
## 5. WebUI 方案设计(最小可用)
### 5.1 目标页面
v3.0 WebUI 采用“**多子页面 + 侧边导航栏**”而不是把所有功能挤到单页:
- 原因信息密度更可控后续可扩展v3.5+)且不会把一个页面做成“巨型表单/巨型列表”。
- 实现仍保持轻量:服务端渲染(或静态 HTML + 少量 JS不引入复杂前端工程。
信息架构IA建议如下
1) **登录页**`/ui/login`
- 用户粘贴 token管理员发放浏览器保存localStorage/sessionStorage
- 提供“退出登录/清空 token”
2) **任务列表页**`/ui/tasks`
- 默认列表:最近 N 条任务(按 created_at 倒序)
- 支持过滤workload、stateQUEUED/RUNNING/SUCCEEDED/FAILED/CANCELED、时间范围
- 支持快捷操作:进入详情、取消任务
3) **新建任务页**`/ui/tasks/new`
- 两种模式(二选一,均可实现):
- **YAML 直接提交**:上传/粘贴 TaskSpec YAML最省开发
- **表单生成 YAML**:选择 workload填写核心字段train/val/model/nnodes/gpus生成 YAML 预览后提交
- 提交后跳转到任务详情页
4) **任务详情页**`/ui/tasks/{task_id}`
- 顶部task_id、workload、state、created_at、updated_at、error_summary
- Attempt 卡片latest attempt_no、ray_submission_id、ray_status、start/end
- 操作区:取消任务(若非 terminal、刷新状态、复制路径/ID
- 链接到日志页与产物提示SFTP 路径)
5) **任务日志页**`/ui/tasks/{task_id}/logs`
- 默认 tail=2000可选 200/1000/5000
- 提供“自动刷新(每 3~5 秒)”开关(简单轮询即可)
6) **数据页**`/ui/data`
- 显示 SFTP 连接信息host/port/username
- 显示用户目录约定:
- home`/private/users/<user_id>`
- datasets`/private/users/<user_id>/datasets`
- models`/private/users/<user_id>/models`
- jobs`/private/users/<user_id>/jobs`
- trash/jobs`/private/users/<user_id>/trash/jobs`
- 明确 retentionjobs 结束后 3 天移入回收站,回收站 7 天后删除;重要文件请移到 `models/``datasets/`
7) **(仅管理员可见)用户管理页**`/ui/admin/users`,可选但很有价值)
- 创建用户、禁用用户、签发 token、重置 SFTP 密码(方案 A
### 5.2 页面组织与导航(建议)
侧边栏导航(普通用户):
- Tasks列表
- New Task新建
- DataSFTP/目录说明)
管理员侧边栏额外增加:
- Admin / Users
### 5.3 大致示意图wireframe
下面是一个粗略示意(非最终 UI仅表达信息结构与布局
```
┌──────────────────────────────────────────────────────────────────────┐
│ Argus MVP v3.0 [user: alice] │
├───────────────┬──────────────────────────────────────────────────────┤
│ Side Nav │ /ui/tasks │
│ │ │
│ • Tasks │ [Filter] workload=all state=all [Search task_id] │
│ • New Task │ │
│ • Data │ Task List │
│ • Admin(*) │ ┌────────────────────────────────────────────────┐ │
│ │ │ task_id workload state ... │ │
│ │ │ mvp2-alice-ppo-... ppo RUNNING ... │ │
│ │ │ mvp2-alice-sft-... sft SUCCEEDED... │ │
│ │ └────────────────────────────────────────────────┘ │
│ │ [View] [Cancel] │
└───────────────┴──────────────────────────────────────────────────────┘
```
任务详情页(示意):
```
┌──────────────────────────────────────────────────────────────────────┐
│ /ui/tasks/{task_id} │
├──────────────────────────────────────────────────────────────────────┤
│ task_id: mvp2-alice-ppo-... state: RUNNING workload: ppo │
│ created_at: ... updated_at: ... │
│ error_summary: (empty) │
│ │
│ latest_attempt: a01 ray_submission_id: ...--a01 ray_status: RUNNING │
│ [Open Logs] [Cancel Task] [Refresh] │
│ │
│ Artifacts (SFTP paths): │
│ jobs/: /private/users/alice/jobs/<ray_submission_id>/ │
│ trash/: /private/users/alice/trash/jobs/<ray_submission_id>/ │
│ tip: move important files to /private/users/alice/models/ │
└──────────────────────────────────────────────────────────────────────┘
```
### 5.2 技术取舍(建议:不引入 Node 构建)
为了降低部署复杂度,建议 v3.0 WebUI 以 “服务端渲染 + 少量 JS/HTMX” 或 “纯静态 HTML+fetch” 实现:
- 由 API server 提供静态资源FastAPI StaticFiles
- 页面调用同源 API避免跨域与复杂前端构建链
## 6. API 扩展设计(概览)
v3.0 可以保持 `/api/v2/...` 不变,增量加:
- SFTPGo 集成管理端点(管理员):
- 创建/禁用用户时联动 SFTPGo
- 重置 SFTP 密码 / 更新 SSH key
- 用户数据端点(可选,最小化):
- `/api/v2/me`:返回 user_id、SFTP 信息host/port/home
- `/api/v2/files`:仅用于浏览/下载(上传仍走 SFTP
详细见 `specs/mvp/v3.0/v3.0_api.md`
## 7. 配置与部署v3.0 新增项)
`configs/dev.yaml` 基础上扩展一组 `data` 配置(示意):
```yaml
data:
shared_root: "/private" # 通常与 ray.shared_root 一致
user_root: "/private/users" # 用户空间根目录
allow_common_prefix: "/private/common/"
allow_user_prefix_template: "/private/users/{user_id}/"
sftpgo:
enabled: true
host: "127.0.0.1"
sftp_port: 2022
admin_api_base: "http://127.0.0.1:8081/api/v2"
admin_user: "admin"
admin_password_env: "SFTPGO_ADMIN_PASSWORD" # 仅 head 容器内可读
retention:
jobs_trash_after_days: 3
jobs_purge_after_days: 7
trash_root_template: "/private/users/{user_id}/trash/jobs"
janitor_interval_s: 3600 # 每小时扫一次(可配置)
```
## 8. 风险点与对策
1) **路径逃逸/越权读取**
- 必须在 API 提交任务时校验路径前缀
- SFTPGo 必须 chroot 到用户 home
2) **大文件上传稳定性**
- 优先用 SFTP断点续传/可靠性更好)
3) **用户 token 与 SFTP 凭据的生命周期**
- token 走 v2.5 SQLite
- SFTP 凭据建议独立(密码/SSH key并提供 reset 流程
4) **GPFS/NFS 权限**
- 确保 `/private/users/<user>` 目录权限可被 SFTPGo 写入且 worker 可读
## 9. 已确认结论(来自你的反馈)
1) 允许用户上传并在训练时使用自定义数据集:允许(`/private/users/<u>/datasets/...`)。
2) 允许用户上传并在训练时使用本地模型路径:允许(`/private/users/<u>/models/...`)。
3) v3.0 不允许执行用户自定义代码(不注入 `PYTHONPATH` 作为可执行 code path
4) SFTPGo 认证方式v3.0 先 password。
5) WebUI按“简单最小必要功能”做token 粘贴登录优先)。
## 10. 待确认问题(需要你给结论)
已确认jobs 清理执行主体v3.0 采用 **API server 内置 janitor 后台线程**