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

271 lines
8.2 KiB
Markdown
Raw Normal View History

# 生产稳定基线
日期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 | 临时资源未清理 | 保留生产宿主,停止或归档临时栈 |