11 KiB
11 KiB
Real Host Acceptance Runbook
日期:2026-05-21
先读哪几份文档
docs/EXECUTION_BOARD.md- 当前 gate、最新阻断、最新 live 真相以它为准。
docs/PRODUCTION_CLOSURE_BOARD.md- 看是否已经达到可上线口径,以及哪些只是历史 PASS。
docs/PROVIDER_ONBOARDING_PLAYBOOK.md- 当你是在“新增 provider”或“宿主升级后重新适配”场景下工作时,先看这份。
- 它定义稳定的 onboarding / rerun 顺序,而不是只定义一次性的验收动作。
docs/REAL_HOST_ACCEPTANCE_CHECKLIST.md- 每次 real-host 验收先走这一页。
- 适合快速确认红线、三层证据和最短诊断顺序。
docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md- 看已经调通的细节、典型误判点、推荐诊断顺序。
目标
把当前 CONDITIONAL_APPROVED 的剩余外部门禁收敛为一套可直接执行的真实宿主验收流程,覆盖:
- 真实 sub2api 宿主接入探测
- pack 安装
- preview/import 验证
- access preview / access status 验证
- reconcile 验证
- rollback smoke
前置条件
控制面
sub2api-cn-relay-manager已启动CRM_BASE_URL可访问,例如http://127.0.0.1:8080- 已设置
CRM_ADMIN_TOKEN
真实宿主
- 已知真实宿主
HOST_BASE_URL - 已知宿主管理认证:
HOST_API_KEY或HOST_BEARER_TOKEN
- 至少一个真实 provider key
- 已知 pack 路径,例如
/app/packs/openai-cn-pack
推荐执行方式
0. 先跑脚本回归自检(避免把 harness 漂移带进真实宿主结论)
cd /path/to/sub2api-cn-relay-manager
bash ./scripts/test_real_host_scripts.sh
说明:
- 当前推荐显式用
bash调起,确保在不同机器上不会因为脚本执行位差异把 harness 回归误报成逻辑失败。 - 只有这一步通过后,再继续真实宿主验收。
1. 构建本地容器镜像(适用于代理/离线开发机)
cd /path/to/sub2api-cn-relay-manager
scripts/build_local_image.sh
默认输出:
- 二进制:
bin/sub2api-cn-relay-manager - 镜像:
sub2api-cn-relay-manager:local
2. 先 dry-run 检查真实验收参数
CRM_BASE_URL=http://127.0.0.1:8080 \
CRM_ADMIN_TOKEN=replace-me \
HOST_NAME=prod-sub2api \
HOST_BASE_URL=https://sub2api.example.com \
HOST_API_KEY=host-admin-key \
PACK_PATH=/app/packs/openai-cn-pack \
PROVIDER_ID=deepseek \
KEYS=sk-live-1,sk-live-2 \
ACCESS_MODE=self_service \
ACCESS_API_KEY=user-gateway-key \
DRY_RUN=1 \
scripts/real_host_acceptance.sh
3. 执行真实验收
CRM_BASE_URL=http://127.0.0.1:8080 \
CRM_ADMIN_TOKEN=replace-me \
HOST_NAME=prod-sub2api \
HOST_BASE_URL=https://sub2api.example.com \
HOST_API_KEY=host-admin-key \
PACK_PATH=/app/packs/openai-cn-pack \
PROVIDER_ID=deepseek \
KEYS=sk-live-1,sk-live-2 \
ACCESS_MODE=self_service \
ACCESS_API_KEY=user-gateway-key \
scripts/real_host_acceptance.sh
4. 订阅模式示例
CRM_BASE_URL=http://127.0.0.1:8080 \
CRM_ADMIN_TOKEN=replace-me \
HOST_NAME=prod-sub2api \
HOST_BASE_URL=https://sub2api.example.com \
HOST_BEARER_TOKEN=host-bearer-token \
PACK_PATH=/app/packs/openai-cn-pack \
PROVIDER_ID=deepseek \
KEYS=sk-live-1 \
ACCESS_MODE=subscription \
SUBSCRIPTION_USERS=user-a,user-b \
SUBSCRIPTION_DAYS=30 \
scripts/real_host_acceptance.sh
5. 导入后自动补 access 前置(可选)
当真实宿主需要额外完成“普通用户余额 / key-group 绑定 / 订阅写入 / 缓存失效”等宿主侧动作时,可在 import 完成后插入自定义 hook:
AFTER_IMPORT_HOOK_COMMAND='bash /path/to/host-access-hook.sh' \
... \
scripts/real_host_acceptance.sh
hook 执行时会额外导出:
BATCH_IDBATCH_DETAIL_FILE(若非 dry-run,会指向05a-batch-detail-pre-access.json)PROVIDER_IDHOST_BASE_URLCRM_BASE_URLACCESS_MODEMODEARTIFACT_DIR
标准产物会新增:
05a-batch-detail-pre-access.json05b-after-import-hook.stdout.txt05b-after-import-hook.stderr.txt
产物
脚本会把每一步 JSON 响应落到:
artifacts/real-host-acceptance/<timestamp>/
默认文件顺序:
01-create-host.json02-probe-host.json03-install-pack.json04-preview-import.json05-import.json05a-batch-detail-pre-access.json(若拿到了batch_id且非 dry-run)05b-after-import-hook.stdout.txt/05b-after-import-hook.stderr.txt(若配置了 hook)06-access-preview.json07-access-status.json08-provider-status.json09-reconcile.json10-batch-detail.json11-rollback.json(若未跳过)
通过标准
至少同时满足:
probe-host返回宿主版本与 capability 快照install-pack成功import返回batch_id,且 batch/provider 状态不为failedaccess-preview返回available=true或 access status 进入:subscription_readyself_service_readyfully_ready
reconcile不返回关键失败rollback smoke成功(若本次需要验证回滚链路)
推荐额外落盘的三层证据
为了避免把不同语义混为一谈,真实宿主验收建议每次都额外记录下面三层证据:
- account 单体视角
GET /api/v1/admin/accounts/:idGET /api/v1/admin/accounts/:id/models
- group/普通用户聚合视角
- 用真实可用的普通用户 key 或 subscription managed key 请求
GET /v1/models
- 用真实可用的普通用户 key 或 subscription managed key 请求
- completion smoke
- 用同一条普通用户访问链路请求
POST /v1/chat/completions
- 用同一条普通用户访问链路请求
注意:
GET /api/v1/admin/accounts/:id/models正确,不等于普通用户/v1/models一定正确/v1/models正确,也不等于/v1/chat/completions一定正确- 这三层必须分开记证据、分开下结论
当前门禁解释
- 若以上脚本在真实宿主环境全部通过:
- 可以把当前项目推进到 真实环境放行
- 若脚本未执行:
- 仍然不能把最新代码直接视作真实宿主已放行
- 若脚本执行但失败:
- 失败应被归类为真实宿主兼容性 / 凭据 / 网络 / pack 内容问题,而不是再泛化成“代码是否已完成”
注意事项
- 默认会执行 rollback smoke;若当前环境不允许回滚,设置:
SKIP_ROLLBACK=1 scripts/real_host_acceptance.sh
PACK_PATH必须是控制面进程可读路径,不是用户本地概念路径。- 如果控制面部署在容器中,确保 pack 目录已经挂载进去。
HOST_API_KEY与HOST_BEARER_TOKEN二选一即可;脚本会自动推导auth.type=apikey|bearer。ACCESS_API_KEY必须使用真实未脱敏的普通用户 gateway key;不能直接复用数据库/列表接口中的展示值。- 真实宿主初始化只会准备管理员账号;普通用户账号/密码不会自动生成,验收前必须显式创建并留存可复用凭据。
self_service验证除普通用户 key 外,还需要该 key 绑定目标 group;若目标 group 是标准计费组,还需要用户侧具备可用余额,否则/v1/models可能从“未授权”转为INSUFFICIENT_BALANCE。subscription验证需要目标 group 本身是subscription类型,并且完成“普通用户订阅分配 + 普通用户 key 绑定该 group”;仅有管理员主体或未绑定 key 不足以通过/v1/models。- 若需要验证
reconcile收敛,优先在干净宿主场景或隔离 group 下执行,避免历史残留资源把结果污染成status=drifted/extra_count>0。 scripts/import_remote43_provider.sh现已内置 remote43 的 subscription 验收补全动作:会根据 import batch 自动解析目标 group,执行“普通用户最低余额补齐 + key/group 绑定 + user_subscriptions upsert + 定向 Redis 缓存失效(auth / balance / subscription)”,并把 SQL / host state 证据写入 artifact 目录。- 当 CRM 进程与 operator 到 host 的访问地址不一致时,优先显式设置
CRM_HOST_BASE,避免把 CRM 侧探测地址和本地运维隧道地址混用。 - 对
Upstream service temporarily unavailable一类 502,不要先认定是上游聊天链路故障;先看脚本落盘的09-models.headers.txt/10-models.body.json。若/v1/models已返回了别的 provider 模型集(例如 GPT 系列而不是预期的 DeepSeek/Minimax 模型),先检查普通用户 key/group 绑定,也要检查 CRM 导入时是否把 provider 的channel_template.model_mapping、restrict_models、billing_model_source一并下发到宿主 channel。 - subscription 场景里,closure 最终用于 gateway probe 的 key 是宿主侧 managed key,不一定是请求体里外部传入的
ACCESS_API_KEY。如果你拿原始 key 直打/v1/models收到403 not assigned to any group,先不要判定 CRM 主链路失败。 - 对“既有 channel 没自动补
model_pricing”这类 live 现象,先核对在线 CRM 进程的启动时间与 git 提交时间;之前 MiniMax 的该现象最终被确认是 stale CRM 进程导致,而不是源码缺逻辑。 - 当 CRM 切换到本机运行时,
PACK_PATH也必须切换到控制面本机可读路径;继续沿用远端/home/ubuntu/...会触发stat pack path ... no such file or directory,这是验收 harness 参数问题,不是导入业务逻辑问题。 - 若要把 DeepSeek 的“host
/v1/models=200 但 host/v1/chat/completions=502,而 upstream 直探/chat/completions=200”做成可提 issue 的最小复现,直接运行scripts/check_deepseek_completion_split.sh。它会同时落盘 host/v1/models、host/v1/chat/completions、upstream/chat/completions三层证据,并在summary.json里给出host_compatibility_gap|upstream_key_quota_issue|unknown分类。 - 若 reconcile 面对的是“非 latest batch 的同前缀旧账号”,最新代码会把它们记为
stale_noise_count/stale_noise_accounts并保留raw_extra_count,而不是继续把它们算进extra_count造成 drift 误报;因此应优先看extra_count是否归零,再看probe_failures/access_status是否仍有真实异常。 - self_service 场景里,普通用户 gateway key 访问宿主
/v1/models//v1/chat/completions时,真实语义是Authorization: Bearer <gateway-key>;若 CRM 的 self_service closure 仍显示401/403 broken,优先排查 gateway probe 是否错误复用了x-api-key。 - fresh-host 管理员 bearer token 过期时,最前面的
POST /api/hosts/probe-host可能直接表现成 CRM 侧502。遇到这类现象,先刷新 host bearer token,再继续验收,不要先把它归因为最新代码故障。 - shared fresh-host 上若
05-import.json/07-access-status.json已经 ready,而09-reconcile.json仍是status=drifted,优先把它解释为历史残留资源噪音;PRD 首版放行判断应以 import/access 闭环是否打通为主。
建议固定执行的快速诊断顺序
- 先看环境
- CRM 是否是最新提交对应的在线进程
PACK_PATH是否是 CRM 本机可读路径CRM_HOST_BASE是否与 CRM 到 host 的实际访问地址一致
- 再看宿主落库
- account
credentials.model_mapping GET /api/v1/admin/accounts/:id/models- channel
model_mapping/model_pricing/restrict_models/billing_model_source
- account
- 最后看普通用户流量
/v1/models/v1/chat/completions
若需要背景解释、误判案例和已调通经验,直接看:docs/REAL_HOST_ACCEPTANCE_LEARNINGS.md