feat: activate D601 sub2api external edge

This commit is contained in:
Codex
2026-06-12 07:56:46 +00:00
parent f5a6610fe9
commit c23d9b5ea0
8 changed files with 2375 additions and 164 deletions
+18 -6
View File
@@ -5,7 +5,7 @@ description: UniDesk Sub2API 平台运维技能。用户提到 Sub2API、sub2api
# UniDesk Sub2API
UniDesk 在 k3s `platform-infra` namespace 运维 Sub2API。G14 是默认 active runtimeD601 只作为同一 YAML/CLI 控制下的 standby predeploy target,外置 DB 未就绪时应用和本地 Redis cache 都保持 replicas=0。日常操作统一使用 UniDesk CLI,不直接写 Kubernetes 资源或手工调用 Sub2API 管理 API。
UniDesk 在 k3s `platform-infra` namespace 运维 Sub2API。G14 是默认 active runtimeD601 同一 YAML/CLI 控制,可保持 standby predeploy,也可在外置 DB、镜像、FRP 和 egress proxy 条件就绪后作为 external-active target 运行。日常操作统一使用 UniDesk CLI,不直接写 Kubernetes 资源或手工调用 Sub2API 管理 API。
**固定入口**: `cd /root/unidesk && bun scripts/cli.ts platform-infra sub2api ...`
@@ -33,8 +33,8 @@ bun scripts/cli.ts platform-infra sub2api codex-pool trace --request-id <request
- 配置真相是 YAML`config/platform-infra/sub2api.yaml``config/platform-infra/sub2api-codex-pool.yaml`
- 业务策略和具体数值只以 YAML 为准。已有字段的数值调整只改 YAML 并跑 `plan` / `sync --confirm` / `validate`;不要自动补代码硬编码、schema 硬范围、合同测试、单元测试或长期参考文档。配置校验只校验格式、类型、必填和可渲染性,不判断数值策略是否“合理”。
- 本 skill 目录下若存在 `agents/*.yaml`,只作为 skill/agent 展示与调用元数据,不是 Sub2API 或 Codex pool 运行配置;不要在 skill 目录维护第二份账号、capacity、priority、endpoint 或 Secret 配置。
- Runtime target 由 `config/platform-infra/sub2api.yaml` 声明;默认 `G14:k3s` 是 active target`D601:k3s` standby predeploy target。master server 只是控制端和消费者,不部署 Sub2API/PostgreSQL/Redis。
- D601 standby 不部署本地 PostgreSQL,不运行第二套 sentinel 或 FRP 管理入口;外置 DB endpoint/Secret 未准备好时只能预部署 namespace、NetworkPolicy、Service,以及 replicas=0 的 Sub2API/Redis Deployment。Redis 激活后也只允许 ephemeral cache。
- Runtime target 由 `config/platform-infra/sub2api.yaml` 声明;默认 `G14:k3s` 是 active target`D601:k3s` 可由 YAML 声明为 standby predeploy 或 external-active target。master server 只是控制端和消费者,不部署 Sub2API/PostgreSQL/Redis。
- D601 standby 不部署本地 PostgreSQL,不运行 sentinel 或 FRP 管理入口;外置 DB endpoint/Secret 未准备好时只能预部署 namespace、NetworkPolicy、Service,以及 replicas=0 的 Sub2API/Redis Deployment。Redis 激活后也只允许 ephemeral cache。D601 external-active 仍不部署本地 PostgreSQL,必须直连 YAML 声明的外置 DB,使用本地 ephemeral Redis,并且只有在 YAML 启用时才运行 frpc、egress proxy 和目标级 sentinel。
- Secret、`~/.codex/config.toml*``~/.codex/auth.json*` 是运行时输入或本地状态,不提交。
- 默认 `~/.codex/config.toml``~/.codex/auth.json` 只作为统一 Sub2API consumer 使用;`config.toml` 必须指向 `https://sub2api.74-48-78-17.nip.io/``auth.json` 必须使用统一 pool API key。新增上游账号不得覆盖这两个默认文件,只能新增 `config.toml.<profile>` / `auth.json.<profile>` 并在 YAML 里声明。
- 输出只能包含 Secret 路径、长度、preview/fingerprint;禁止打印完整 API key、admin password、JWT secret、TOTP key。
@@ -57,7 +57,7 @@ bun scripts/cli.ts platform-infra sub2api validate --target D601
- `plan` 读取 `config/platform-infra/sub2api.yaml`,渲染 `src/components/platform-infra/sub2api/sub2api.k8s.yaml`,检查 no Ingress/NodePort/LoadBalancer/hostPort/hostNetwork/resource limits,并要求 `NetworkPolicy/allow-all` 随 manifest 受控创建。
- `apply --confirm` 默认创建异步 job;按返回的 `job status` 命令轮询,再跑 `status``validate`
- `status --full|--raw` 只在需要展开远端 stdout/stderr 或原始 JSON 时使用。
- `validate` 是按需验收,不是连续可用性探针。对 D601 standby`validate --target D601` 验证预部署形态,不要求外置 DB 当前可连接。
- `validate` 是按需验收,不是连续可用性探针。对 D601 standby`validate --target D601` 验证预部署形态,不要求外置 DB 当前可连接;对 D601 external-active,必须验证外置 DB、ephemeral Redis、Sub2API service、YAML egress proxy 和目标级 public exposure
## 镜像升级
@@ -73,14 +73,22 @@ bun scripts/cli.ts platform-infra sub2api validate --target D601
```bash
bun scripts/cli.ts platform-infra sub2api codex-pool plan
bun scripts/cli.ts platform-infra sub2api codex-pool plan --target D601
bun scripts/cli.ts platform-infra sub2api codex-pool sync --confirm
bun scripts/cli.ts platform-infra sub2api codex-pool sync --target D601 --confirm
bun scripts/cli.ts platform-infra sub2api codex-pool validate
bun scripts/cli.ts platform-infra sub2api codex-pool validate --target D601
bun scripts/cli.ts platform-infra sub2api codex-pool trace --request-id <requestId>
bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-image status
bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-image status --target D601
bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-image build --confirm
bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-image build --target D601 --confirm
bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-probe --account unidesk-codex-hy --confirm
bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-probe --target D601 --account unidesk-codex-hy --confirm
bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-report
bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-report --target D601
bun scripts/cli.ts platform-infra sub2api codex-pool cleanup-probes --confirm
bun scripts/cli.ts platform-infra sub2api codex-pool cleanup-probes --target D601 --confirm
```
`config/platform-infra/sub2api-codex-pool.yaml` 控制:
@@ -113,7 +121,7 @@ bun scripts/cli.ts platform-infra sub2api codex-pool cleanup-probes --confirm
`sync --confirm` 会登录 Sub2API admin、创建/更新 group、创建/更新 YAML 中的 `unidesk-codex-*` accounts、创建/复用统一 API key Secret,并把未处于哨兵 active quarantine 的 managed account 的 `schedulable=true` 恢复为过程控制基线;它默认不删除 YAML 中缺席的 managed account。只有明确退役上游时才使用 `sync --confirm --prune-removed` 删除缺席且 `extra.unidesk_managed=true``unidesk-codex-*` account。
`sentinel-image status|build` 管理哨兵 Python 运行环境镜像。镜像由 YAML 的 `sentinel.image` 基础镜像和 `sentinel.sdk.openaiPythonVersion` 派生,发布到 G14 本地 registry `127.0.0.1:5000/platform-infra/sub2api-account-sentinel:<derived-tag>``build --confirm` 会先检查 registry tag,存在则快速复用,不存在才在 G14 host 构建并 push。CronJob 启动时只校验 SDK 版本,不在运行时 `pip install`
`sentinel-image status|build` 管理哨兵 Python 运行环境镜像。镜像由 YAML 的 `sentinel.image` 基础镜像和 `sentinel.sdk.openaiPythonVersion` 派生,发布到目标 runtime 的本地 registry`build --confirm` 会先检查 registry tag,存在则快速复用,不存在才在目标 host 构建并 push。CronJob 启动时只校验 SDK 版本,不在运行时 `pip install`
`sync --confirm` 同时会按 YAML 渲染账号级哨兵资源,并在 monitor 开启时先确保可复用哨兵镜像存在。当前目标是 `sentinel.monitor.enabled=true` + `sentinel.actions.enabled=true` 的 marker-only 自动冻结/恢复;不要手工 patch CronJob、Secret 或 Sub2API account。若 YAML 新增账号或修改 profile/base URL/API key fingerprint/upstream User-Agent/Responses WebSocket modesync 会从变更前 runtime state 写入 pending probe 记录并立即安排 sentinel probe,但默认仍保持该 account 可调度;只有实际 marker probe 非命中或已有 active quarantine 才会冻结账号。sentinel 冻结/恢复只改 `schedulable=false|true`,不得顺手调用 Sub2API `recover-state` 清除请求路径临时不可调度或其他 runtime backoff。无关账号的既有成功/失败退避不能被重置。若 YAML 下调失败冻结最大窗口,sync 会把仍 active 的旧冻结状态迁移到当前最大窗口内并立即安排 recovery probe,但不会直接解冻。若怀疑某个账号被误判,先用 `codex-pool sentinel-probe --account <accountName> --confirm` 立即触发该账号测量;该命令从现有 CronJob 模板派生一次性 Job,复用同一份 Secret、ConfigMap、OpenAI SDK probe、token/cost 账本和冻结/恢复状态机。
@@ -160,13 +168,14 @@ bun scripts/cli.ts platform-infra sub2api codex-pool expose
bun scripts/cli.ts platform-infra sub2api codex-pool expose --confirm
```
-`publicExposure` YAML 控制。默认公共端是 `publicBaseUrl`master 本地消费端是 `masterBaseUrl`
- YAML `publicExposure` 控制。Codex pool 默认公共端是 `publicBaseUrl`master 本地消费端是 `masterBaseUrl`D601 external-active 的目标级 public exposure 在 `config/platform-infra/sub2api.yaml` 中声明
- `expose --confirm` 只为 YAML 指定的 `remotePort` 补 master `frps` allow port,并在 G14 创建/更新 `sub2api-frpc`
- master Caddy site 也由 `publicExposure.masterCaddy` 渲染;`responseHeaderTimeoutSeconds` 必须足够覆盖 Codex `/responses/compact` 长请求,避免 Caddy 先返回 504 而 Sub2API 后台实际稍后成功。具体数值只改 `config/platform-infra/sub2api-codex-pool.yaml`,修改后跑 `codex-pool expose --confirm`,再核对 Caddyfile 中渲染出的 `response_header_timeout`
- master Caddy 的短窗口边缘重试由 `publicExposure.masterCaddy.edgeRetry` 渲染;用于吸收 FRP remotePort 短暂关闭、`connect: connection refused`、EOF 或 connection reset 这类请求尚未稳定到达 Sub2API 的 502。具体 retry 时长、间隔和 `retryMatch` 范围只写 YAML,修改后跑 `codex-pool expose --confirm`,再核对 Caddyfile 中渲染出的 `lb_try_duration``lb_try_interval``lb_retry_match`。不要手工 patch `/etc/caddy/Caddyfile`
- 非幂等 POST 的 round-trip retry 必须收窄到 YAML `retryMatch` 声明的安全路径;普通 `/responses` 上游账号错误仍归 Sub2API failover / temp-unschedulable / sentinel 处理,不用 Caddy 重放整段推理请求来掩盖账号池问题。
- 同一个 FRP TCP 入口同时暴露 OpenAI-compatible API 和 Sub2API 管理 UI `/login`。不要另开第二个管理端口,除非 YAML 明确声明新的暴露决策。
- Sub2API Kubernetes Service 继续保持 ClusterIP。
- D601 external-active 的公开路径是 `client -> PK01 Caddy -> PK01 frps remotePort -> D601 frpc -> Sub2API`,不经过 pikanode,也不经过 master server 反代。PK01 Caddy 下载必须使用 YAML `publicExposure.pk01.caddyDownloadProxyUrl` 指定的 proxy;如果 Caddy 下载慢,先确认 apply 输出里是 `downloadProxy.mode=curl-proxy``api.pikapython.com` 必须先解析到 YAML 声明的 PK01 公网地址,HTTPS 才能作为最终验证。
## 配置 master Codex 消费端
@@ -193,6 +202,7 @@ bun scripts/cli.ts platform-infra sub2api codex-pool configure-local --confirm
- `sub2api validate`app、PostgreSQL、Redis、service proxy、`NetworkPolicy/allow-all` 和临时跨 Pod PostgreSQL/Redis 连通性检查通过。
- `codex-pool validate`:统一 key 的 `GET /v1/models` 成功,并用 `localCodex.responsesSmokeModel` 跑一次小的 `POST /v1/responses` smokeowner balance / owner concurrency 已满足 YAML 最小值,capacity、WebSocket v2、Sub2API 内置 temporary-unschedulable 开关/规则和 sentinel runtime 状态与 YAML 对齐;`validation.gatewayResponsesRecent` 汇总最近 6 小时普通 `/responses``/v1/responses` 的 failover、forward failure、最终 4xx/5xx、慢 final error 与 `context canceled` 证据,`validation.gatewayCompactRecent` 单独汇总 `/responses/compact` 证据。若当前 Responses smoke `ok=true` 但 recent 字段 `degraded=true`,先区分是历史窗口残留还是新的 request id 正在失败;长期判定见 `docs/reference/platform-infra.md`
-`publicExposure.enabled=true`,确认 FRP path 可用;`expose --confirm` 会用未带 key 的 public `/v1/models` 401 作为网关可达性探针。
- 若目标声明了 `egressProxy.enabled=true`,确认 proxy Deployment/Service readySub2API 和 sentinel env 与 YAML 对齐,并通过 YAML 声明的 health URL 完成代理出站探针。
如果要证明真实模型请求可用,使用最小 `/v1/responses` 或等价 Codex smoke。不要把 group-level `/v1/models` 成功解释成每个上游 account 都健康。
@@ -205,6 +215,8 @@ bun scripts/cli.ts platform-infra sub2api codex-pool configure-local --confirm
- pool key 401:跑 `codex-pool sync --confirm` 重建 Sub2API key 与 k3s Secret 绑定,再跑 `codex-pool validate`
- 运行中过去的验证探针残留:只用 `codex-pool cleanup-probes --confirm` 清理 `unidesk-probe-*` 临时资源;不要把真实 managed account 删除当作探针清理或可用性恢复。
- FRP 不通:先看 `codex-pool expose --confirm` 输出的 `masterFrps``masterCaddy``sub2api-frpc` 和 public 401 probe;需要低层证据时只用 `trans G14:k3s` 做 bounded 查询。
- D601 external-active 的 `api.pikapython.com` 不通:先区分 DNS/TLS/Caddy/FRP/Sub2API。DNS 未解析到 YAML 声明的 PK01 地址时,Caddy ACME 会失败,`https://api.pikapython.com` 不能算完成;可用 PK01 loopback FRP 端口和 PK01 公网 remotePort 证明 D601 FRP 数据路径,但最终仍要等 DNS 生效后重跑 HTTPS health、`/v1/models``/v1/responses`
- Caddy 下载慢或失败:先确认 `config/platform-infra/sub2api.yaml` 已设置 `publicExposure.pk01.caddyDownloadProxyUrl`,并重跑 `sub2api apply --target D601 --confirm` 看 PK01 apply summary 中的 `downloadProxy.mode=curl-proxy`。不要反复裸连 GitHub release。
- `/responses/compact` 在接近 master Caddy `response_header_timeout` 的固定时长后返回 504,或 Sub2API 日志稍后记录 `codex.remote_compact.succeeded` 时,优先检查 master Caddy `response_header_timeout` 是否由 YAML `publicExposure.masterCaddy.responseHeaderTimeoutSeconds` 渲染,修正后跑 `codex-pool expose --confirm`;这类边缘代理超时不会触发 Sub2API 账号级临时下线。reload 前已经在途的 compact 请求仍可能按旧 timeout 结束,判断修复是否生效时只看 reload 之后新发起的请求。
- `/responses/compact` 或普通 public URL 在几秒窗口内出现 502Caddy 日志显示 `dial tcp 127.0.0.1:<remotePort>: connect: connection refused``EOF``connection reset by peer`,同时 frps 日志出现 `platform-infra-sub2api proxy closing` / `listener is closed` / `new proxy ... success`,说明失败在 master Caddy 与 FRP remotePort 边缘层,Sub2API 和 sentinel 可能完全看不到。先确认 `publicExposure.masterCaddy.edgeRetry` 已按 YAML 渲染并 `codex-pool expose --confirm` 生效;若仍频繁发生,再继续查 G14 `sub2api-frpc` 到 master `frps` 的控制连接稳定性。不要把这类边缘 502 误判成账号池上游错误,也不要通过禁用账号恢复。
- default profile 递归:检查 YAML default entry 是否使用 `*.pre-sub2api` 备份文件;必要时恢复备份后重新 `configure-local --confirm`