455 lines
17 KiB
Markdown
455 lines
17 KiB
Markdown
# 部署与运维模块
|
||
|
||
## 1. 定位
|
||
|
||
本模块规定 cpa-ext 如何构建、安装、启动、观察、备份、升级和恢复。它把 [dev.md](dev.md) 的 native runtime 约束、[security.md](security.md) 的 fail-closed 前提和 [persistence.md](persistence.md) 的数据库规则转成可执行运维流程。
|
||
|
||
当前仓库仍是只声明 `usage_plugin` 的基础骨架,不具备本文所述完整生产能力。只有 [test-plan.md](test-plan.md) 的相应发布门禁通过后,才能把某个版本标记为 production-ready。
|
||
|
||
## 2. 支持基线
|
||
|
||
设计验证基线:
|
||
|
||
| 项目 | 目标 |
|
||
| --- | --- |
|
||
| CLIProxyAPI | `v7.2.130` / `f43aad7637ad813745bf7d341acb5663617570c5` |
|
||
| Native ABI | `1` |
|
||
| RPC schema | `3` |
|
||
| cpa-ext plugin ID | `cpa-ext` |
|
||
| 当前 module Go | `1.24` |
|
||
| CPA module Go | `1.26.0` |
|
||
| Home mode | 不支持,必须关闭 |
|
||
| CPA build | 必须是 CGO/plugin-capable,不能使用 `no-plugin` 产物 |
|
||
|
||
生产兼容性按“精确 CPA tag + OS + arch + cpa-ext artifact”发布,不只写“ABI 1”。每次升级 CPA 都要重新跑 ABI、E2E、安全、性能和 soak 门禁。
|
||
|
||
目标发布矩阵:
|
||
|
||
- Linux amd64/arm64 `.so`;
|
||
- Windows amd64 `.dll`;
|
||
- macOS amd64/arm64 `.dylib`(若实际维护);
|
||
- WSL 构建的 `.so` 只供运行在 WSL/Linux 的 CPA,不能装入 Windows CPA。
|
||
|
||
Go c-shared 需要目标平台 C toolchain,不能只设置 `GOOS/GOARCH` 假装完成 CGO 交叉编译。
|
||
|
||
## 3. 运行拓扑
|
||
|
||
### 3.1 默认 MVP
|
||
|
||
```text
|
||
reverse proxy / readiness gate
|
||
│
|
||
▼
|
||
CLIProxyAPI + cpa-ext.so
|
||
│
|
||
├─ synchronous short SQLite transactions
|
||
├─ callback-driven bounded maintenance
|
||
└─ no long-lived plugin worker
|
||
```
|
||
|
||
第一版默认 callback-driven:认证、准入、事实和账本在回调内完成必要短事务;聚合追赶、清理和 checkpoint 只在安全宿主调用中按时间/页数预算推进。
|
||
|
||
### 3.2 Sidecar 拓扑
|
||
|
||
P0 runtime/24h soak 失败,或需要长期 quota refresh、备份、归档、大导出时:
|
||
|
||
```text
|
||
CLIProxyAPI + thin cpa-ext plugin ── SQLite/IPC ── cpa-ext sidecar
|
||
```
|
||
|
||
sidecar 只承担长期任务/公共 API,不得形成第二套余额或价格权威。SQLite 单 writer、IPC 认证、文件锁和 crash ownership 必须另行验证。
|
||
|
||
### 3.3 Worker 拓扑
|
||
|
||
只有目标 CPA/Go/toolchain/SQLite driver 的双 runtime 24h soak 通过,才允许动态库启用有界 worker。发布元数据必须标出 topology;不能在补丁版本中静默从 callback-driven 改为 worker。
|
||
|
||
## 4. 文件与权限布局
|
||
|
||
建议 Linux 布局:
|
||
|
||
```text
|
||
<cpa-root>/
|
||
├─ cliproxy
|
||
├─ config.yaml
|
||
├─ plugins/
|
||
│ └─ linux/amd64/cpa-ext-v0.1.0.so
|
||
└─ data/cpa-ext/
|
||
├─ cpa-ext.db
|
||
├─ cpa-ext.db-wal
|
||
├─ cpa-ext.db-shm
|
||
├─ backups/
|
||
└─ diagnostics/
|
||
|
||
/etc/cpa-ext/
|
||
└─ hmac-keyring.json # 或 OS secret store,绝不放进 data/backups
|
||
```
|
||
|
||
要求:
|
||
|
||
- CPA 运行用户拥有 plugin/data,其他用户默认不可读;
|
||
- keyring `0600`、目录 `0700`;
|
||
- DB/backup `0600`、目录 `0700`;
|
||
- plugin binary 只由发布/运维用户写,运行用户只读;
|
||
- 数据目录不能位于临时目录、网络共享或会被自动清理的位置;
|
||
- 不把 SQLite、WAL、keyring、Auth、日志放进发布归档;
|
||
- Windows 使用等价 ACL,不能只依赖扩展名。
|
||
|
||
## 5. 目标配置
|
||
|
||
```yaml
|
||
remote-management:
|
||
allow-remote: false
|
||
secret-key: "<strong-random-management-key>"
|
||
|
||
# 仅保留一个运维离线 sentinel;绝不分发给用户。
|
||
api-keys:
|
||
- "<strong-random-sentinel-key>"
|
||
|
||
plugins:
|
||
enabled: true
|
||
dir: plugins
|
||
configs:
|
||
cpa-ext:
|
||
enabled: true
|
||
priority: 100
|
||
store:
|
||
version: "0.1.0"
|
||
codex_only: true
|
||
data_dir: data/cpa-ext
|
||
database_file: cpa-ext.db
|
||
settlement_currency: USD
|
||
frontend_auth_exclusive: true
|
||
unbound_key_policy: deny
|
||
unpriced_usage_policy: deny_new_requests
|
||
maintenance_mode: callback
|
||
```
|
||
|
||
说明:
|
||
|
||
- 上述是完整目标配置,当前基础插件只解析 `codex_only`;
|
||
- CPA `Home` 是由 `-home-jwt` 注入的 runtime-only 状态,YAML 中的 `home:` 会被当前 parser 忽略;生产启动命令不得使用 `-home-jwt`,并由 readiness 检查实际 runtime 状态;
|
||
- `enabled`、`priority` 是 CPA host-owned 字段,也会出现在 `config_yaml`;
|
||
- `store.version` 用于选择版本化动态库;
|
||
- `database_file` 相对 `data_dir` 解析,不能允许 `..` 逃逸;
|
||
- `settlement_currency` 第一版只能是 USD;
|
||
- HMAC keyring 通过受限 secret-file/OS store locator 注入,不把 key 值放进 YAML;
|
||
- 生产不允许 observe-only、unbound allow 或 unknown price=0;
|
||
- 所有可互相绑定的 Codex Auth 应使用相同 CPA priority。
|
||
|
||
热配置:UI 展示、查询限制、非安全统计刷新参数、明确允许的业务阈值。冷配置:data/database path、keyring source、currency、plugin identity、runtime topology。冷配置变化由 LKG reconfigure 拒绝并提示排空重启。
|
||
|
||
## 6. 本地开发构建
|
||
|
||
### 6.1 WSL/Linux
|
||
|
||
```bash
|
||
cd /mnt/d/agent/cpa-plugin
|
||
./scripts/check-env.sh
|
||
go test ./...
|
||
go test -race ./...
|
||
./scripts/build.sh
|
||
file bin/cpa-ext.so
|
||
nm -D bin/cpa-ext.so | grep cliproxy_plugin_init
|
||
```
|
||
|
||
`build.sh` 会执行 gofmt、`go mod tidy`、单元测试和 c-shared 构建。正式 CI 应把“依赖文件是否被意外修改”作为检查,不能让 tidy 的变化无人审阅。
|
||
|
||
### 6.2 PowerShell 调用 WSL
|
||
|
||
```powershell
|
||
./scripts/build.ps1
|
||
wsl file /mnt/d/agent/cpa-plugin/bin/cpa-ext.so
|
||
wsl nm -D /mnt/d/agent/cpa-plugin/bin/cpa-ext.so
|
||
```
|
||
|
||
当前产物是 Linux `.so`。Windows `.dll` 必须在 Windows CGO toolchain 下单独构建和验证 export。
|
||
|
||
### 6.3 当前测试宿主
|
||
|
||
仓库已有:
|
||
|
||
```bash
|
||
./scripts/run-test-host.sh
|
||
tail -f .runtime/cliproxy.log
|
||
./scripts/stop-test-host.sh
|
||
```
|
||
|
||
`.runtime/` 只用于本地测试,不是生产目录。`/healthz` 只证明 CPA 进程存活,不证明 cpa-ext active、价格可用或不存在认证旁路。
|
||
|
||
## 7. 发布物
|
||
|
||
每个 artifact 必须包含/伴随:
|
||
|
||
- `cpa-ext` 动态库;
|
||
- version、git commit、build timestamp;
|
||
- target OS/arch、Go version、C toolchain;
|
||
- minimum/exact tested CPA version;
|
||
- ABI=1、max RPC schema=3、capability 清单;
|
||
- runtime topology;
|
||
- LICENSE、NOTICE/THIRD_PARTY;
|
||
- SHA-256 checksum,推荐额外签名/provenance/SBOM。
|
||
|
||
CPA plugin store zip 根目录中的动态库文件名使用:
|
||
|
||
```text
|
||
cpa-ext.so
|
||
# 或
|
||
cpa-ext-v0.1.0.so
|
||
```
|
||
|
||
安装目标可位于 `plugins/<goos>/<goarch>/`。发布包不能包含生成 `.h`,除非消费者明确需要。
|
||
|
||
## 8. 安装前检查
|
||
|
||
1. 确认 CPA exact version、plugin-capable build 和平台架构;
|
||
2. 确认 CPA 未使用 `-home-jwt` 启动,实际 runtime `Home.Enabled=false`;
|
||
3. 备份 CPA config、SQLite 和匹配 keyring;
|
||
4. 校验 artifact checksum/signature;
|
||
5. 校验动态库 export `cliproxy_plugin_init`;
|
||
6. 检查 data/keyring 目录权限和磁盘余量;
|
||
7. 检查只有 sentinel 留在 CPA native `api-keys`,没有用户 Key;
|
||
8. 检查 management 仅 loopback/受信网关可达;
|
||
9. 检查所有可绑定 Codex Auth priority;
|
||
10. 准备 gateway drain 和 rollback artifact;
|
||
11. 首次生产部署必须先有已审阅 published price version;
|
||
12. 确认 P0 runtime/performance/soak gate 结论与所选 topology 一致。
|
||
|
||
## 9. 首次安装
|
||
|
||
1. 在排空环境停止 CPA;
|
||
2. 原子复制版本化 dynamic library 到目标目录;
|
||
3. 创建 data/keyring 目录和权限;
|
||
4. 写入/审阅配置,先不开放用户入口;
|
||
5. 启动 CPA,观察 plugin load/register/schema/capabilities;
|
||
6. 等待 migration、integrity 基础检查、keyring、price 和 recovery 完成;
|
||
7. 用 Management `/plugins` 确认 `cpa-ext` active 和版本;
|
||
8. 调用 cpa-ext `/readiness` 与最小 `/ready`;
|
||
9. 执行无效用户 Key 负向探针,确认不能访问上游;
|
||
10. 在隔离测试账户完成“签发 → 准入 → strict route → Usage → 金额账本”;
|
||
11. 对比无插件 OAuth 基线的 TTFT/吞吐;
|
||
12. gateway 才开始逐步放量。
|
||
|
||
任何一步失败都保持用户入口关闭。不能因为 CPA `/healthz` 为 200 就继续。
|
||
|
||
## 10. 启动顺序与 readiness
|
||
|
||
插件内部:
|
||
|
||
1. 解析 lifecycle config/schema;
|
||
2. 验证冷配置和路径;
|
||
3. 打开 SQLite、migration、quick integrity;
|
||
4. 加载 HMAC keyring;
|
||
5. 加载 active PriceVersion;
|
||
6. 恢复 pending/Usage/settlement/outbox;
|
||
7. 加载账户、Credential、route 和统计 Snapshot;
|
||
8. 装配 callback runner/sidecar;
|
||
9. 原子发布 Runtime;
|
||
10. 返回 registration。
|
||
|
||
Readiness component:
|
||
|
||
| Component | 失败影响 |
|
||
| --- | --- |
|
||
| runtime/schema/capabilities | 不注册/不开放流量 |
|
||
| SQLite/migration/ledger | fail closed |
|
||
| HMAC keyring | fail closed |
|
||
| active pricing | fail closed |
|
||
| Credential/account directory | fail closed |
|
||
| statistics projection | 可降级,显示 lag |
|
||
| quota refresh | 可降级,显示 stale |
|
||
| UI resources | 不影响计费,但管理页不可用 |
|
||
| backup/archive runner | 告警,不允许掩盖核心 readiness |
|
||
|
||
外部 gate 组合:
|
||
|
||
- CPA process health;
|
||
- Management plugin active/version/capabilities;
|
||
- cpa-ext self readiness;
|
||
- resource `/ready` 存在;
|
||
- Home 关闭;
|
||
- 负向认证探针;
|
||
- gateway 自己确认 sentinel 未被分发。
|
||
|
||
## 11. 监控与告警
|
||
|
||
最低指标:
|
||
|
||
| 类别 | 指标 |
|
||
| --- | --- |
|
||
| Runtime | active/fused/reconfigure rejected/schema/topology |
|
||
| RPC | 每 method 调用数、错误、panic、耗时、payload bytes |
|
||
| Auth | success/reject/unknown/dependency failure、rate limit |
|
||
| Access | terminate code、strict mismatch、scheduler fallback |
|
||
| Billing | settlement、amount、late revision、unpriced、unmeasured、negative balance |
|
||
| Persistence | transaction latency/error/busy、WAL/DB size、integrity、backup age |
|
||
| Projection | event_seq、checkpoint、lag、rebuild status |
|
||
| Upstream | account active/missing/priority shadow、quota age/error |
|
||
| Performance | TTFT、total latency、chunk gap、alloc/GC、stream RPC bytes |
|
||
| Gateway | open/drained、last readiness/negative probe |
|
||
|
||
必须告警:
|
||
|
||
- plugin route/active 状态消失或 fuse;
|
||
- readiness 核心 component 失败;
|
||
- persistence gap、账本不一致;
|
||
- active price 缺失/未知 GPT-5.6;
|
||
- unpriced/unmeasured 超阈值;
|
||
- keyring mismatch;
|
||
- DB/WAL/磁盘逼近上限;
|
||
- backup 超过目标 RPO;
|
||
- projection lag;
|
||
- priority 变化导致 binding 不可用;
|
||
- stream 性能相对基线退化。
|
||
|
||
指标端点默认只在 Management/受信 sidecar 暴露,不把账户数、金额或错误详情放进公开 `/ready`。
|
||
|
||
## 12. 日常维护
|
||
|
||
### 12.1 每日
|
||
|
||
- 检查 readiness、gateway 和关键告警;
|
||
- 检查 unpriced/unmeasured/persistence gap;
|
||
- 检查 backup 成功和磁盘;
|
||
- 检查 quota stale、missing Auth 和 strict mismatch;
|
||
- 检查异常 Credential 消费和负余额。
|
||
|
||
### 12.2 每周
|
||
|
||
- 运行账本余额 reconciliation;
|
||
- 检查 projection checkpoint 与 archive eligibility;
|
||
- 审阅价格候选,但不自动发布;
|
||
- 审阅 audit、Key rotation/expiry 和管理员操作;
|
||
- 抽样恢复 backup 到隔离目录。
|
||
|
||
### 12.3 每月/发布前
|
||
|
||
- 完整离线 restore drill;
|
||
- 容量/查询计划检查;
|
||
- 依赖、CPA 和价格来源复审;
|
||
- sentinel/Management/HMAC/key rotation 演练;
|
||
- 安全故障注入和 24h soak(涉及 runtime/toolchain/SQLite 变化时)。
|
||
|
||
## 13. 备份与恢复
|
||
|
||
### 13.1 备份
|
||
|
||
- 使用 SQLite online backup API,不直接复制 WAL 模式 `.db`;
|
||
- 备份包含 schema/version/content hash/created_at;
|
||
- keyring 单独加密备份,记录非秘密 fingerprint/key IDs;
|
||
- 保留 7~30 天并至少一份异地副本;
|
||
- 备份目录不由 public API 任意下载;
|
||
- 每次破坏性 migration 和版本升级前强制备份;
|
||
- backup 成功必须通过打开、integrity 和基本账本检查。
|
||
|
||
### 13.2 离线恢复
|
||
|
||
1. gateway 排空并停止 CPA/sidecar;
|
||
2. 保存损坏现场副本;
|
||
3. 选择匹配时间点的 SQLite + keyring;
|
||
4. 在隔离目录验证 checksum、schema、integrity、key fingerprint;
|
||
5. 替换明确目标文件,不操作宽泛目录;
|
||
6. 启动时运行 migration/recovery;
|
||
7. 从权威事实重建 projection;
|
||
8. 运行账本、Credential 和 PriceVersion reconciliation;
|
||
9. 完成全套 readiness/负向/计费探针;
|
||
10. 逐步恢复流量并记录事件审计。
|
||
|
||
缺失/不匹配 keyring 时不得自动生成新 key 后继续;保持 fail closed。
|
||
|
||
## 14. 升级与回滚
|
||
|
||
### 14.1 禁止热替换生产动态库
|
||
|
||
当前 CPA binary hot reload 会把旧 native 实例放入 retired 集合,直到宿主整体 shutdown 才真正 shutdown。旧 runtime/worker/SQLite handle 可能继续存在,因此生产升级固定为:排空 → 停 CPA → 替换/切换 version pin → 启动。
|
||
|
||
### 14.2 升级流程
|
||
|
||
1. 阅读 release notes、CPA compatibility、migration 和 topology 变化;
|
||
2. staging 完成 test-plan 全门禁;
|
||
3. 备份 SQLite + keyring + config;
|
||
4. gateway drain;
|
||
5. 停止 CPA 并确认进程退出;
|
||
6. 安装新版本化 artifact、更新 version pin;
|
||
7. 启动、migration/recovery/readiness;
|
||
8. 执行负向认证、金额 golden、route、cancel/stream 和性能 smoke;
|
||
9. 小流量观察;
|
||
10. 标记升级完成并保留旧 artifact/backup。
|
||
|
||
### 14.3 回滚
|
||
|
||
如果 migration 保持旧版兼容,可以停机切回旧 artifact。若 schema 已不兼容:
|
||
|
||
- 停机;
|
||
- 恢复升级前 SQLite + 对应 keyring;
|
||
- 恢复旧 config/artifact;
|
||
- 重新完成 readiness/探针;
|
||
- 对升级窗口产生的真实上游消费人工 reconciliation,不能静默丢账。
|
||
|
||
价格回滚不需要二进制回滚,走 [pricing.md](pricing.md) 的“创建新发布版本”。
|
||
|
||
## 15. 配置变更
|
||
|
||
- 使用 CPA Management API/YAML 变更前保存当前 revision;
|
||
- 安全敏感变更必须双人复核和 reason;
|
||
- 写入后检查 LKG/`reconfigure_rejected`;
|
||
- capability shape 不因热配置错误消失;
|
||
- 冷配置只在排空重启中修改;
|
||
- 不用手改 SQLite 代替管理 API;
|
||
- 不在 config 中放 Management/downstream/HMAC/OAuth secret 的副本;
|
||
- 变更 Codex Auth priority 前预览受影响 binding。
|
||
|
||
## 16. 常见故障 Runbook
|
||
|
||
### 16.1 CPA 正常但 `/ready` 404
|
||
|
||
立即撤流量 → 检查 Home、plugin global/instance enabled、文件名/版本 pin、架构、export、load/register 日志 → 不要临时删除 sentinel 或放开 native Key。
|
||
|
||
### 16.2 Readiness 显示 DB/keyring/price unhealthy
|
||
|
||
撤流量 → 保留现场 → 检查权限、磁盘、key ID、migration、active price → 修复后运行 integrity/reconciliation → 不切 observe-only。
|
||
|
||
### 16.3 用户请求突然变慢
|
||
|
||
先 A/B 比较同 CPA/OAuth/model/tier/transport:无插件、空 hook、collection-only、完整插件 → 检查 schema=3、每 chunk RPC 字节、HistoryChunks、response-before 重复 body、DB/锁和 GC → 超过 [dev.md](dev.md) 阈值即回滚/撤下相关 hook。
|
||
|
||
### 16.4 strict binding 不可用
|
||
|
||
检查 CPA Auth active/disabled/unavailable、模型和 priority → 查看 scheduler candidates/bindable reason → `priority_shadowed` 时统一 priority 或调整产品绑定,不强行返回隐藏 Auth ID。
|
||
|
||
### 16.5 quota stale
|
||
|
||
不影响已由 CPA candidate 证明可用的基础 strict 请求,但 UI 标 stale → 手动小批 refresh → 检查 provider allowlist、Auth ref、限流和 sidecar → 不把 stale 写成 0/无限。
|
||
|
||
### 16.6 persistence gap/unmeasured 激增
|
||
|
||
撤流量或收紧并发 → 检查 callback、DB、response correlation 和 CPA 版本 → 保存上游证据 → 修复后追加 settlement/adjustment;禁止猜 Token 或覆盖旧账本。
|
||
|
||
## 17. 容量与保留
|
||
|
||
发布前用真实形状数据验证:
|
||
|
||
- 50/500/更多 Credential 与上游账户;
|
||
- 百万级 Request/Execution/Usage/Ledger;
|
||
- 30/90 天查询;
|
||
- streaming 长 Prompt/多 chunk;
|
||
- 并发 1/8/32 及计划上限;
|
||
- WAL、backup、rebuild 和 archive;
|
||
- 磁盘增长、峰值内存、CPU、p95/p99。
|
||
|
||
容量结论记录最后成功点和最小失败点,生产持续负载取安全折扣。可参考 usage-keeper `internal/benchmark/capacity-v1` 的 canonical dataset、clone、cgroup memory peak 和 p99 方法,但需要增加 native plugin/CPA/stream 维度。
|
||
|
||
## 18. 验收标准
|
||
|
||
- 文档明确当前骨架与 production-ready 版本的差别;
|
||
- artifact、CPA、ABI/schema、OS/arch/toolchain 可追溯;
|
||
- 安装前 checksum/export/权限/version pin 均验证;
|
||
- 生产 gateway、sentinel、exclusive 和负向探针缺一不可;
|
||
- `/healthz` 不被误用为插件 readiness;
|
||
- SQLite/keyring 成对备份和隔离恢复演练通过;
|
||
- 升级一定排空并重启 CPA,不热替换;
|
||
- migration 失败、keyring mismatch、price 缺失时保持 fail closed;
|
||
- 关键指标/告警/日常检查和故障 runbook 可执行;
|
||
- WSL/Linux 与 Windows artifact 不混用;
|
||
- 生产配置不使用 `000`、observe-only、用户 native CPA Key 或未确认价格;
|
||
- 发布流程引用并满足 [test-plan.md](test-plan.md) 全部门禁。
|