# Real Host Acceptance Runbook 日期:2026-05-16 ## 目标 把当前 `CONDITIONAL_APPROVED` 的剩余外部门禁收敛为一套可直接执行的真实宿主验收流程,覆盖: 1. 真实 sub2api 宿主接入探测 2. pack 安装 3. preview/import 验证 4. access preview / access status 验证 5. reconcile 验证 6. 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` ## 推荐执行方式 ### 1. 构建本地容器镜像(适用于代理/离线开发机) ```bash 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 检查真实验收参数 ```bash 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. 执行真实验收 ```bash 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. 订阅模式示例 ```bash 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: ```bash AFTER_IMPORT_HOOK_COMMAND='bash /path/to/host-access-hook.sh' \ ... \ scripts/real_host_acceptance.sh ``` hook 执行时会额外导出: - `BATCH_ID` - `BATCH_DETAIL_FILE`(若非 dry-run,会指向 `05a-batch-detail-pre-access.json`) - `PROVIDER_ID` - `HOST_BASE_URL` - `CRM_BASE_URL` - `ACCESS_MODE` - `MODE` - `ARTIFACT_DIR` 标准产物会新增: - `05a-batch-detail-pre-access.json` - `05b-after-import-hook.stdout.txt` - `05b-after-import-hook.stderr.txt` ## 产物 脚本会把每一步 JSON 响应落到: ```text artifacts/real-host-acceptance// ``` 默认文件顺序: - `01-create-host.json` - `02-probe-host.json` - `03-install-pack.json` - `04-preview-import.json` - `05-import.json` - `05a-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.json` - `07-access-status.json` - `08-provider-status.json` - `09-reconcile.json` - `10-batch-detail.json` - `11-rollback.json`(若未跳过) ## 通过标准 至少同时满足: 1. `probe-host` 返回宿主版本与 capability 快照 2. `install-pack` 成功 3. `import` 返回 `batch_id`,且 batch/provider 状态不为 `failed` 4. `access-preview` 返回 `available=true` 或 access status 进入: - `subscription_ready` - `self_service_ready` - `fully_ready` 5. `reconcile` 不返回关键失败 6. `rollback smoke` 成功(若本次需要验证回滚链路) ## 当前门禁解释 - 若以上脚本在真实宿主环境全部通过: - 可以把当前项目从 **代码层 `CONDITIONAL_APPROVED`** 推进到 **真实环境放行** - 若脚本未执行: - 仍然只能维持 `CONDITIONAL_APPROVED` - 若脚本执行但失败: - 失败应被归类为真实宿主兼容性 / 凭据 / 网络 / pack 内容问题,而不是再泛化成“代码是否已完成” ## 注意事项 1. 默认会执行 rollback smoke;若当前环境不允许回滚,设置: ```bash SKIP_ROLLBACK=1 scripts/real_host_acceptance.sh ``` 2. `PACK_PATH` 必须是控制面进程可读路径,不是用户本地概念路径。 3. 如果控制面部署在容器中,确保 pack 目录已经挂载进去。 4. `HOST_API_KEY` 与 `HOST_BEARER_TOKEN` 二选一即可;脚本会自动推导 `auth.type=apikey|bearer`。 5. `ACCESS_API_KEY` 必须使用真实未脱敏的普通用户 gateway key;不能直接复用数据库/列表接口中的展示值。 6. 真实宿主初始化只会准备管理员账号;普通用户账号/密码不会自动生成,验收前必须显式创建并留存可复用凭据。 7. `self_service` 验证除普通用户 key 外,还需要该 key 绑定目标 group;若目标 group 是标准计费组,还需要用户侧具备可用余额,否则 `/v1/models` 可能从“未授权”转为 `INSUFFICIENT_BALANCE`。 8. `subscription` 验证需要目标 group 本身是 `subscription` 类型,并且完成“普通用户订阅分配 + 普通用户 key 绑定该 group”;仅有管理员主体或未绑定 key 不足以通过 `/v1/models`。 9. 若需要验证 `reconcile` 收敛,优先在干净宿主场景或隔离 group 下执行,避免历史残留资源把结果污染成 `status=drifted` / `extra_count>0`。 10. `scripts/import_remote43_provider.sh` 现已内置 remote43 的 subscription 验收补全动作:会根据 import batch 自动解析目标 group,执行“普通用户最低余额补齐 + key/group 绑定 + user_subscriptions upsert + 定向 Redis 缓存失效(auth / balance / subscription)”,并把 SQL / host state 证据写入 artifact 目录。 11. 当 CRM 进程与 operator 到 host 的访问地址不一致时,优先显式设置 `CRM_HOST_BASE`,避免把 CRM 侧探测地址和本地运维隧道地址混用。 12. 对 `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。