Files
sub2api-cn-relay-manager/docs/PRODUCTION_STABILITY_BASELINE.md
2026-06-04 20:00:03 +08:00

271 lines
8.2 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.
# 生产稳定基线
日期2026-06-04
适用环境:`sub.tksea.top` / remote43
## 目标
这份文档固定当前线上生产入口,防止临时验收栈、历史 patched host 或旧用户 key 被误当成生产真相。
生产稳定判断只看当前生产栈。历史 artifact、旧 OpenClaw profile、旧 key、旧端口只能作为迁移线索不能作为生产健康结论。
remote43 只应长期保留一套生产宿主。生产宿主同时承担生产流量和受控 smoke 测试;临时宿主只能用于短期验收,验收结束后必须清理或停止,不能长期留作备用生产入口。
## 当前生产固定点
| 层级 | 固定值 | 说明 |
|---|---|---|
| 公网数据面 | `https://sub.tksea.top/v1` | OpenAI-compatible 用户调用入口 |
| 公网 Portal | `https://sub.tksea.top/portal/` | 用户态静态 portal |
| 公网管理代理 | `https://sub.tksea.top/portal-admin-api/` | 反代到 CRM 控制面 |
| 生产宿主 | `127.0.0.1:8080` | `sub2api` 主容器 |
| 生产 CRM | `127.0.0.1:18190` | `crm-only-20260602_18190` |
| 生产宿主容器 | `sub2api` | 当前主数据面容器 |
| 生产宿主数据库 | `sub2api-postgres` | 当前主宿主 PostgreSQL |
| 生产宿主 Redis | `sub2api-redis` | 当前主宿主 Redis |
| 生产 CRM 二进制 | `/home/ubuntu/crm-only-20260602_18190/sub2api-cn-relay-manager-server` | 控制面进程 |
| 生产 CRM 数据库 | `/home/ubuntu/crm-only-20260602_18190/sub2api-cn-relay-manager.db` | 控制面 SQLite |
## Nginx 基线
`/etc/nginx/sites-available/tksea` 必须满足:
```nginx
location /portal/ {
alias /var/www/sub2api-portal/;
index index.html;
try_files $uri $uri/ /portal/index.html;
}
location /portal-proxy/ {
proxy_pass http://127.0.0.1:8080/;
}
location /portal-admin-api/ {
proxy_pass http://127.0.0.1:18190/;
}
location /kimi/ {
proxy_pass http://127.0.0.1:8080/;
}
location /kimi-v1/ {
proxy_pass http://127.0.0.1:8080/v1/;
}
location / {
proxy_pass http://127.0.0.1:8080;
}
```
公网生产入口不得指向:
```text
127.0.0.1:18169
127.0.0.1:18173
127.0.0.1:18191
127.0.0.1:18192
127.0.0.1:18193
```
这些端口属于历史 patched stack、route lab 或验证栈。它们可以用于验收,但不能接管 `sub.tksea.top` 的生产入口。
## 健康判定
### 无密钥健康检查
无密钥请求 `/v1/models` 返回 `401 API_KEY_REQUIRED` 是健康信号。它证明公网请求已经到达生产宿主,而不是 nginx 死 upstream。
```bash
curl -sS -i https://sub.tksea.top/v1/models
```
预期:
```text
HTTP/2 401
API_KEY_REQUIRED
```
如果返回 `502 Bad Gateway`,优先检查 nginx upstream 是否又指回旧端口。
管理代理健康检查:
```bash
curl -fsS https://sub.tksea.top/portal-admin-api/healthz
```
预期:
```text
ok
```
Portal 静态入口检查:
```bash
curl -fsS -I https://sub.tksea.top/portal/
```
预期:`HTTP/2 200`
### 真实用户 smoke
生产 smoke 必须使用当前生产宿主数据库里的 active key。旧 OpenClaw profile 中保存的 key 可能来自旧宿主或旧分组,不能作为生产健康依据。
最低通过标准:
1. active key 请求 `https://sub.tksea.top/v1/models` 返回 200。
2. 模型列表包含 `gpt-5.4``gpt-5.4-mini`
3. 同一个 key 请求 `gpt-5.4` completionprompt 为 `reply with pong only`,返回 `pong`
4. 同一个 key 请求 `gpt-5.4-mini` completionprompt 为 `reply with pong only`,返回 `pong`
2026-06-04 修复后已验证:
```text
gpt-5.4 -> HTTP 200, text='pong'
gpt-5.4-mini -> HTTP 200, text='pong'
```
## 当前生产绑定基线
OpenAI 中转 self-service 链路必须保持:
```text
channel 1 -> group 3
api key 11 -> group 3
account 7 -> group 3
```
`account 7` 同时服务 subscription 分组:
```text
account 7 -> group 4
```
2026-06-04 的修复补齐了:
```text
account 7 -> group 3
```
没有这条绑定时,`/v1/models` 仍可能返回 200`/v1/chat/completions` 会失败:
```text
account_select_failed: no available accounts
```
## 验收栈隔离规则
验收脚本可以创建临时 host、临时 group、临时 account、临时 key 和临时 CRM但不得修改生产公网入口。
下列对象不能作为生产入口:
```text
crm-verify-*
route-lab-*
sub2api-kimi-patched-*
sub2api-patched-*
remote43-kimi-patched-auto2-18169
proxy-real-host-1780026133
```
这些对象只用于复现、导入验收或历史 artifact 对照。它们的 PASS 不能替代当前生产 smoke。
## 临时宿主生命周期
remote43 的默认运行策略是“单生产宿主”。除当前生产宿主外,其他 host、CRM、Postgres、Redis、route lab 或 patched stack 都视为临时资源。
临时资源创建时必须满足:
1. 不接管 `sub.tksea.top` 的公网数据面。
2. 不修改生产 Nginx 的 `/``/v1``/portal-proxy``/portal-admin-api` upstream。
3. 使用独立端口、独立容器名、独立数据库或独立 SQLite。
4. artifact 记录创建原因、端口、容器名和预期清理时间。
临时资源用完后必须执行清理。最低要求:
1. 停止临时 host / CRM 进程或容器。
2. 停止临时 Postgres / Redis 容器,除非同一验收批次还在使用。
3. 删除或归档临时 SSH tunnel、临时 env 文件和临时 bootstrap 文件。
4. 确认 `sudo nginx -T` 不引用临时端口。
5. 重新跑生产 smoke确认生产宿主仍可用。
允许保留的长期资源只有当前生产基线列出的生产宿主、生产 CRM、生产数据库和生产 Redis。
如果确实需要保留某个临时栈用于复现,必须显式标注为 `do-not-route-public`,并保证它不被 Nginx 或 OpenClaw production profile 使用。
清理后检查:
```bash
sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
ss -ltnp
sudo nginx -T | grep -nE '18169|18173|18191|18192|18193|proxy_pass'
curl -sS -i https://sub.tksea.top/v1/models
curl -fsS https://sub.tksea.top/portal-admin-api/healthz
```
`/v1/models` 无 key 返回 `401 API_KEY_REQUIRED` 才表示生产数据面仍指向当前生产宿主。
## 部署脚本基线
`scripts/deploy/deploy_tksea_portal.sh` 的默认端口必须保持:
```bash
REMOTE_HOST_PORT="${REMOTE_HOST_PORT:-8080}"
REMOTE_CRM_PORT="${REMOTE_CRM_PORT:-18190}"
```
`deploy/tksea-portal/nginx.sub.tksea.top.conf.example` 必须和线上端口一致:
```text
/portal-proxy/ -> 127.0.0.1:8080
/portal-admin-api/ -> 127.0.0.1:18190
/kimi/ -> 127.0.0.1:8080
/kimi-v1/ -> 127.0.0.1:8080/v1/
```
每次部署前后都要检查:
```bash
sudo nginx -T | grep -nE 'location /v1|location /portal|location /portal-admin-api|location /kimi|proxy_pass'
```
部署后必须执行:
```bash
sudo nginx -t
sudo systemctl reload nginx
curl -sS -i https://sub.tksea.top/v1/models
curl -fsS https://sub.tksea.top/portal-admin-api/healthz
```
## 旧 Key 解释规则
旧用户 key 或旧 OpenClaw profile 失败,不直接等于生产宿主异常。先判断它是否属于当前生产宿主和当前分组。
常见旧 key 失败原因:
```text
key 属于旧宿主数据库
key 未绑定 group
key 绑定的 group 没有 account
key 对应 subscription 已过期
key 保存于旧 OpenClaw profile
key 仍指向旧模型映射
```
判断生产健康时,优先使用当前生产数据库生成的 smoke key或当前生产 active key。
## 故障分层
| 现象 | 优先判断 | 下一步 |
|---|---|---|
| `/v1/models` 无 key 返回 502 | nginx upstream 或宿主端口错误 | 查 `nginx -T``ss -ltnp``/var/log/nginx/error.log` |
| `/v1/models` 无 key 返回 401 | 数据面入口到达宿主 | 继续用 active key 跑 `/v1/models` |
| active key `/v1/models` 返回 200但 completion 失败 | group/account/channel/upstream 问题 | 查 `account_groups``channel_groups`、宿主日志 |
| 日志出现 `no available accounts` | group 没有可调度 account | 查并修复 `account_groups` |
| 日志出现 upstream 502/503 | 上游 key/quota/供应商异常 | 查 account probe、上游直探 |
| OpenClaw profile 返回 401但 production key curl 通过 | 本机 OpenClaw key 过期或不属于当前生产宿主 | 更新 OpenClaw profile key |
| remote43 上存在多套 host | 临时资源未清理 | 保留生产宿主,停止或归档临时栈 |