2026-08-20 20:11:06 +08:00
2026-08-14 13:15:18 +08:00
2026-08-20 20:11:06 +08:00
2026-08-14 19:51:58 +08:00
2026-08-11 16:12:24 +08:00
2026-08-14 14:40:34 +08:00
2026-08-14 20:55:59 +08:00
2026-08-20 10:26:44 +08:00

Model Platform

自托管的 Jupyter 模型开发平台:交互式 workspace、DAG 调度、对象存储工件、 按 workspace 隔离的运行时 — 全部经一个 Nginx 网关对外。

技术栈:React Router SPA · FastAPI · APScheduler · MySQL · S3 兼容存储 (或本地文件系统,STORAGE_BACKEND=local)· 共享 Jupyter · rclone FUSE 挂载(仅 s3 模式) 单一入口(Nginx :80);其他服务只在 Docker 内网互通。

它做什么

能力 位置
workspace 内 notebook 编辑,行级锁 backend/jupyter.py + scripts.is_locked
Jupyter 鉴权路由(浏览器永远拿不到 runtime token nginx/default.conf + auth_request + backend/jupyter.py
notebook / script / version / run_log 的对象存储(s3 / local 二选一) common/storage/ + backend/scripts.py
DAG 调度:节点、边、cron、手动触发、重试、快照 backend/schedules.py + backend/schedule_runs.py + schedule/5 个模块)
DAG 执行走 MySQL Outbox(无 Redis,无进程内队列) schedule/orchestrator.py + schedule/worker.py
每个 workspace 一个 Jupyter 子进程池,配 asyncio 锁 runtime/process.py
runtime 内 rclone FUSE 把 workspace 桶挂上来(s3 模式) runtime/mount.py
仅 MySQL 持久化(26 张表,软删除,无外键) common/db/models/

架构一览

                        ┌────────────────────┐
                        │  Browser (SPA)     │
                        └─────────┬──────────┘
                                  │ HTTPS / WS
                        ┌─────────▼──────────┐
                        │  Nginx (only :80) │  ← templates/default.conf
                        │   /api/  /jupyter/ /storage/
                        └────┬───────┬──────┘
                             │       │
              ┌──────────────┘       └─────────────┐
              ▼                                     ▼
   ┌──────────────────┐               ┌──────────────────────┐
   │ FastAPI Backend  │               │ Runtime (Jupyter)     │
   │ + /internal/v1   │   control     │ - rclone FUSE mount  │ (P0-1)
   │   /objects       │   token-Auth  │                       │
   │ (storage)        ├──────────────►│ - subprocess pool    │
   │ - DAG CRUD       │               │   (per workspace)    │
   │ - script CRUD    │               └──────────┬───────────┘
   │ - auth_request   │                          │ FUSE / shared vol
   │ - /api/v1/...    │                          ▼
   └────┬──────┬──────┘               ┌──────────────────────┐
        │      │                       │ Object storage      │
        │      └──────── HTTP ───────►│ (s3: S3 service /    │
        ▼                              │  local: shared vol) │
   ┌────────────┐                      │ 4 buckets per usage │
   │  MySQL     │◄───────── poll ─────│                      │
   │ - 26 tbls  │                      └──────────────────────┘
   │ - outbox   │
   │ - jobstore │
   └────┬───────┘
        ▲
        │ outbox poll
   ┌────┴──────────────────────────┐
   │ Schedule Executor             │
   │ - CronScheduler (APScheduler) │
   │ - DispatchOrchestrator        │
   │ - NodeExecutor (worker)       │
   │ - SchedulerService (facade)   │
   └───────────────────────────────┘

对象存储通过 STORAGE_BACKENDs3 | local)二选一。s3 模式下 4 个 purpose 命名桶 workspace / version / run-log / trash)是独立的 S3 bucketlocal 模式下 是 LOCAL_STORAGE_BASE_DIR 的子目录,通过 Docker volume local-storage 共享。 详见 DEVELOP.md §存储。

详细设计见 ARCHITECTURE.md。实现的偏离和近期重构记录在 HANDOVER.md

目录结构

frontend/                    React Router SPA
backend/                     FastAPI:公开 API + 内部存储 API
runtime/                     Jupyter 子进程管理 + rclone FUSE
schedule/                    DAG 调度器(5 模块:context/scheduler/
                             orchestrator/worker/service
common/                      配置、SQLAlchemy 模型、存储 SDK、
                             outbox 事件、jobstore
migrations/                  Alembic 基线 + 各特性 migration
nginx/                       (仅概念 — 见下方「容器」一节)
scripts/                     nginx-entrypoint.sh(模板渲染)
docker-compose.yml           4 服务 — web / backend / runtime / schedule
default.conf                 Nginx 模板(挂载,启动时渲染)
.env.example                 common.config.Settings 消费的所有环境变量

容器

服务 镜像 暴露 用途
web nginx:alpine 宿主机 :8888:80 SPA、/api/ 反向代理、/jupyter/{ws}/ auth_request 代理、/storage/ S3 直通(仅 s3 模式)
backend Dockerfile 仅内网 DAG CRUD、script CRUD、schedule 触发、/api/v1/auth/jupyter/internal/v1/objects 服务间 RPC(共享 INTERNAL_SERVICE_TOKEN 鉴权,P0-1)
runtime Dockerfile 仅内网 每个 workspace 一个 Jupyter 子进程池、rclone FUSE 挂载 workspace 桶(s3 模式)
schedule Dockerfile 仅内网 cron tick + DAG 执行(轮询 MySQL Outbox

架构故意只暴露一个宿主机端口(网关);其他服务都在 Docker 内网。 这一点在 docker-compose.yml 里强制执行 — backend / runtime / schedule 都没有 ports:。在 P0-1 之前,backend 与 runtime 曾短暂地把 8891 / 8892 映射到宿主机;此映射已被删除,改用 INTERNAL_SERVICE_TOKEN 头对 /internal/v1/* 做服务间鉴权,见 API.md §9

快速启动

cp .env.example .env
# 编辑 .env — 至少改 MYSQL 密码,以及(s3 模式下)S3 凭据。

# 静态检查
uv sync --all-packages
uv run --frozen --package backend python -m compileall -q backend/src common/src
uv run --frozen --package schedule python -m compileall -q schedule/src
uv run --frozen --package runtime python -m compileall -q runtime/src

# 应用 schema
uv run --frozen --package backend alembic upgrade head

# 启动整套服务
docker compose config          # 校验
docker compose up -d --build
docker compose ps

访问 http://localhost:8888

日志

docker compose logs -f backend
docker compose logs -f schedule
docker compose logs -f runtime

停服(保留 MySQL + S3 / local-storage 数据卷)

docker compose down

抹数据

docker compose down -v

配置

所有环境变量在 common/src/common/config.py 里用 pydantic-settings 的 Settings 类一次性声明,外面套一层 @lru_cache 单例。新增环境变量:

  1. common/src/common/config.pySettings 里加字段(带合理 default,使 dev 启动不需要设)
  2. .env.example 加一行带注释
  3. 调用点用 settings.<name>,永远不要用 os.environ["..."]

完整环境变量列表和含义见 DEVELOP.md

存储布局

4 个 purpose 命名桶。从 StorageObjects.usage_type 到桶的映射由 单一入口backend/storage_api.py:resolve_bucket)决定:

usage_type 桶(环境变量) 默认名
working_copypublic_scriptdata_resourcesnapshot S3_WORKSPACE_BUCKET workspace
version_artifact S3_VERSION_BUCKET version
run_logrun_result S3_RUN_LOG_BUCKET run-log
(软删除目标) S3_TRASH_BUCKET trash

STORAGE_BACKEND=s3 模式下是 4 个独立 S3 桶。STORAGE_BACKEND=local 模式下 是 LOCAL_STORAGE_BASE_DIR(默认 /data)下的 4 个子目录:

/data/
├── workspace/   # S3_WORKSPACE_BUCKET
├── version/     # S3_VERSION_BUCKET
├── run_log/     # S3_RUN_LOG_BUCKET
└── trash/       # S3_TRASH_BUCKET

某个 workspace 的 artifact_bucket 列(非 NULL 时)覆盖该 workspace 的默认桶, 无视 usage_type — 适合把付费客户隔离到专属桶。

对象 key 是两层扁平路径 — workspace_id 加服务端签发的 ulid

<workspace_bucket>/<workspace_id>/<ulid>{.<ext>}

文件名、扩展名、MIME、逻辑路径都放在 StorageObjectsScripts 行里,不进 object key — 重新组织存储不需要重写数据库。

Backend 代码从不写容器本地文件系统(STORAGE_BACKEND=local 模式除外,那里共享 local-storage volume 就是规范存储)。Schedule Executor 在 tempfile.TemporaryDirectory() 里暂存节点工件(自动清理)。只有 runtime 容器保留宿主 volume — s3 模式下 rclone FUSE 挂载需要;local 模式下是 no-op 透传。

文档

  • README.md(本文)— 快速导读
  • ARCHITECTURE.md — 设计图 + 简化历史
  • HANDOVER.md — 实现偏离、近期重构、待办事项
  • DEVELOP.md — 开发指南(环境变量、代码规约、常用操作)
  • CLAUDE.md — agent 面向的本仓库规约

许可

内部。

S
Description
No description provided
Readme
5.2 MiB
Languages
Python 49.8%
TypeScript 49.2%
Dockerfile 0.4%
CSS 0.4%