Files
sub2api-cn-relay-manager/docs/DEPLOYMENT.md

232 lines
9.0 KiB
Markdown
Raw Normal View History

# Sub2API CN Relay Manager 部署指南
## 概览
Sub2API CN Relay Manager 是一个 Go 控制面服务,用于:
- 注册并探测 sub2api 宿主
- 安装 pack / 导入 provider
- 记录 import batch / managed resources / access closure / reconcile 结果
- 执行基于已记录资源集的回滚
当前内置运行面能力以最小生产闭环为主:
- 已内置:`/healthz`、SQLite 状态库、宿主注册与探测、导入/回滚/对账 API
- 未内置:`/metrics`、限流、配额治理、Prometheus/Grafana 集成
## 前置条件
| 组件 | 版本 | 说明 |
|---|---|---|
| Go | 1.22+ | 构建与本地运行 |
| SQLite | 3.40+ | 内嵌状态库,需持久化挂载 |
| Docker / Podman | 4.x+ | 本地容器验收可选 |
| 控制面 Admin Token | - | 调用控制面 API |
| 宿主 Admin 凭据 | - | 注册 host 时写入控制面,用于后续 import / reconcile / rollback |
## 快速启动
```bash
cp .env.example .env
# 至少设置 SUB2API_CRM_ADMIN_TOKEN
docker compose up --build -d
curl -fsS http://127.0.0.1:18081/healthz
```
或本地直接运行:
```bash
SUB2API_CRM_ADMIN_TOKEN=change-me-before-production SUB2API_CRM_LISTEN_ADDR=127.0.0.1:18081 SUB2API_CRM_SQLITE_DSN='file:/tmp/sub2api-cn-relay-manager.db?_foreign_keys=on&_busy_timeout=5000' go run ./cmd/server
```
## 关键配置
| 变量 | 说明 | 示例 |
|---|---|---|
| `SUB2API_CRM_ADMIN_TOKEN` | 控制面 Bearer token | `crm-admin-token` |
| `SUB2API_CRM_LISTEN_ADDR` | 监听地址 | `:18081` |
| `SUB2API_CRM_SQLITE_DSN` | SQLite DSN | `file:/tmp/sub2api-cn-relay-manager.db?_foreign_keys=on&_busy_timeout=5000` |
| `SUB2API_CRM_REPO_ROOT` | provider 草稿发布到 pack/provider 文件时使用的仓库根目录 | `/home/ubuntu/sub2api-cn-relay-manager-git-current` |
## 公网 Portal 资产
如果你还需要同时维护 `sub.tksea.top` 上的用户态 portal不要再把静态页或 Nginx 规则只放在 `/tmp`
- 静态页源码:`deploy/tksea-portal/index.html`
- Nginx 示例:`deploy/tksea-portal/nginx.sub.tksea.top.conf.example`
- 部署脚本:`scripts/deploy/deploy_tksea_portal.sh`
- 资产回归:`scripts/test/test_tksea_portal_assets.sh`
feat(deploy): add CRM-only online deployment to remote43 - scripts/deploy/deploy_crm_only.sh: 单进程部署 sub2api-cn-relay-manager CRM 控制面到 remote43,不依赖 sub2api host / PG / Redis 容器。 复用 scripts/deploy/remote43_patched_stack_lib.sh 的 env 渲染 (render_remote43_crm_env),render_crm_only_bootstrap 用 $\{VAR\} 占位符 + sed 注入避开 set -u + unquoted-heredoc 边缘问题。 部署前先 kill 老进程 (再 scp 二进制) 避免 ELF overwrite 失败。 - docs/DEPLOYMENT.md: 加 '在线部署节点' 段,记录 stack / 端口 / 入口 / 验证。 - docs/EXECUTION_BOARD.md: 顶部加 'Latest Online Stack' 段。 - artifacts/online-deploy-20260602/: 本次真实部署的证据 - 01-local-build.txt: 本地 server 二进制 md5 + git head - 02-remote-inspect.txt: 远端 process / port / db tables - 03-crm-api-checks.txt: /healthz /api/packs /api/hosts /metrics 真实响应 - 04-portal-public.txt: sub.tksea.top 公共入口 - 05-quality-gates.txt: gofmt / vet / test -race / integration - manifest.json: 结构化汇总 验证(2026-06-02 21:32-21:43): - /healthz: HTTP 200 'ok' - /api/packs (Bearer): HTTP 200 '{"packs":[]}' - /api/hosts (Bearer): HTTP 200 '{"hosts":[]}' - /api/packs (no auth): HTTP 401 - /metrics (Prometheus): HTTP 200,含 active_hosts/active_providers/ db_connections_active + Go runtime - sub.tksea.top/portal/: HTTP 200 - sub.tksea.top/portal-admin-api/healthz: HTTP 200 'ok'(反代到 CRM) - go test -race ./internal/... ./tests/integration/...: PASS - gofmt / go vet: 干净
2026-06-02 21:46:39 +08:00
- 浏览器级 smoke`scripts/test/verify_frontend_smoke.sh`
- 前端统一矩阵:`scripts/acceptance/verify_frontend_acceptance_matrix.sh`
- Provider Admin 页面验收:`scripts/acceptance/verify_provider_admin_actions.sh`
当前正式入口:
- `https://sub.tksea.top/portal/`
- `https://sub.tksea.top/portal/admin/`
- 管理首页
- 统一提供“新增模型 / 供应商目录”和“导入供应商帐号”入口
- 当前已支持管理员用户名 / 密码登录;登录成功后浏览器会持有同域 HttpOnly session cookie
- `https://sub.tksea.top/portal/admin/providers.html`
- provider 目录与 preview/import 管理页
- 当前已支持通过 `provider_drafts` API 把 provider manifest 草稿持久化到 CRM SQLite并直接更新 / 删除 / 发布到 pack 仓库
- `https://sub.tksea.top/portal/admin/batch-import.html`
- 结构化 batch-import 入口,当前跳到 legacy 最小管理页
- `https://sub.tksea.top/portal/admin-batch-import.html`
- 最小管理页
- 直接消费 `POST /api/batch-import/runs`
- 直接消费 `GET /api/batch-import/runs/{run_id}`
- 直接消费 `GET /api/batch-import/runs/{run_id}/items`
管理态同域代理:
- `https://sub.tksea.top/portal-admin-api/`
- 反代到 CRM
- 浏览器侧优先走管理员 session同时保留 Bearer admin token 兼容脚本与紧急兜底
- 作用是让静态 admin 页面不必直接访问 remote43 的内网 `18190`
生产稳定基线:
- 当前生产数据面固定为 `https://sub.tksea.top/v1` -> `127.0.0.1:8080`
- 当前生产控制面固定为 `https://sub.tksea.top/portal-admin-api/` -> `127.0.0.1:18190`
- `18169``18173``18191``18192``18193` 只能作为历史/验收栈端口,不得作为公网生产入口
- remote43 只应长期保留一套生产宿主;临时宿主用于验收后必须停止或清理
- 详细规则见 `docs/PRODUCTION_STABILITY_BASELINE.md`
管理员登录配置:
- `SUB2API_CRM_ADMIN_TOKEN`
- 必填
- 继续作为服务端管理 API 的 Bearer token同时也是 session cookie 的签名密钥
- `SUB2API_CRM_ADMIN_USERNAME`
- 可选,默认 `admin`
- `SUB2API_CRM_ADMIN_PASSWORD`
- 可选
- 若未配置,当前实现会回退为“使用 `SUB2API_CRM_ADMIN_TOKEN` 作为登录密码”
- `SUB2API_CRM_ADMIN_SESSION_TTL`
- 可选,默认 `12h`
- 控制浏览器管理态 session 的有效期
当前 provider 草稿发布相关 API
- `POST /api/provider-drafts`
- `GET /api/provider-drafts`
- `GET /api/provider-drafts/{draft_id}`
- `PUT /api/provider-drafts/{draft_id}`
- `DELETE /api/provider-drafts/{draft_id}`
- `POST /api/provider-drafts/{draft_id}/publish`
`providers.html` 页面内显式动作当前推荐独立验收:
- `GET /api/packs`
- `GET /api/hosts`
- `GET /api/packs/{pack_id}/providers`
- `POST /api/providers/{provider_id}/preview-import`
- `POST /api/providers/{provider_id}/import`
- `POST /api/provider-drafts`
- `PUT /api/provider-drafts/{draft_id}`
- `DELETE /api/provider-drafts/{draft_id}`
- `POST /api/provider-drafts/{draft_id}/publish`
对应脚本:
```bash
bash ./scripts/acceptance/verify_provider_admin_actions.sh
```
feat(deploy): add CRM-only online deployment to remote43 - scripts/deploy/deploy_crm_only.sh: 单进程部署 sub2api-cn-relay-manager CRM 控制面到 remote43,不依赖 sub2api host / PG / Redis 容器。 复用 scripts/deploy/remote43_patched_stack_lib.sh 的 env 渲染 (render_remote43_crm_env),render_crm_only_bootstrap 用 $\{VAR\} 占位符 + sed 注入避开 set -u + unquoted-heredoc 边缘问题。 部署前先 kill 老进程 (再 scp 二进制) 避免 ELF overwrite 失败。 - docs/DEPLOYMENT.md: 加 '在线部署节点' 段,记录 stack / 端口 / 入口 / 验证。 - docs/EXECUTION_BOARD.md: 顶部加 'Latest Online Stack' 段。 - artifacts/online-deploy-20260602/: 本次真实部署的证据 - 01-local-build.txt: 本地 server 二进制 md5 + git head - 02-remote-inspect.txt: 远端 process / port / db tables - 03-crm-api-checks.txt: /healthz /api/packs /api/hosts /metrics 真实响应 - 04-portal-public.txt: sub.tksea.top 公共入口 - 05-quality-gates.txt: gofmt / vet / test -race / integration - manifest.json: 结构化汇总 验证(2026-06-02 21:32-21:43): - /healthz: HTTP 200 'ok' - /api/packs (Bearer): HTTP 200 '{"packs":[]}' - /api/hosts (Bearer): HTTP 200 '{"hosts":[]}' - /api/packs (no auth): HTTP 401 - /metrics (Prometheus): HTTP 200,含 active_hosts/active_providers/ db_connections_active + Go runtime - sub.tksea.top/portal/: HTTP 200 - sub.tksea.top/portal-admin-api/healthz: HTTP 200 'ok'(反代到 CRM) - go test -race ./internal/... ./tests/integration/...: PASS - gofmt / go vet: 干净
2026-06-02 21:46:39 +08:00
最小前端门禁:
```bash
bash ./scripts/test/test_tksea_portal_assets.sh
bash ./scripts/test/verify_frontend_smoke.sh
```
`publish` 的运行前提:
- CRM 进程必须配置 `SUB2API_CRM_REPO_ROOT`
- 该目录必须是真实 Git 仓库,而不是普通文件夹
- remote43 当前推荐固定路径:`/home/ubuntu/sub2api-cn-relay-manager-git-current`
- 推荐由 `scripts/deploy/setup_remote43_patched_stack.sh` 统一准备,不要再手工生成带日期后缀的临时 repo 根目录
- 当前实现会原子完成:
- 生成或更新 `packs/<pack_id>/providers/<provider_id>.json`
- bump `pack.json` patch 版本
- 更新 `checksums.txt`
- 校验整个 pack
- `git add` + `git commit`
兼容入口:
- `https://sub.tksea.top/kimi-portal/`
- 当前应保持跳转到 `/portal/`
## 上线前验证
```bash
gofmt -l .
feat(deploy): add CRM-only online deployment to remote43 - scripts/deploy/deploy_crm_only.sh: 单进程部署 sub2api-cn-relay-manager CRM 控制面到 remote43,不依赖 sub2api host / PG / Redis 容器。 复用 scripts/deploy/remote43_patched_stack_lib.sh 的 env 渲染 (render_remote43_crm_env),render_crm_only_bootstrap 用 $\{VAR\} 占位符 + sed 注入避开 set -u + unquoted-heredoc 边缘问题。 部署前先 kill 老进程 (再 scp 二进制) 避免 ELF overwrite 失败。 - docs/DEPLOYMENT.md: 加 '在线部署节点' 段,记录 stack / 端口 / 入口 / 验证。 - docs/EXECUTION_BOARD.md: 顶部加 'Latest Online Stack' 段。 - artifacts/online-deploy-20260602/: 本次真实部署的证据 - 01-local-build.txt: 本地 server 二进制 md5 + git head - 02-remote-inspect.txt: 远端 process / port / db tables - 03-crm-api-checks.txt: /healthz /api/packs /api/hosts /metrics 真实响应 - 04-portal-public.txt: sub.tksea.top 公共入口 - 05-quality-gates.txt: gofmt / vet / test -race / integration - manifest.json: 结构化汇总 验证(2026-06-02 21:32-21:43): - /healthz: HTTP 200 'ok' - /api/packs (Bearer): HTTP 200 '{"packs":[]}' - /api/hosts (Bearer): HTTP 200 '{"hosts":[]}' - /api/packs (no auth): HTTP 401 - /metrics (Prometheus): HTTP 200,含 active_hosts/active_providers/ db_connections_active + Go runtime - sub.tksea.top/portal/: HTTP 200 - sub.tksea.top/portal-admin-api/healthz: HTTP 200 'ok'(反代到 CRM) - go test -race ./internal/... ./tests/integration/...: PASS - gofmt / go vet: 干净
2026-06-02 21:46:39 +08:00
bash ./scripts/test/test_tksea_portal_assets.sh
bash ./scripts/test/verify_frontend_smoke.sh
go vet ./...
go test ./...
go test -race ./...
go test ./tests/integration/... -count=1
go test -cover ./internal/...
```
feat(deploy): add CRM-only online deployment to remote43 - scripts/deploy/deploy_crm_only.sh: 单进程部署 sub2api-cn-relay-manager CRM 控制面到 remote43,不依赖 sub2api host / PG / Redis 容器。 复用 scripts/deploy/remote43_patched_stack_lib.sh 的 env 渲染 (render_remote43_crm_env),render_crm_only_bootstrap 用 $\{VAR\} 占位符 + sed 注入避开 set -u + unquoted-heredoc 边缘问题。 部署前先 kill 老进程 (再 scp 二进制) 避免 ELF overwrite 失败。 - docs/DEPLOYMENT.md: 加 '在线部署节点' 段,记录 stack / 端口 / 入口 / 验证。 - docs/EXECUTION_BOARD.md: 顶部加 'Latest Online Stack' 段。 - artifacts/online-deploy-20260602/: 本次真实部署的证据 - 01-local-build.txt: 本地 server 二进制 md5 + git head - 02-remote-inspect.txt: 远端 process / port / db tables - 03-crm-api-checks.txt: /healthz /api/packs /api/hosts /metrics 真实响应 - 04-portal-public.txt: sub.tksea.top 公共入口 - 05-quality-gates.txt: gofmt / vet / test -race / integration - manifest.json: 结构化汇总 验证(2026-06-02 21:32-21:43): - /healthz: HTTP 200 'ok' - /api/packs (Bearer): HTTP 200 '{"packs":[]}' - /api/hosts (Bearer): HTTP 200 '{"hosts":[]}' - /api/packs (no auth): HTTP 401 - /metrics (Prometheus): HTTP 200,含 active_hosts/active_providers/ db_connections_active + Go runtime - sub.tksea.top/portal/: HTTP 200 - sub.tksea.top/portal-admin-api/healthz: HTTP 200 'ok'(反代到 CRM) - go test -race ./internal/... ./tests/integration/...: PASS - gofmt / go vet: 干净
2026-06-02 21:46:39 +08:00
## 在线部署节点latest online stack
日期2026-06-02
stack`crm-only-20260602_18190`
hostubuntu@43.155.133.187
CRM 端口18190仅 127.0.0.1 监听,不直接对外暴露)
CRM 二进制:`/home/ubuntu/crm-only-20260602_18190/sub2api-cn-relay-manager-server`
CRM env`/home/ubuntu/crm-only-20260602_18190/.env.crm`chmod 600root 持有)
CRM 日志:`/home/ubuntu/crm-only-20260602_18190/crm.log`
CRM 数据库:`/home/ubuntu/crm-only-20260602_18190/sub2api-cn-relay-manager.db`
publish 仓库:`/home/ubuntu/sub2api-cn-relay-manager-git-current`main @ 4ec9dad4
运维 env`/tmp/crm-only-20260602.env`本地chmod 600
部署脚本:`scripts/deploy/deploy_crm_only.sh`
真实验收:见 `artifacts/online-deploy-20260602/manifest.json`
访问入口(公网):
- 用户 portalhttps://sub.tksea.top/portal/
- 管理 portalhttps://sub.tksea.top/portal/admin/
- 管理态同域反代https://sub.tksea.top/portal-admin-api/ → http://127.0.0.1:18190/
- 直接 CRM 访问:必须先开 SSH 隧道跑 `bash /tmp/crm-only-20260602.tunnel.sh`
然后 `set -a; source /tmp/crm-only-20260602.env; set +a`
当前已验证2026-06-02
- `GET /healthz` → 200 `ok`
- `GET /api/packs`Bearer→ 200 `{"packs":[]}`
- `GET /api/hosts`Bearer→ 200 `{"hosts":[]}`
- `GET /metrics`(无 authPrometheus 格式)→ 200`active_hosts` `active_providers` `db_connections_active` + Go runtime metrics
- `GET /api/packs`(无 auth→ 401auth 拦截正常
- SQLite 库初始化出 22 张表schema_migrations 存在
部署步骤(后续重启 / 滚动更新时):
```bash
cd /home/long/project/sub2api-cn-relay-manager
go build -trimpath -ldflags='-s -w' -o server ./cmd/server
STACK_NAME=crm-only-20260602 bash scripts/deploy/deploy_crm_only.sh
# 然后在新终端开隧道
bash /tmp/crm-only-20260602.tunnel.sh
```
## 生产注意事项
- host 注册后,后续 `preview-import / import / reconcile / access / rollback-provider / status / resources / import-batches` 应统一使用 `host_id``host_id` 查询参数,不再依赖临时 `host_base_url` 作为运行时主键。
- 状态库会持久化宿主认证信息;部署时必须把 SQLite 文件放到受限目录并纳入备份/权限管理。
- `rollback-provider` 现在只按已记录的 managed resources 回滚;若缺少批次资源记录,会拒绝危险删除。
- capability probe 已改为无副作用探测,但仍建议先在预生产宿主验证后再接入生产宿主。
## 真实能力边界
当前文档不应宣称以下能力已经内置:
- `/metrics`
- Prometheus / Grafana 接入
- 限流 / quota enforcement
- 完整审计日志面板
这些能力若为上线要求,需要单独实现后再升级部署结论。