359 lines
20 KiB
Markdown
359 lines
20 KiB
Markdown
# 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 Management(SFTPGo)**
|
||
- 提供用户上传/下载入口(SFTP 为主)。
|
||
- 数据落到 GPFS(dev 环境 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 Server(FastAPI)**
|
||
- 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 新增组件:SFTPGo(Data 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-delete):job 结束后 **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 API:SFTPGo 只是用户访问协议层(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)
|
||
|
||
**方案 A(v3.0 推荐):API Server 负责“联动创建 SFTPGo 用户”**
|
||
- 在 v2.5 的 `POST /api/v2/users` 成功后:
|
||
- API server 调用 SFTPGo 管理 API 创建同名用户
|
||
- 设置 home dir = `/private/users/<user_id>`
|
||
- 设置权限(默认可写;是否只读可配置)
|
||
- 认证方式:
|
||
- v3.0 最小可用:用户名+密码(确认:v3.0 先 password;API 生成一次性密码,用户首次登录后要求改密)
|
||
- 或:SSH public key(WebUI 允许上传 public key,API 写入 SFTPGo)
|
||
|
||
**方案 B(更强但复杂):SFTPGo 外部认证**
|
||
- SFTPGo 把认证委托给 API server(token/SSO),SFTP 也走内部 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、state(QUEUED/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`
|
||
- 明确 retention:jobs 结束后 3 天移入回收站,回收站 7 天后删除;重要文件请移到 `models/` 或 `datasets/`
|
||
7) **(仅管理员可见)用户管理页**(`/ui/admin/users`,可选但很有价值)
|
||
- 创建用户、禁用用户、签发 token、重置 SFTP 密码(方案 A)
|
||
|
||
### 5.2 页面组织与导航(建议)
|
||
侧边栏导航(普通用户):
|
||
- Tasks(列表)
|
||
- New Task(新建)
|
||
- Data(SFTP/目录说明)
|
||
|
||
管理员侧边栏额外增加:
|
||
- 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 后台线程**。
|