271 lines
8.2 KiB
Markdown
271 lines
8.2 KiB
Markdown
# 生产稳定基线
|
||
|
||
日期: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` completion,prompt 为 `reply with pong only`,返回 `pong`。
|
||
4. 同一个 key 请求 `gpt-5.4-mini` completion,prompt 为 `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 | 临时资源未清理 | 保留生产宿主,停止或归档临时栈 |
|