Files
cube/README.md
T
Fam Zheng 011e7ddb98 README: 平台约定(栈/部署/CI 一锅端)
把脚手架决策定下来,作为 cube 上所有 app 必须遵守的"宪法":

- 域名:<app>.famzheng.me,零 DNS 操作
- 后端:Rust + Axum,每 app 独立仓库,cube-core crate 复用样板
- 前端:Vite + Vue 3 + TS(选 Vue 是因为 AI 写得稳)
- 构建:host musl 编译 + scratch 容器
- Registry:gitea 自带(registry.famzheng.me 反代 /v2/*)
- CI:gitea Actions + 现有 instance-level act_runner
- 通用约定:/healthz, stdout JSON log, env+Secret 配置, 禁 config 文件挂载
2026-05-04 11:08:01 +01:00

129 lines
5.3 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.
# cube
Fam 的小 app 平台。
## 是什么
`cube` 是一个跑在 **`famzheng.me` 节点**hostname `famzheng.com`,单节点 k3s + traefik + gitea,公网 IP `178.104.186.206`)上的小 app 平台,专门收纳 Fam 自己写的一堆小工具/玩具 web app。底层硬件、k3s、Gitea、act_runner 等基础设施详见 `~/.claude/memory/infra.md`
主要任务:把目前散落在 `oci.euphon.net`Oracle Cloud ARM VM)上**值得留下**的 Fam 个人小 app 迁过来。oci 主机本身不退役,留给 Hera 同学继续用。
设计目标:反 Karpathy "web app 像拼宜家家具" 的困境 —— 全部自有基础设施,**零跨 dashboard 配置**,新 app 上线压缩到 5 分钟内。
---
## 平台约定
> 这是 cube 上所有 app 的"宪法",所有 app 必须遵守,破例必须改这份文档并经 Fam 拍板。
### 部署目标
单一目标:famzheng.me 节点的 k3s`kubectl context default`),不双轨。
- 每 app 一个 k8s namespacens 名 = app 名,不加 cube- 前缀)
- traefik ingress + 通配符 LE 证书自动签
### 域名
- `<app>.famzheng.me`wildcard A 记录已配,**零 DNS 操作**
- 不嵌 `cube` 子域(冲突检查 = 起新 ingress 时 traefik 自然报错,不需要额外心智)
- 旧域名(如 `portfolio.oci.euphon.net`)让 oci ingress 兜底 308 redirect 到新地址,过渡期后下掉
### 后端:Rust + Axum,每 app 独立仓库
- gitea repo `fam/<app>`,单 axum 服务
- 公共代码通过 `cube-core` crate 复用:`cube-core = { git = "https://famzheng.me/gitea/fam/cube-core", tag = "v0.x" }`
- `cube-core` 提供:
- `/healthz` router200 = ok
- `ServeDir` 静态前端 fallback 到 `index.html`
- `tracing` 配 JSON stdout
- SIGTERM graceful shutdown
- env → struct 配置加载(`envy` crate
- 业务 app 只写 `/api/*` 路由 + handler
```rust
Router::new()
.merge(cube_core::base("dist"))
.nest("/api", api_routes())
```
- 不上 cargo workspace —— 每 app 一个 repo 彻底分
### 前端:Vite + Vue 3
- 默认栈:**Vite + Vue 3 + TypeScript + Pinia + Vue Router**
- 选 Vue 而不是 Svelte 的原因:AIClaude / GPT)写 Vue 3 `<script setup>` + Composition API 训练语料密度大、bug 少;Svelte 5 runes 出来后 AI 经常混 4/5 语法
- build 输出到 `frontend/dist/`axum 用 `ServeDir` 同进程同容器同域名 serve
- **不搞前后端分离部署**、不搞独立前端域名、不搞独立 CI
### 构建:host musl + scratch 容器
host 上直接 cargo build,不在容器里跑 cargo
```bash
cargo build --release --target x86_64-unknown-linux-musl
(cd frontend && npm ci && npm run build)
```
Dockerfile
```dockerfile
FROM scratch
COPY target/x86_64-unknown-linux-musl/release/<app> /app
COPY frontend/dist /dist
ENTRYPOINT ["/app"]
```
- 镜像 < 20MB
- runner 上不跑 docker buildx 套娃,build 速度爆表
- prereq(首次设置 host):`apt install musl-tools` + `rustup target add x86_64-unknown-linux-musl`
### Container Registry
- 用 **gitea 自带** Container RegistryPackages 功能,gitea 1.20+ 自带)
- **坑**gitea 挂在 `/gitea/` 子路径下,docker daemon 默认拼 `https://famzheng.me/v2/...` 会 404
- 方案:加一条 ingress `registry.famzheng.me` 反代 `/v2/*` → `gitea-svc:3000/v2/*`,复用 gitea token 鉴权
- 镜像命名:`registry.famzheng.me/fam/<app>:<sha>`
- **未实施**:第一个 app 上线时再搞 ingress;过渡期可本地 build 直接 `docker save` + `k3s ctr image import`
### CI/CD
走 gitea Actions,复用现有 instance-level act_runnergnoc 用户,host shell 模式,labels `*:host`**新 repo 不用注册 runner**)。
每个 app repo 一份 `.gitea/workflows/deploy.yml`,固定 5 步:
1. `cargo build --release --target x86_64-unknown-linux-musl`
2. `(cd frontend && npm ci && npm run build)`
3. `docker build -t registry.famzheng.me/fam/<app>:$GITHUB_SHA .`
4. `docker push registry.famzheng.me/fam/<app>:$GITHUB_SHA`
5. `kubectl -n <app> set image deploy/<app> <app>=registry.famzheng.me/fam/<app>:$GITHUB_SHA`
host shell PATH 注意:workflow 第一行 `export PATH="$HOME/.cargo/bin:$PATH"`gnoc 的 rustup 装在 `~/.cargo`)。
**不做 PR 预览环境** —— 个人小 app 不需要,徒增复杂度。
### 通用约定(每个 app 都要遵守)
- 暴露 `/healthz`200 = ok),k8s liveness/readiness 共用同一个 endpoint
- 日志统一 stdout`cube-core` 的 tracing 配 JSON),让 k3s 收
- 配置走 env var + k8s Secret**禁止** config.yaml 文件挂载(partiverse 那个"空目录顶替 config 文件"的坑别再踩)
- 数据持久化只走 PVC + 每天 minio backup CronJob`cube-core` 提供 base 模板)
- 镜像 tag 用 git SHA,不用 `latest`
---
## 当前状态
- 仓库刚初始化(2026-05-04),脚手架/cube-core 都还没动手。
- 迁移源端清单见 [`doc/todo.md`](doc/todo.md)。
## 迁移名单(截至 2026-05-04
- `portfolio`host systemd `portfolio.service`uvicorn `:8890`,**不在 k3s 里**;迁过来要重写成 Rust + axum 还是直接装 python 运行时?待定)
- `repo-vis`
- `simpleasm`
- `guitar`
- `pyroblem`(详情待补)
## 相关
- 宿主节点:`famzheng.me` / hostname `famzheng.com` —— 详见 `~/.claude/memory/infra.md`
- 迁移源:`oci.euphon.net`ARM Ubuntu 22.04 + k3s