mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 08:06:19 +00:00
Co-authored-by: Zyy <zyycn@users.noreply.github.com> Co-authored-by: Codex <noreply@openai.com>
1036 lines
67 KiB
Markdown
1036 lines
67 KiB
Markdown
# 部署与运维
|
||
|
||
首次安装可按 [快速开始](../README.md#快速开始) 操作。
|
||
本文补充客户端配置、权限、备份和升级;部署命令从安装目录 `codex-proxy-rs/` 执行,
|
||
其中 `deploy/` 存放 Compose 文件和配置,`.runtime/` 存放持久化数据
|
||
|
||
| 任务 | 入口 |
|
||
| --- | --- |
|
||
| 安装与启动 | [手动安装](#手动安装) · [Windows 二进制部署](#windows-二进制部署) · [启动](#启动) · [公网访问](#公网访问) |
|
||
| 接入客户端 | [客户端配置](#客户端配置) · [时区与客户端环境信息](#时区与客户端环境信息) · [登录与生图排查](#登录与生图排查) |
|
||
| 日常运维 | [运行设置](#启动后的运行设置) · [小内存优化](#小内存优化) · [持久化与日志](#持久化与备份) · [请求排查](#请求错误排查) · [密码轮换](#密码语义) |
|
||
| 更新与恢复 | [镜像升级](#镜像升级与源码构建) · [在线更新](#管理端在线更新) · [备份与恢复](#备份与恢复) · [停止与重启](#停止与重启) |
|
||
| 运行源码 | [开发与源码联调](../docs/development.md) |
|
||
|
||
## 配置归属
|
||
|
||
配置按职责保存,不都在 YAML 中:
|
||
|
||
| 位置 | 内容 | 修改方式 |
|
||
| --- | --- | --- |
|
||
| `deploy/config.yaml` | 监听、数据库/Redis 连接、部署凭据、时区、日志与可选内存分配策略等启动配置 | 从模板创建,已有部署合并修改,不覆盖原文件 |
|
||
| `deploy/compose.yaml` | 镜像、容器网络、端口、挂载、PostgreSQL 启动参数与资源限制 | 调整 Compose 并重建受影响容器 |
|
||
| PostgreSQL | 账号、Key、运行设置、上游身份、插件配置等业务数据 | 管理端或管理 API |
|
||
| 浏览器本地 | 主题和界面偏好 | 当前浏览器的主题设置 |
|
||
|
||
项目不使用 `.env` 配置文件。Compose 环境变量用于容器地址、镜像选择和构建发布;
|
||
可选的内存分配策略由 `config.yaml` 的 `services.app-runtime` 桥接到应用容器。
|
||
配置加载会忽略未知字段,并在启动控制台提示字段名;不输出对应值。缺少必填字段时会指出缺项并停止启动,
|
||
已知字段的类型和取值仍需合法;可选字段省略时使用默认值
|
||
|
||
`store.pool.observability_timeout_seconds` 默认 120 秒,控制观测查询与统计 Worker 的操作预算,
|
||
缓存等待和统计事务的 SQL 时限沿用该配置。修改后重启应用;连接排队仍受
|
||
`store.pool.acquire_timeout_seconds` 约束,模型请求时限由 `api.request_timeout_seconds` 单独控制
|
||
|
||
后端从当前目录向上查找 `deploy/config.yaml`,相对数据、日志和静态资源路径以该文件所在目录解析
|
||
|
||
Compose 通过环境变量将监听和数据库地址设为容器内地址,并指定镜像中的静态资源目录
|
||
|
||
### 部署时区
|
||
|
||
在 `deploy/config.yaml` 设置统一的 IANA 时区,省略时默认 `Asia/Shanghai`:
|
||
|
||
```yaml
|
||
host:
|
||
timezone: 'Asia/Shanghai'
|
||
```
|
||
|
||
支持 `UTC`、`America/New_York`、`Asia/Kathmandu` 等名称;空值、未知名称和错误类型会阻止启动,
|
||
错误指出 `host.timezone`,不输出配置值。修改后重启网关生效,二进制与 Compose 使用同一配置
|
||
|
||
管理端与 Key 用量页的时间文本由后端生成;自然日筛选、日统计、Key 限额、账号预热和备份 Cron 使用该时区。
|
||
浏览器、进程 `TZ` 和 PostgreSQL 会话时区不覆盖业务配置;Compose 的基础设施时间基准为 UTC。
|
||
上游请求身份中的位置时区仍由对应运行设置或代理配置控制,与部署时区独立
|
||
|
||
切换时区保留已打开限额窗口的 UTC 边界与用量,到期后按新时区续接;不会因重启立即清零。
|
||
备份调度从切换后的当前时刻计算未来执行点,不补跑旧时区漏过的任务,已入队任务保持原状态。
|
||
夏令时中不存在的预热或 Cron 时刻跳过,重复时刻只选较早一次。日志日期与保留规则见[日志与保留窗口](#日志与保留窗口)
|
||
|
||
### 小内存优化
|
||
|
||
1 GB 内存、网关与 PostgreSQL / Redis 同机、低并发部署可从本节参数开始评估。
|
||
这些值是调优起点,不是整机内存上限或容量保证;先记录实际负载下的内存峰值,优先缩小数据库连接池和缓冲,再按需调整 Redis 与应用空闲内存回收
|
||
|
||
| 调整对象 | 配置入口 | 生效方式 |
|
||
| --- | --- | --- |
|
||
| 应用空闲内存回收 | `config.yaml` → `services.app-runtime.environment.GLIBC_TUNABLES` | 重建应用容器 |
|
||
| 应用 PostgreSQL 连接池 | `config.yaml` → `store.pool.max_connections` | 重启应用;Compose 可按下文重建 |
|
||
| PostgreSQL 内存与连接预算 | `compose.yaml` → `services.postgres.command` | 重建 PostgreSQL 容器 |
|
||
| Redis 持久化与内存预算 | `config.yaml` → `services.redis-credentials.command` | 重建 Redis 容器 |
|
||
|
||
以下 YAML 片段合并到已有节点,不要重复创建顶层 `store` / `services`,也不要覆盖凭据、挂载和健康检查。
|
||
这些参数没有管理端热更新入口;二进制部署沿用 `store.pool`,PostgreSQL / Redis 参数则配置到各自服务的配置文件或启动命令,应用不会读取 `services` 桥接区来修改外部数据库
|
||
|
||
#### 应用空闲内存回收
|
||
|
||
Linux/glibc 部署可选择更积极地归还请求结束后的空闲内存,适合内存紧张且请求后 RSS 长时间偏高的机器。
|
||
该选项默认关闭,官方 Linux 镜像支持;更频繁的映射与回收可能增加 CPU 开销,应结合实际负载观察
|
||
|
||
在 `deploy/config.yaml` 的 `services.app-runtime.environment` 中设置 `GLIBC_TUNABLES`,空字符串表示不启用额外调优:
|
||
|
||
```yaml
|
||
services:
|
||
app-runtime:
|
||
environment:
|
||
GLIBC_TUNABLES: 'glibc.malloc.mmap_threshold=131072:glibc.malloc.trim_threshold=131072'
|
||
```
|
||
|
||
Compose 从 `services.app-runtime` 取得参数;不开启优化时保留该段并将值设为 `''`。
|
||
PostgreSQL 和 Redis 的凭据桥接配置与该段并列,无需在 `compose.yaml` 重复填写 `GLIBC_TUNABLES`
|
||
|
||
校验并重建应用容器使配置生效;`docker compose restart` 不会更新容器环境变量:
|
||
|
||
```bash
|
||
docker compose -f deploy/compose.yaml config --quiet
|
||
docker compose -f deploy/compose.yaml up -d --no-build --no-deps --force-recreate codex-proxy-rs
|
||
```
|
||
|
||
此配置将 glibc 的 mmap 与 trim 阈值固定为 128 KiB,关闭对应的动态阈值调整。
|
||
如已有 `GLIBC_TUNABLES`,用冒号合并这两个参数,避免重复同名项;同时检查是否另设了 `MALLOC_MMAP_THRESHOLD_` 或 `MALLOC_TRIM_THRESHOLD_`,统一在一处维护。
|
||
参数含义见 [glibc 文档](https://sourceware.org/glibc/manual/latest/html_node/Memory-Allocation-Tunables.html)
|
||
|
||
关闭时删除这两个参数;没有其他 tunable 时将值恢复为 `''`,然后再次重建应用容器。
|
||
`services` 是 Compose 桥接区,直接运行二进制不会读取这一段。Linux/glibc 二进制部署需由启动器在进程启动前设置同一环境变量,例如:
|
||
|
||
```bash
|
||
GLIBC_TUNABLES=glibc.malloc.mmap_threshold=131072:glibc.malloc.trim_threshold=131072 ./codex-proxy-rs
|
||
```
|
||
|
||
该变量由 glibc 在进程启动时读取,不支持管理端热更新。
|
||
它不设置内存上限,不释放仍被请求或连接持有的缓冲,也不处理容器文件缓存;观察时分别比较进程 RSS、cgroup 的 `anon` 与 `file`
|
||
|
||
#### PostgreSQL 与应用连接池
|
||
|
||
在 `deploy/config.yaml` 的 `store` 下设置连接池上限,未列出的池参数保留默认值:
|
||
|
||
```yaml
|
||
store:
|
||
pool:
|
||
max_connections: 5
|
||
```
|
||
|
||
应用默认上限为 20,允许的最小值为 2;降低上限可以限制实际数据库连接数,但也会增加请求、后台写入与管理查询之间的等待。
|
||
如果出现连接获取超时,应结合数据库负载调大池容量,不要仅延长等待时间
|
||
|
||
将 `deploy/compose.yaml` 中 `services.postgres.command` 替换为以下列表,保留 UTC 参数:
|
||
|
||
```yaml
|
||
command:
|
||
- postgres
|
||
- -c
|
||
- timezone=UTC
|
||
- -c
|
||
- log_timezone=UTC
|
||
- -c
|
||
- shared_buffers=64MB
|
||
- -c
|
||
- max_connections=30
|
||
- -c
|
||
- work_mem=2MB
|
||
- -c
|
||
- maintenance_work_mem=32MB
|
||
- -c
|
||
- autovacuum_work_mem=16MB
|
||
- -c
|
||
- max_parallel_workers_per_gather=0
|
||
```
|
||
|
||
`shared_buffers` 缩小共享缓冲;服务端 `max_connections` 限制整个数据库的连接数,应覆盖所有应用池,并为备份、维护和其他客户端留余量。
|
||
它与应用连接池上限是两个配置,调小服务端上限不会主动回收应用池里的空闲连接
|
||
|
||
`work_mem` 是每个排序或哈希操作的基础预算,哈希还受 `hash_mem_multiplier` 影响,一条 SQL 可以同时使用多份预算。
|
||
`maintenance_work_mem` 限制维护操作内存,`autovacuum_work_mem` 按自动清理进程分别计;保持自动清理开启。
|
||
关闭并行查询、缩小这些预算可能增加临时文件 I/O,并延长统计、索引创建与维护时间
|
||
|
||
参数定义见 [PostgreSQL 18 内存与并行查询](https://www.postgresql.org/docs/18/runtime-config-resource.html)和[连接设置](https://www.postgresql.org/docs/18/runtime-config-connection.html)
|
||
|
||
#### Redis 持久化与内存预算
|
||
|
||
项目默认开启 AOF,未显式设置 `maxmemory` 和定时 RDB 快照规则。
|
||
小内存部署可保留 AOF,关闭定时 RDB 快照以减少快照期间的 fork / 写时复制峰值;代价是少一份独立的 RDB 快照,AOF 重写仍需要预留额外内存
|
||
|
||
在已有 `deploy/config.yaml` 中替换 `services.redis-credentials.command`,复用同一文件内的密码锚点,保留该服务的 `healthcheck`:
|
||
|
||
```yaml
|
||
services:
|
||
redis-credentials:
|
||
command:
|
||
- redis-server
|
||
- --appendonly
|
||
- 'yes'
|
||
- --save
|
||
- ''
|
||
- --requirepass
|
||
- *redis_password
|
||
# 当前用量和负载峰值明显低于 64 MiB 时,再取消以下注释
|
||
# - --maxmemory
|
||
# - 64mb
|
||
# - --maxmemory-policy
|
||
# - noeviction
|
||
```
|
||
|
||
`--save` 后的空字符串必须保留,它表示关闭定时快照。持久化取舍见 [Redis 文档](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/)
|
||
|
||
设置内存上限前先查看 `INFO memory` 的 `used_memory`、`used_memory_peak` 和 `used_memory_rss`,为业务增长、分配器和持久化峰值留余量。
|
||
`maxmemory 64mb` 只是可选起点,未达到上限时不会因此降低 RSS,也不是进程或容器的总内存上限。
|
||
达到上限后,`noeviction` 会拒绝需要增加内存的写入,可能影响登录与请求调度,应调大预算或减少负载
|
||
|
||
Redis 保存租约、冷却和登录会话等[协调状态](../docs/architecture.md#8-状态所有权),不要使用 `allkeys-lru`、`volatile-lru` 等提前淘汰键的策略。
|
||
预算与策略定义见 [Redis 内存限制](https://redis.io/docs/latest/develop/reference/eviction/)
|
||
|
||
#### 使配置生效与检查效果
|
||
|
||
只调整应用配置时,按[应用空闲内存回收](#应用空闲内存回收)中的命令重建应用容器。
|
||
调整 PostgreSQL / Redis 启动参数需要短暂停服,在维护窗口执行;只改其中一个服务时,数据库重建命令只列出该服务:
|
||
|
||
```bash
|
||
docker compose -f deploy/compose.yaml config --quiet
|
||
docker compose -f deploy/compose.yaml stop codex-proxy-rs
|
||
docker compose -f deploy/compose.yaml up -d --no-build --no-deps --force-recreate --wait postgres redis
|
||
docker compose -f deploy/compose.yaml up -d --no-build --no-deps --force-recreate --wait codex-proxy-rs
|
||
```
|
||
|
||
数据库健康检查通过后再执行最后一条命令;`restart` 不会更新容器启动命令或环境变量。
|
||
外部数据库需通过其管理方式应用参数,不能用本地 Compose 命令代替
|
||
|
||
通过交互提示输入对应数据库密码,核对实际生效值与内存占用,避免将密码写入命令行:
|
||
|
||
```bash
|
||
docker compose -f deploy/compose.yaml exec postgres psql -U codex_proxy -d codex_proxy -W \
|
||
-c 'SHOW shared_buffers; SHOW max_connections; SHOW work_mem; SHOW maintenance_work_mem; SHOW autovacuum_work_mem; SHOW max_parallel_workers_per_gather;'
|
||
docker compose -f deploy/compose.yaml exec redis redis-cli --askpass CONFIG GET appendonly save maxmemory maxmemory-policy
|
||
docker compose -f deploy/compose.yaml exec redis redis-cli --askpass INFO memory
|
||
docker compose -f deploy/compose.yaml stats --no-stream
|
||
```
|
||
|
||
在相同负载下比较空闲、请求峰值与后台统计 / 维护期间的占用,并检查实际模型请求、管理查询是否正常,有无连接获取超时、Redis OOM 或容器重启。
|
||
不要把 PostgreSQL 各进程的 RSS 直接相加,共享页会重复计数;容器文件缓存也应与匿名内存区分。
|
||
容器硬内存上限不能代替这些预算,过紧会导致 OOM;需要回退时恢复调整前的参数,按同样流程重建对应容器
|
||
|
||
## 部署结构
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
Client[客户端 / 管理浏览器] -->|HTTPS| Proxy[反向代理]
|
||
Proxy -->|HTTP / SSE / WebSocket| App[网关:单副本]
|
||
App --> PG[(PostgreSQL)]
|
||
App --> Redis[(Redis)]
|
||
App --> Upstream[上游服务]
|
||
App -. 数据库备份 .-> Backup[S3 / R2]
|
||
```
|
||
|
||
网关端口默认只绑定本机;数据库和 Redis 不对公网开放。插件以网关同一系统身份运行,不能作为不可信代码沙箱
|
||
|
||
## 手动安装
|
||
|
||
本节适用于 Linux amd64/arm64,需准备 Docker Engine、Docker Compose Plugin、curl 和 OpenSSL,
|
||
并确保当前用户能访问 Docker、可通过 `sudo` 或 root 设置目录权限。一键安装入口见
|
||
[快速开始](../README.md#一键安装),脚本会自动完成本节准备和服务启动
|
||
|
||
下载同一正式 Release 的部署文件并设置目录权限:
|
||
|
||
```bash
|
||
mkdir -p codex-proxy-rs/deploy && cd codex-proxy-rs
|
||
|
||
# 只解析一次最新正式版本,确保两个文件来自同一 Release。
|
||
CPR_RELEASE_URL="$(curl -fsSL -o /dev/null -w '%{url_effective}' https://github.com/zyycn/codex-proxy-rs/releases/latest)"
|
||
CPR_RELEASE_TAG="${CPR_RELEASE_URL##*/}"
|
||
curl -fsSL "https://github.com/zyycn/codex-proxy-rs/releases/download/${CPR_RELEASE_TAG}/compose.yaml" \
|
||
-o deploy/compose.yaml
|
||
curl -fsSL "https://github.com/zyycn/codex-proxy-rs/releases/download/${CPR_RELEASE_TAG}/config.example.yaml" \
|
||
-o deploy/config.example.yaml
|
||
|
||
install -d -m 0750 .runtime/postgres .runtime/redis
|
||
sudo install -d -m 0770 -o "$(id -u)" -g 10001 .runtime/data .runtime/logs
|
||
sudo install -m 0640 -o "$(id -u)" -g 10001 deploy/config.example.yaml deploy/config.yaml
|
||
```
|
||
|
||
也可将 `CPR_RELEASE_TAG` 设置为指定的发布标签。Release 附带的 `compose.yaml` 默认使用该版本镜像,
|
||
配置模板和部署文件均包含在 `checksums.txt` 中;不要混用 `main` 分支模板与已发布镜像
|
||
|
||
为 PostgreSQL 与 Redis 分别生成一个密码:
|
||
|
||
```bash
|
||
openssl rand -hex 24
|
||
openssl rand -hex 24
|
||
```
|
||
|
||
把两个 48 位十六进制结果分别写入 `deploy/config.yaml` 的:
|
||
|
||
- `store.database.password`
|
||
- `store.redis.password`
|
||
|
||
另行设置 `admin.default_password`:至少 12 个字符,不能使用常见弱口令或包含 `$`
|
||
|
||
| 会话配置 | 含义 | 配置模板 / 默认值 |
|
||
| --- | --- | --- |
|
||
| `admin.session_ttl_minutes` | 管理员续期凭据有效期,成功续期后重新计时 | 模板为 10080 分钟(7 天) |
|
||
| `admin.session_absolute_ttl_minutes` | 从登录起计算的最长有效期 | 模板及省略时均为 43200 分钟(30 天) |
|
||
| `client.session_ttl_minutes` | 密钥身份固定有效期,不随活动续期 | 默认 1440 分钟(1 天) |
|
||
|
||
管理员访问凭据最多有效 24 小时,由内部策略控制;不活动期限较短时,访问期限缩短到其四分之一,为自动续期留出窗口。
|
||
推荐使用模板中的 7 天不活动期限和 30 天最长登录期限。修改配置需重启,不能延长已建立会话的最长有效期。
|
||
浏览器会话与续期规则见[认证 API](../docs/api.md#统一登录与会话),独立于 `/v1/*` 鉴权和限额
|
||
|
||
PostgreSQL 与 Redis 密码统一接受 16–128 字节,不能全为空白或包含控制字符;仍须满足
|
||
各自服务端的密码策略。托管服务可使用服务商允许的高强度密码,默认安装继续生成 48 位随机十六进制密码
|
||
|
||
Compose 通过 `config.yaml` 的凭据桥接区引用同一密码;三个值都不需要额外导出为环境变量,
|
||
数据库和 Redis 密码不能嵌入连接 URL。通过环境变量提供密码时分别使用
|
||
`CPR_DATABASE_PASSWORD` 与 `CPR_REDIS_PASSWORD`,原始密码由连接 URL 构造器编码,不要预先做 URL 编码。Compose 凭据桥接建议沿用上面的
|
||
随机十六进制密码,避免 `$` 的变量插值语义
|
||
|
||
Linux 上应用容器以 `10001:10001` 运行。上述命令将应用数据和日志目录设为 `0770`,
|
||
配置设为 `0640`,均由当前用户持有、容器组 `10001` 访问。
|
||
`config.yaml` 通过 Compose `configs` 只读挂载,普通 Compose 保留宿主机文件的 UID/GID 和 mode
|
||
|
||
### Redis ACL 用户
|
||
|
||
连接已配置 ACL 用户的 Redis 时,把用户名写在 `store.redis.url` 中,密码仍单独填写:
|
||
|
||
```yaml
|
||
store:
|
||
redis:
|
||
url: 'redis://u1@redis-host:6379/0'
|
||
password: &redis_password '<u1 的密码>'
|
||
```
|
||
|
||
将这两项合并到已有配置,保留其他配置和密码锚点;Redis 服务端需事先创建并授权该用户。
|
||
用户名中的 `@`、`:` 等特殊字符需要 URL 百分号编码。未填写用户名时使用 Redis 的 `default` 用户
|
||
|
||
环境变量 `CPR_REDIS_URL` 优先于 `store.redis.url`。默认 `deploy/compose.yaml` 已将它设为
|
||
`redis://redis:6379/`,使用 ACL 用户时还需把应用服务的该环境变量改为
|
||
`redis://u1@redis-host:6379/0`,其中主机名必须能从应用容器访问;只改 `config.yaml` 不会生效。
|
||
默认 Compose Redis 服务使用密码认证,不会自动创建 ACL 用户
|
||
|
||
## Windows 二进制部署
|
||
|
||
发行包提供 Windows x64(`windows_amd64`)和 ARM64(`windows_arm64`)版本,按运行机器的 CPU 架构选择。
|
||
需先准备可访问的 PostgreSQL 和 Redis,创建数据库及用户;使用数据库备份功能时,还需将匹配服务端版本的 `pg_dump` 加入 `PATH`
|
||
|
||
从 [Release 附件](https://github.com/zyycn/codex-proxy-rs/releases) 下载对应的
|
||
`codex-proxy-rs_<version>_windows_<arch>.tar.gz`,解压后保留 `codex-proxy-rs.exe`、`web/dist`、`deploy` 和 `plugins/official` 的目录关系。
|
||
在解压后的 `codex-proxy-rs` 目录中打开 PowerShell,首次安装时创建配置:
|
||
|
||
```powershell
|
||
Copy-Item .\deploy\config.example.yaml .\deploy\config.yaml
|
||
```
|
||
|
||
编辑 `deploy/config.yaml`,填写 `store.database`、`store.redis` 的连接地址和独立密码,以及 `admin.default_password`。
|
||
密码格式沿用模板要求;将已有的 `api.asset_directory` 改为 `../web/dist`,在 `host` 下合并以下更新与重启配置:
|
||
|
||
```yaml
|
||
host:
|
||
system_update:
|
||
deployment_mode: binary
|
||
self_restart_enabled: true
|
||
api:
|
||
asset_directory: '../web/dist'
|
||
```
|
||
|
||
从安装目录启动:
|
||
|
||
```powershell
|
||
.\codex-proxy-rs.exe serve
|
||
```
|
||
|
||
启动成功后访问 `http://127.0.0.1:8080`,使用配置中的管理员账号登录;在运行终端按 `Ctrl+C` 停止服务。
|
||
官方构建支持管理端[在线更新、重启与回滚](#管理端在线更新),安装目录与 `.runtime` 必须可写。
|
||
手动更换版本时先停止进程并备份当前发行文件,再整体替换 EXE、`web/dist` 和 `plugins/official`,
|
||
保留 `deploy/config.yaml` 与 `.runtime`,按新版本模板合并配置后重新启动
|
||
|
||
## 启动
|
||
|
||
```bash
|
||
docker compose -f deploy/compose.yaml config --quiet
|
||
docker compose -f deploy/compose.yaml pull
|
||
docker compose -f deploy/compose.yaml up -d --no-build --wait
|
||
docker compose -f deploy/compose.yaml ps
|
||
```
|
||
|
||
Consul 或负载均衡器用 `GET /readyz` 决定是否接入流量。升级后先在实例上确认返回 `204`,
|
||
再把服务注册中的检查路径切到 `/readyz`。如果路由还依赖 Docker 容器健康状态,
|
||
也要将应用容器的 `healthcheck.test` 指向 `/readyz`
|
||
|
||
```bash
|
||
curl -i http://127.0.0.1:8080/readyz
|
||
```
|
||
|
||
用独立监控定期请求 `GET /healthz`,对文件日志故障、账号解冻等关键后台任务异常告警。镜像和 Compose 默认使用这项综合检查。
|
||
两个接口的状态码见[健康检查 API](../docs/api.md#2-健康检查)
|
||
|
||
`/healthz` 返回 `503` 时,失败组件与原因保存在运维错误记录中,组件为 `health`,关联标识以
|
||
`probe:` 或 `worker:` 开头。Worker 事件的错误详情包含任务状态和 `last_error`。连续相同故障去重,恢复后再次失败重新记录,
|
||
应用日志记录故障与恢复摘要。健康响应保持空正文,详情沿用有界观测队列,队列或存储故障时可能缺失
|
||
|
||
HTTP 在数据库迁移和初始化完成后才监听。迁移期间启动日志每 15 秒报告已等待时间,
|
||
包含执行迁移和等待迁移锁的时间,不代表完成比例;不要因暂时无法访问而反复重启。
|
||
镜像和 Compose 默认提供 5 分钟健康检查启动宽限期,探测成功后立即转为健康,无需等满宽限期。
|
||
历史数据较多时应提前延长 `healthcheck.start_period` 和部署工具的等待超时;只修改 Compose
|
||
文件不会改变已创建容器的检查配置,需要在维护窗口重新创建容器
|
||
|
||
不要把未脱敏的 `docker compose config` 或 `docker inspect` 输出上传到工单;它们会包含
|
||
PostgreSQL/Redis 启动密码。日常校验使用 `config --quiet`
|
||
|
||
### 启动后的运行设置
|
||
|
||
在管理端修改以下设置,保存后对新请求生效,重启不会覆盖已保存的选择:
|
||
|
||
| 设置 | 入口与边界 |
|
||
| --- | --- |
|
||
| 上游客户端身份 | “系统设置 → 上游配置 → 客户端身份”;Key 可按 Provider 覆盖,支持自动或固定版本 |
|
||
| 全局请求位置 | “系统设置 → 上游配置 → 请求位置覆盖”;默认关闭,不从 YAML 导入 |
|
||
| 代理位置 | “代理管理”;优先于全局位置,全局关闭时仍可独立生效 |
|
||
|
||
位置覆盖只影响 OpenAI 请求中受支持的位置与时区信息,不改变上下文日期、真实出口 IP、epoch 时间戳或系统时区。
|
||
`openai.residency` 和 `openai.account_routing_override` 是独立于客户端身份的启动配置,
|
||
分别指定 `x-openai-internal-codex-residency` 和 `x-openai-account-routing-override` 请求头。
|
||
两项均为可选字符串,配置值原样发送并覆盖客户端同名头,支持自定义地域或路由值。
|
||
省略或设为 `null` 时删除对应请求头。
|
||
修改后重启应用生效。身份与位置字段见 [运行设置 API](../docs/api.md#8-运行设置)
|
||
后台账号操作使用 Provider 自己的官方画像;发布资料刷新不改写用户选择或 `config.yaml`
|
||
|
||
### CPR 互联
|
||
|
||
在上游的「API 密钥」中为专用密钥选择账号分组并开启「CPR 互联」,即可共享密钥预算、并发上限和 RPM,并按需公开账号池、账号额度和套餐信息。
|
||
共享 Key 的实际推理只使用这些分组中的直连 OAuth 账号;分组内的 API Key 转供账号不会参与
|
||
|
||
在下游导入或编辑 OpenAI API Key 账号,填写上游 API 地址和该 Key,后台自动发现 CPR 互联能力。
|
||
地址包含自定义前缀时保留完整前缀,后台在该地址下请求 `cpr/link`。
|
||
列表与详情显示已保存的观测。展开账号可查看上游密钥限制,普通账号并发留空时跟随上游有效的正数上限。启用的 OpenAI API Key 账号可点击「刷新额度」重新识别。上游明确关闭共享或不支持互联时,账号显示为 API,重新识别成功后显示 CPR
|
||
|
||
第三方网关通过插件实现相同协议也可被识别,不按软件名称判定兼容。
|
||
上游明确返回 Key 预算耗尽时,下游暂停该账号;后续同步确认允许使用后自动恢复,普通限流冷却独立保留。
|
||
金额只供展示,缺少访问状态时不根据余额推断恢复。账号池额度观测作为 Smart 选择的参考,不能保证下一次请求成功
|
||
互联两端均须使用 CPR Link v3;协议、第三方接入与公开边界见 [CPR 互联 API](../docs/api.md#cpr-互联只读协议)
|
||
|
||
### 批量维护代理
|
||
|
||
在“代理管理 → 新增代理”切换到“批量”,每行粘贴一个完整代理 URL、`host:port` 或 `host:port:用户名:密码`,最多 100 条。
|
||
完整 URL 使用各自协议,简写使用表单选择的协议;IPv6 主机使用方括号,简写中的密码可包含冒号。
|
||
名称按前缀和输入顺序生成,请求位置继承全局设置。勾选“添加后测试”时,只测试本次成功添加的记录。
|
||
添加和测试结果合并展示,便于区分保存失败和连接测试失败
|
||
|
||
列表支持跨页选中,勾选代理后显示“批量测试”按钮,点击直接测试选中项。
|
||
测试同时检测连通性和出口地区;测试前固定实际名单,同时执行两条,结果更新到列表。
|
||
批量添加中的停止操作只阻止后续提交,已开始的请求仍会完成。
|
||
离开代理页面停止后续提交,批量进度不在刷新后保留,已保存代理和测试结果仍可在列表查询
|
||
|
||
格式错误、重复或配置冲突不覆盖已有代理,也不阻止其他条目处理。
|
||
“修改未添加项”只带回明确未添加的条目;连接测试失败的代理已经保存,可在列表中重新测试。
|
||
请求中断或结果无法确认时暂停后续提交,添加结果待确认的条目可能已保存,应先刷新列表核对,不能直接重复添加
|
||
|
||
创建或编辑账号时,在“出站隧道”选择直连或已保存的代理,点击右侧的“测试出口”图标。
|
||
耗时显示在对应选项右侧,悬浮可查看 IPv4 / IPv6,测试失败通过通知提示。
|
||
禁用代理的账号使用服务器直连出口。出口测试不保存账号设置,也不验证账号权限或额度;账号可用性使用账号连接测试
|
||
|
||
在代理列表点击关联账号数量,再点击“分配账号”,搜索并跨页勾选账号后点击右上角的“分配”,一次最多 1000 个。
|
||
已使用此代理的账号不可重复选择;所选账号原有代理会被替换,凭据、分组与调度设置保留。禁用代理须先启用
|
||
|
||
### 插件命令行
|
||
|
||
使用与网关相同的工作目录和 `deploy/config.yaml`,读取制品已接受且当前已启用的插件实例:
|
||
|
||
```bash
|
||
codex-proxy-rs plugin --help
|
||
codex-proxy-rs plugin '实例ID' '命令' --help
|
||
codex-proxy-rs plugin '实例ID' '命令' --参数 '值'
|
||
```
|
||
|
||
将示例中的实例 ID、命令、参数名和值替换为插件实际提供的内容
|
||
|
||
不带子命令或使用 `serve` 时启动网关;插件命令不监听 HTTP、不启动后台任务,也不恢复或清理正在运行的
|
||
网关准入状态。顶层 `--help/--version` 不读取配置。插件帮助只读数据库,不执行迁移、登录或账号保存,
|
||
因此需要先由正常部署完成数据库初始化。宿主诊断保留在配置的文件日志中,stdout/stderr 专用于命令结果
|
||
|
||
参数支持 `--名称=值` 或 `--名称 值`;布尔参数可直接写 `--名称`,关闭时写 `--名称=false`。
|
||
duration 使用 `500ms`、`1m30s` 等带单位形式。参数可能进入 shell 历史和系统进程列表,不能因为插件声明
|
||
了敏感参数就认为命令行是安全的密钥输入通道;真实凭据优先使用对应插件提供的受控登录或导入入口
|
||
|
||
成功命令原样返回插件退出码与输出。命令受总期限约束,`Ctrl-C` 或终止信号会取消调用并回收插件进程。
|
||
若命令产生账号,只有成功结果才按顺序通过 Admin 保存;调用仍检查期限、实例版本和当前资源事实,
|
||
模型执行 Key 由具体调用选择,不在实例中预绑定。
|
||
部分保存后失败或取消不会自动重放,先核对账号和审计记录,再决定是否重新执行
|
||
|
||
独立仓库 `codex-proxy-plugins` 的 `examples/workbench` 提供命令行、请求处理与管理页面示例;模型相关操作会产生真实上游用量
|
||
|
||
## 公网访问
|
||
|
||
Compose 默认只绑定 `127.0.0.1`。从其他设备访问时,在应用前配置反向代理,
|
||
不要把 PostgreSQL 或 Redis 暴露到公网
|
||
|
||
同源登录页的管理员与 API Key 登录都支持 HTTP 和 HTTPS 登录。反向代理应原样保留浏览器的 `Origin`,
|
||
不要清除它或改写 Cookie 的 `Secure` 属性;HTTPS 反代可以使用 HTTP 回源。
|
||
HTTP 传输不加密,公网部署仍建议使用 HTTPS。
|
||
会话 Cookie 合同见 [管理接口鉴权](../docs/api.md#管理接口)
|
||
|
||
反向代理需要保留 `Authorization`,支持 `/v1/responses` 的 WebSocket Upgrade,
|
||
并关闭 SSE 响应缓冲。读取超时应覆盖长时间生成任务。
|
||
客户端使用部署地址下的 `/v1`,协议与部署一致,不要使用前端开发服务的 `5173/dev/v1`。
|
||
当前应用只支持单副本,不能通过复制容器扩容
|
||
|
||
Responses 流式请求完成准入后,每 15 秒无输出会发送一次 SSE 注释保活,覆盖首个业务事件前的等待,
|
||
并设置 `X-Accel-Buffering: no` 和 `Cache-Control: no-cache, no-transform`。
|
||
反向代理仍需允许这些响应头生效;准入前的等待需要足够的读取超时。
|
||
心跳仅覆盖客户端到 CPR 的下游链路,不会延长账号出站 HTTP/SOCKS 代理的读取期限。
|
||
提前发送心跳后的状态码与错误行为见 [Responses 交付合同](../docs/api.md#容量拒绝与重试)
|
||
|
||
网关默认不限制模型请求的总执行时长;OpenAI 上游流默认有 300 秒空闲超时。
|
||
`api.request_timeout_seconds` 默认 `null`,只控制 HTTP 路由返回响应前的等待,不是流式正文或 WebSocket 每轮执行的总时限。
|
||
排队、插件显式执行期限与客户端断开的边界见 [请求期限](../docs/api.md#请求期限)
|
||
|
||
OpenAI Responses 上游 WebSocket 每 25 秒发送 Ping 保活,并回应上游 Ping;Ping/Pong 写入最多等待 5 秒,
|
||
不因等待 Pong 超时主动断开。入站背压期间暂停主动 Ping,恢复后不集中补发。
|
||
下游 Responses WebSocket 每 25 秒发送 Ping,第一条未响应的 Ping 发出后 30 秒没有入站活动即断开;
|
||
接收中的消息字节、Pong 和其他有效入站消息均算活动,慢速上传不必等整帧收齐才刷新探活状态。
|
||
后续 Ping 不延长这次截止,心跳入队及写入最多等待 5 秒。两段心跳各自处理,不跨 WS 转发或转换为 SSE 事件。
|
||
心跳不重置上游业务消息的 `openai.ws_pool.stream_idle_timeout_ms`
|
||
|
||
每条已保存代理可在管理端配置“上游 WS 连接上限”,默认留空、不限制连接数。
|
||
预算限制单个 CPR 进程使用该代理的 OpenAI Responses 上游 WS 总数,关联账号共享,包含建连中、在途、空闲及非池化连接;直连不受影响。
|
||
上限应按代理实际容量、其他流量与 CPR 实例数设置。例如 32 槽代理可配置为 24,为 HTTP 等流量留出 8 槽,但这不是统一默认值。
|
||
不同代理 URL(含认证)分别计数;不同 URL 若共用服务商额度,需自行分配预算,各 CPR 实例之间也不协调容量。
|
||
修改上限对后续请求生效,已存在的连接继续计数;调低上限不会强制中断在途或绑定下游 WS 的连接。
|
||
预算仅覆盖宿主原生 OpenAI Responses 连接,插件自建连接及其他 HTTP/WS 流量仍需预留容量
|
||
|
||
未绑定活跃客户端 WS 的池化连接,归池后按 `openai.ws_pool.idle_timeout_ms` 计时,默认 `300000`(5 分钟,必须大于零)。
|
||
Ping/Pong 不延长空闲期限;默认每 5 秒扫描,通常在空闲 300~305 秒开始回收,复用前也检查是否到期。
|
||
关闭先停止复用,最多等待 1 秒发送 Close,随后终止并等待 socket 释放,容量在物理连接释放后才归还。
|
||
仍绑定活跃客户端 WS 的连接不受空闲期限约束;客户端退出会释放仍归它所有的上游连接,重新绑定或切到 HTTP 时释放失去用途的旧连接。
|
||
连接总寿命由 `openai.ws_pool.max_age_ms` 控制,默认 55 分钟;到期退出复用,不为清池打断正在生成的响应。
|
||
满额时优先关闭同代理下最久未使用的普通空闲 WS;在途、建连中和绑定下游 WS 的连接不参与容量淘汰。
|
||
没有可回收连接时等待容量,受原请求期限、客户端取消和连接池关闭约束;无显式请求期限时不会额外设置排队超时。
|
||
被回收连接上的精确续接沿用既有客户端完整回放合同,可能多一次恢复往返
|
||
|
||
若 Codex 在压缩或长时间生成时出现 `error decoding response body`,这表示
|
||
客户端读取 HTTP 响应体失败。请结合网关请求诊断中的 `upstream.read.failed`、
|
||
`downstream.body.closed` 和反向代理日志判断断开位置,不能仅凭此消息认定是 JSON 格式错误。
|
||
SSE 注释保活用于防止传输链路空闲断开,不会重置 Codex 等待完整 SSE 事件的超时;
|
||
若报错为 `idle timeout waiting for SSE`,再检查客户端的 `stream_idle_timeout_ms`
|
||
|
||
## 客户端配置
|
||
|
||
在管理端创建客户端密钥,打开「使用密钥」,按操作系统复制 `config.toml`。
|
||
已有配置先备份,合并后完全退出并重启 Codex。代理密钥由 Provider 配置提供,
|
||
不需要另写 `auth.json`,已有官方登录文件可以保留
|
||
|
||
管理端生成的 CC Switch 一键导入包含连接信息和当前 Key 的日/周额度查询,默认每 30 分钟刷新。
|
||
查询地址和凭据随导入生成,在 CC Switch 中修改 Provider 的地址或 Key 后,需重新导入以同步用量查询。
|
||
导入不包含下方模板的远程压缩、原生生图、WebSocket 和语音设置。需要这些设置时,使用下方配置直连 CPR,
|
||
避免 CC Switch 切换或接管后重写 Provider 配置
|
||
|
||
Linux/macOS 默认目录为 `~/.codex/`,Windows 为 `%USERPROFILE%\.codex\`;
|
||
设置过 `CODEX_HOME` 时以该目录为准。Provider 设置应写入用户配置,不要只写到项目目录。
|
||
配置层级见 [官方配置参考](https://learn.chatgpt.com/docs/config-file/config-reference)
|
||
|
||
### config.toml
|
||
|
||
以下示例与当前管理端模板的配置项一致。替换地址和密钥,模型可改成该密钥有权限使用的模型:
|
||
|
||
```toml
|
||
model_provider = "OpenAI"
|
||
model = "gpt-5.6-terra"
|
||
review_model = "gpt-5.6-terra"
|
||
model_reasoning_effort = "max"
|
||
service_tier = "default"
|
||
# 语音通话控制地址
|
||
experimental_realtime_ws_base_url = "http://127.0.0.1:8080/v1"
|
||
|
||
[model_providers.OpenAI]
|
||
name = "OpenAI"
|
||
base_url = "http://127.0.0.1:8080/v1"
|
||
model_catalog_url = "http://127.0.0.1:8080/v1/models"
|
||
wire_api = "responses"
|
||
supports_websockets = false
|
||
requires_openai_auth = false
|
||
# 代理密钥
|
||
experimental_bearer_token = "<client-api-key>"
|
||
|
||
[model_providers.OpenAI.capabilities]
|
||
# V2 远程压缩
|
||
remote_compaction = "v2"
|
||
|
||
[model_providers.OpenAI.http_headers]
|
||
# 服务端托管认证
|
||
X-OpenAI-Actor-Authorization = "proxy-managed"
|
||
|
||
[features]
|
||
image_generation = true
|
||
goals = true
|
||
```
|
||
|
||
`OpenAI` 是自定义 Provider ID,大小写要与 `model_provider` 一致。合并配置时修改已有表,
|
||
不要重复添加 `[features]` 或 Provider 表。更换模型时也要检查其支持的推理强度。
|
||
密钥以明文保存,文件仅供本人读取,不要提交到 Git。接入代理不需要扩大命令沙箱的联网或文件权限
|
||
|
||
`model_catalog_url` 指向同一网关的 `/v1/models`;更换网关地址时与 `base_url` 一起修改。
|
||
该接口返回当前密钥可见的模型目录,具体格式见 [模型目录](../docs/api.md#模型目录)
|
||
|
||
V2 远程压缩通过 Responses 接口发送 `compaction_trigger`,网关和上游需支持该协议。
|
||
若 Provider ID 改为 `custom`,相应能力配置也要改到 `[model_providers.custom.capabilities]`
|
||
|
||
需要指定完整模型目录时,在账号的模型列表中导出所选 Codex 模型,并在 `config.toml` 顶层设置
|
||
`model_catalog_json = "/absolute/path/to/cpr-model-catalog.json"`。导出文件不含账号凭据;
|
||
它是一次目录快照,调整选择或上游模型能力变化后需重新导出
|
||
|
||
### 生图和 WebSocket
|
||
|
||
模板启用原生生图。Codex 可在任务需要图片时调用 `image_gen.imagegen`,
|
||
再通过代理的 Images 接口生成或编辑图片;也可以在需求中明确要求生成并使用图片。
|
||
需要支持该能力的客户端、支持图片输入的对话模型,以及有生图权限和额度的 Codex 账号。
|
||
代理不会增加上游权限,xAI 账号不能承接这些 Images 请求
|
||
|
||
生图不要求开启 WebSocket。要启用客户端 WebSocket,把当前 Provider 的
|
||
`supports_websockets` 改为 `true`,并检查反向代理是否允许 Upgrade
|
||
|
||
客户端到代理、代理到上游是两段独立连接。客户端关闭 WebSocket 后,
|
||
服务端仍可能用 WebSocket 访问上游;客户端开关不控制服务端连接池和 HTTP 回退策略。
|
||
Codex OAuth 账号的上游传输方式默认 WS;固定为 SSE 的账号不承接必须依赖 WS 的预热及连接内续接。
|
||
仅使用这类账号时,客户端保持 `supports_websockets = false`,避免先尝试 WS 再回退
|
||
|
||
### 语音通话
|
||
|
||
Codex 0.164.0 的 `/voice` 通过 Provider 的 `base_url` 创建通话,控制连接另用顶层
|
||
`experimental_realtime_ws_base_url`,不会自动继承 Provider 地址。两者应指向同一网关的 `/v1`,
|
||
使用相同的代理密钥,并确保反向代理允许 WebSocket Upgrade。此设置独立于 Responses 的
|
||
`supports_websockets` 开关
|
||
|
||
已有配置需把 `experimental_realtime_ws_base_url` 合并到第一个 TOML 表之前,修改后完全退出并重启
|
||
Codex。创建通话成功后仍报 `realtime websocket handshake failed` 时,先检查客户端日志中的
|
||
`connecting realtime websocket` 是否指向网关;若仍为 `api.openai.com`,说明控制连接未经过代理
|
||
|
||
语音使用具备上游权限的 OAuth 账号,音频流由客户端与上游直接传输,路由与限制见
|
||
[Codex Live 语音通话](../docs/api.md#codex-live-语音通话)
|
||
|
||
### 时区与客户端环境信息
|
||
|
||
要覆盖模型请求中的位置和时区,在管理端启用全局自定义位置,或为账号关联的代理设置位置;
|
||
代理位置优先。`host.timezone` 控制部署日历和管理端时间展示,不代替请求位置设置。
|
||
覆盖字段与环境消息识别条件见 [代理位置合同](../docs/api.md#独立代理管理--managed-proxies)
|
||
|
||
Codex 回答仍显示本机时区时,先核对请求是否使用了预期账号和代理,再区分时间上下文与工具结果:
|
||
|
||
- 使用新会话询问环境上下文中的时区,避免已有工具输出或历史回答影响判断
|
||
- 检查 `environment_context` 和 Desktop 的 `codex_apps_client_time_context` 是否满足[时间上下文识别条件](../docs/api.md#独立代理管理--managed-proxies);显式普通文本分类或非法 XML 不会被覆盖
|
||
- 若模型调用 `date` 等工具查询本机,工具结果仍反映真实系统时区,覆盖不会修改客户端操作系统
|
||
|
||
网关的请求头画像和位置覆盖不提供完整匿名化。正文与工作区 metadata 的保留范围见
|
||
[正文兼容](../docs/api.md#正文兼容),xAI 请求不应用 OpenAI 的请求位置覆盖
|
||
|
||
Codex 客户端还有独立的出站请求。analytics 使用 `chatgpt_base_url`,可以包含真实系统类型、
|
||
系统版本和架构;模型 Provider 的 `base_url` 不改变该地址。官方发行配置默认启用 Statsig 指标,目标为
|
||
`https://ab.chatgpt.com/otlp/v1/metrics`,不由网关上游画像控制
|
||
|
||
需要关闭这两类遥测时,将以下字段合并到客户端 `config.toml`,避免重复创建已有表,完全退出并重启 Codex:
|
||
|
||
```toml
|
||
[analytics]
|
||
enabled = false
|
||
|
||
[otel]
|
||
metrics_exporter = "none"
|
||
```
|
||
|
||
这两个开关不清理模型请求中的工作目录、Git metadata、用户内容或工具结果。
|
||
|
||
### 登录与生图排查
|
||
|
||
- 仍提示登录:确认实际读取的用户配置目录、选中的 Provider 和客户端版本,再完全退出重启。
|
||
新 Provider 使用 `requires_openai_auth = false`,不依赖本地 ChatGPT 登录
|
||
- 没有生图工具:核对 Codex 实际读取的 Provider 配置,确认 Actor 标记、`requires_openai_auth = false` 和图片输入能力,
|
||
不只检查导出模板;CC Switch 导入不包含生图设置。客户端缓存的本地 Free 账号也可能影响工具展示。
|
||
先备份并区分本地真实账号文件与代理密钥文件,不要直接删除全部登录状态
|
||
- 已调用生图但失败:查看服务端账号的凭据、权限、额度和请求错误,不能只凭文本对话成功判断
|
||
- `401`:检查代理密钥是否正确、是否启用;Actor 标记不能代替密钥
|
||
- `400` 且错误码为 `client_version_too_old` 或 `client_version_unavailable`:检查客户端版本及反向代理是否保留版本头,
|
||
低于管理员设置的最低版本时需要更新客户端
|
||
手机远程控制的版本识别范围见 [版本门禁规则](../docs/api.md#1-鉴权与公共约定)
|
||
- 地址包含 `5173/dev/v1`:这是开发代理地址,依赖 Vite 服务。日常使用改成后端或 HTTPS 地址;
|
||
验证 WebSocket 时直接连接后端,当前 Vite 代理未显式开启 WebSocket 转发
|
||
|
||
其他客户端使用 Responses API、`/v1` Base URL 和代理密钥即可,不需要 Actor 标记。
|
||
路由与请求格式见 [API 参考](../docs/api.md#3-openai-数据面与模型目录)
|
||
|
||
## 停止与重启
|
||
|
||
管理端重启、SIGTERM 和 SIGINT 使用同一关闭策略:停止接收新连接,不等待现有 HTTP、SSE、
|
||
WebSocket 会话完成,进程退出时强制断开剩余连接。已入队的观测写入与后台清理保留
|
||
`host.worker_shutdown_timeout_seconds`(默认 30 秒)的收尾预算;客户端在新进程就绪后重新连接
|
||
|
||
管理端重启受理后,到进程退出期间,更新、回滚和再次重启均被拒绝;可继续查询更新状态
|
||
|
||
Compose 的 `stop_grace_period` 为 75 秒,覆盖默认 30 秒后台收尾及进程清理余量。
|
||
调大后台关闭预算时,也必须同步预留容器停止宽限期,否则 Docker 会在宽限期结束时 SIGKILL
|
||
|
||
## 本地开发
|
||
|
||
源码环境、前后端启动与组件库联调见 [开发与源码联调](../docs/development.md)
|
||
|
||
本机开发可使用 Compose 提供 PostgreSQL 和 Redis,集成测试另用[专用测试库](../backend/migrations/README.md#本地测试库)
|
||
|
||
## 持久化与备份
|
||
|
||
### 数据目录
|
||
|
||
Compose 使用以下绑定目录:
|
||
|
||
| 目录 | 内容 |
|
||
| --- | --- |
|
||
| `.runtime/data` | OpenAI 会话锚点密钥、更新状态、临时更新目录与备份暂存区 |
|
||
| `.runtime/logs` | 应用文件日志、按需启用的 OAuth 恢复记录与请求转储 |
|
||
| `.runtime/postgres` | PostgreSQL 持久化数据 |
|
||
| `.runtime/redis` | Redis AOF |
|
||
|
||
普通 `docker compose down` 不会删除这些目录。删除 `.runtime` 会永久清除本地状态
|
||
|
||
应用容器以 `10001:10001` 运行,`.runtime/data` 与 `.runtime/logs` 的顶层目录使用 `0770` 权限和 `10001` 属组。
|
||
安装脚本对这两个目录仅设置顶层所有权与权限,内部文件及私有目录保留应用创建时的所有权与权限
|
||
|
||
PostgreSQL 是账号、Client Key、运行设置、请求记录与审计的权威存储;账号 credential 按
|
||
Provider schema 以明文 JSON 保存于 PostgreSQL。Redis 只保存可重建、可过期的协调状态,例如
|
||
会话亲和、lease、cooldown、OAuth pending flow 与套餐模型目录 cache
|
||
|
||
备份应覆盖哪些目录、如何生成一致性数据库归档,见 [备份与恢复](#备份与恢复)
|
||
|
||
### 日志与保留窗口
|
||
|
||
文件日志以时间完整性为清理依据,**没有文件数量淘汰上限**:
|
||
|
||
| 文件集 | 保留配置 | 默认完整窗口 |
|
||
| --- | --- | --- |
|
||
| 普通日志、OAuth 恢复日志 | `host.logging.file.retention_days` | 前 7 个完整自然日及当天 |
|
||
| 全量请求/响应报文 | `host.logging.request_dump_retention_days` | 最近 24 小时 |
|
||
|
||
文件名统一为 `codex-proxy-rs-<类别>.YYYY-MM-DD[.N].log[.gz]`,类别分别为
|
||
`application`、`oauth-recovery`、`request-dump`。专用 tracing target 为 `oauth_recovery` 和
|
||
`request_dump`;普通日志保留各 Rust 模块的 target,便于按模块过滤。
|
||
程序只管理上述规范名称的日志,其他命名的文件由运维手动清理。
|
||
|
||
日志时间戳采用部署时区并携带数字偏移。普通日志与 OAuth 恢复日志按该时区的日期轮转和整组保留:例如 9 月 8 日配置 1 天,会保留 9 月 7 日全天及 9 月 8 日的所有分片,
|
||
到 9 月 9 日才允许清理 9 月 7 日,避免留下半天日志。夏令时日期允许为 23 或 25 小时。切换时区不重命名历史文件;
|
||
按文件名日期与部署时区中的实际修改日期取较近值,旧日期分片近期被写入时整组延后删除。
|
||
|
||
请求转储保留期支持正小数,按每天 24 小时换算,例如 `0.5` 为 12 小时、`0.25` 为 6 小时。
|
||
已关闭分片以最后修改时间判断过期,整片删除,不受时区或夏令时影响。
|
||
时间轮转间隔取一小时与保留期中的较小值,在下一条记录写入时触发。
|
||
实际保存时间可能长于配置值,取决于分片的最后写入时间和下次清理时机
|
||
|
||
`max_file_size_mb` 仅决定分片大小(默认 20 MiB),不决定保存时长,单条大记录不会被截断。
|
||
关闭的分片压缩为 `.log.gz`;成功压缩、同步并发布归档后才删除原文件,保留原修改时间。
|
||
清理发生在启动和轮转时;空闲期间过期文件可能暂时多保留。请求转储重启时新建分片,避免反复追加延后旧记录的过期点。
|
||
检索时须同时读取 `.log` 与 `.log.gz`
|
||
|
||
报文开关为 `host.logging.request_dump`,默认关闭;开启时原始报文按块写入独立文件,
|
||
缓冲拒收或写入失败可能造成缺块,需结合 `file_logging` 健康状态判断完整性。
|
||
数据库请求诊断仍是有界摘要,不能用摘要事件数代替全量报文完整性
|
||
|
||
OAuth 恢复开关为 `host.logging.oauth_recovery`,默认关闭,与普通文件日志开关分别控制:
|
||
|
||
- 开启后,OpenAI 每次成功取得 AT 和可用 RT 时,在资料补全及数据库写入前写入独立的 `oauth-recovery` 文件集
|
||
- 该通道不受普通日志级别过滤,关闭时也不会改写到普通日志或 stdout;接收端不等待后台写入
|
||
|
||
恢复记录和请求转储含原始凭据,`.runtime/logs` 须按敏感数据控制访问和备份
|
||
|
||
每个文件输出使用非阻塞队列,普通额度为 4096 条 / 64 MiB,WARN / ERROR 另有 512 条 / 8 MiB 预留,
|
||
额度包含正在写入的记录。后台批量缓冲写文件,正常退出会排空已接收的队列并同步。
|
||
持续拥堵、超大记录或输出关闭导致的拒收会损失整条记录,所有接口均不会为日志等待;
|
||
`file_logging` 健康探针在拒收或写入/同步失败后报告 `Unhealthy`,
|
||
本次进程内恢复写入也不会清除已有缺口;压缩或清理失败报告 `Degraded`。
|
||
断电、强杀或磁盘故障可能造成日志丢失;需要恢复已删除文件时,应使用独立备份。
|
||
容量不足时不会提前删除保留窗口内日志,必须根据完整日期的压缩后实际用量规划空间,并监控磁盘余量和
|
||
健康探针。Docker stdout 的独立轮转不承担应用文件日志的完整保留承诺
|
||
|
||
完整运行时、Provider、revision 与恢复边界见 [架构文档](../docs/architecture.md)
|
||
|
||
## 请求错误排查
|
||
|
||
1. 先记录故障时间和时区、网关版本/提交、客户端名称与版本,以及 HTTP/SSE/WebSocket 传输。
|
||
区分“上游返回”“网关实际响应”和“客户端终端展示”,不要只凭终端的统一文案推断根因
|
||
2. 收集响应中的 `x-gateway-request-id`、`x-request-id` / `x-oai-request-id`;配置了
|
||
`api.request_id_header` 时也记录该入口头。WebSocket 合成错误的关联头位于本条错误的 `headers`。
|
||
在管理端错误列表按 ID 和时间搜索;检查平台条件并主动刷新,翻页不会推进查询时间。
|
||
只有入口 ID 时,在运维错误中按该 ID 搜索,并结合入口日志和时间定位
|
||
3. 打开错误详情,核对上游/客户端状态、发送状态、attempt、失败阶段及后续恢复关联。
|
||
下载诊断包作为反馈材料,先检查 `availability.gaps`、`exportTruncated` 以及尝试和时间线的完整性。
|
||
该包包含经服务端脱敏的已存上游错误与时间线,分享前仍应检查业务内容及内部信息
|
||
4. 需要更细上下文时,在 `codex-proxy-rs-application.*.log` 及 `.log.gz` 中按网关/上游 ID
|
||
和时间检索,结合 `attempt.started`、`attempt.failed`、`request.finished` 与 transport 阶段判断。
|
||
**开启 `host.logging.file.enabled` 时,stdout 仅保留 `gateway_startup` 通道**;
|
||
`docker logs` 看不到业务错误不代表没有错误。关闭普通文件日志且开启 `host.logging.stdout`
|
||
时,普通日志才按级别输出到 stdout;专用 dump/OAuth 恢复通道不会混入
|
||
5. 查不到请求记录时,先用模型执行 ID 在“全部平台”下搜索:已进入执行会话的建流前失败也会记录,
|
||
但其 attempt 为零,尚未确认的 Provider/账号/传输为空,不会命中具体平台或账号筛选。
|
||
鉴权、解析、未知路由、方法错误和执行前中间件失败记录为 `api/http_ingress` 运维事件,
|
||
路由、权限、取消和准入拒绝由执行层记录;它们没有模型请求或用量,不应按 Provider/账号筛选。
|
||
再检查观测队列丢弃/写入失败告警及 `file_logging` 健康状态。异步投影有延迟,
|
||
刷新后仍需核对缺口,而不是反复重放可能已发送的请求
|
||
6. 仅在上述信息不足且能够控制访问范围时临时开启 `host.logging.request_dump` 复现一次。
|
||
`ingress.request`、`ingress.request.headers` 与 `ingress.request.body` 按入口 ID 关联原始请求,
|
||
`ingress.response` 与对应 headers/body 记录响应;`ingress.body.finished` 的 `complete: false`
|
||
表示正文未读完或读取失败,不能当作完整转储。网关不会为日志读取尚未消费的正文。
|
||
转储含凭据与用户正文,应限定访问、摘取最小片段并人工脱敏;复现后关闭开关,
|
||
按配置的保留窗口及组织的数据处理要求管理已生成文件。不要为普通请求排查开启 OAuth 恢复记录,
|
||
也不要直接上传整个日志目录或完整转储
|
||
|
||
请求问题反馈使用 [接口问题反馈表单](../.github/ISSUE_TEMPLATE/api-bug-report.yml);错误诊断与查询合同见
|
||
[API 文档](../docs/api.md#10-dashboard用量与错误)
|
||
|
||
### 插件取文与 DNS
|
||
|
||
插件受管 HTTP 由实际运行网关的主机或容器解析 DNS,再通过直连或账号代理访问目标。插件拥有完整网络访问能力,宿主不按插件身份限制私网、回环或保留地址
|
||
|
||
域名解析失败时先核对网关运行环境的 DNS。Clash / Mihomo 的 Fake-IP 地址需要相应代理接管;无法连通时可让目标域名返回真实地址,并检查所选代理。代理失败不会自动回退直连
|
||
|
||
排障区分域名解析失败、超时、连接失败和响应读取失败,避免在日志或截图中输出真实请求头、正文及凭据
|
||
|
||
## 密码语义
|
||
|
||
- `admin.default_password` 只在首次创建管理员时使用
|
||
- 已有管理员在「系统设置 → 安全与访问 → 管理员密码」修改登录密码,需要验证当前密码;修改后所有管理员会话失效,使用新密码重新登录
|
||
- PostgreSQL 官方镜像只在空数据目录初始化时使用 `database.password`
|
||
- Redis 在每次容器创建时使用 `redis.password`
|
||
|
||
已有 PostgreSQL 数据目录后,直接修改 `database.password` 不会修改数据库用户密码,只会导致
|
||
应用无法连接。轮换时必须先在 PostgreSQL 中修改用户密码,再同步更新 `config.yaml`。Redis
|
||
密码变更后需要用新配置重新创建 Redis 和应用容器,不需要删除 Redis 数据目录。
|
||
安排维护窗口,避免应用和 Redis 在过渡期间使用不同密码
|
||
|
||
## 镜像升级与源码构建
|
||
|
||
每个 Release 独立提供 `config.example.yaml`、默认镜像固定到该版本的 `compose.yaml` 和校验和;
|
||
各平台归档也包含 `deploy/config.example.yaml`。配置模板来自构建该版本的同一提交。
|
||
使用二进制归档手动部署时,将模板中的 `api.asset_directory` 改为 `../web/dist`,指向归档内的静态资源。
|
||
在线更新默认使用同一目录;如显式设置 `host.system_update.web_dist_dir`,应确保它指向实际提供页面的目录。
|
||
升级时先阅读目标版本说明,下载同一 Release 的部署附件,对比模板并合并必要配置,保留已有凭据
|
||
和 Compose 自定义项。不要用模板覆盖 `config.yaml`,也不要从 `main` 下载模板搭配旧镜像
|
||
|
||
更新部署文件后,从安装目录拉取目标版本镜像并重建应用容器:
|
||
|
||
```bash
|
||
docker compose -f deploy/compose.yaml pull codex-proxy-rs
|
||
docker compose -f deploy/compose.yaml up -d --no-build --wait codex-proxy-rs
|
||
```
|
||
|
||
源码构建需要克隆源码仓库并准备配置与数据目录,构建过程自动准备前端依赖,无需初始化子模块或准备额外 build context。以下命令从仓库根目录执行:
|
||
|
||
```bash
|
||
docker compose -f deploy/compose.yaml build codex-proxy-rs
|
||
docker compose -f deploy/compose.yaml up -d --no-build --wait
|
||
```
|
||
|
||
升级后通过管理端版本接口或容器 image digest 确认运行实例的版本和 revision
|
||
|
||
构建元数据通过一次性进程环境传入:
|
||
|
||
```bash
|
||
CPR_VERSION="$(ruby -ryaml -e 'puts YAML.load_file("release/version.yaml").fetch("version").delete_prefix("v")')" \
|
||
CPR_GIT_SHA="$(git rev-parse HEAD)" \
|
||
CPR_BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||
docker compose -f deploy/compose.yaml build codex-proxy-rs
|
||
```
|
||
|
||
### 账号名称迁移
|
||
|
||
OAuth 账号仍使用默认授权名称(如「Codex 账号」「OpenAI 账号」「xAI 账号」)且保存了邮箱时,
|
||
升级会清空默认名称,账号列表按邮箱前缀和完整邮箱分两行展示。
|
||
自定义名称、已有改名审计记录的账号、无邮箱账号及 API Key 名称保留原值
|
||
|
||
### 错误保护配置升级
|
||
|
||
使用 `accountAutoFreeze*` 运行参数的部署升级时,数据库迁移删除全部旧保护设置,不转换规则或继承参数。
|
||
`accountErrorProtection` 与新安装使用相同默认值:关闭保护、规则为空,其余参数见[错误保护 API](../docs/api.md#账号错误保护)。
|
||
需要保护时,在「系统设置 → 运行设置 → 错误保护」重新配置规则并启用,
|
||
使用管理 API 的客户端须同步改用[新的设置对象](../docs/api.md#账号错误保护),旧字段写入会被拒绝
|
||
|
||
已有活动冻结保留当前冷却截止时间,保护关闭时到期解除,未冻结的旧计数不沿用。通用错误保护仅暂停账号调度,不再自动下调并发;
|
||
已经保存的账号并发上限保持原值,需要恢复时在账号设置中手动调整
|
||
|
||
### 版本命名与升级规则
|
||
|
||
管理端「系统更新」可临时选择通道检查更新,不保存选择,不会自动下载或安装。
|
||
每次打开弹窗按当前运行版本匹配通道:正式版为 Stable,预发行按版本后缀匹配。
|
||
升级并重启后按新运行版本匹配;侧栏更新提示始终使用当前运行通道。预发行编号 `N` 从 1 开始递增
|
||
|
||
| 通道 | 命名示例 | 接收的版本 |
|
||
| --- | --- | --- |
|
||
| Stable | `X.Y.Z` | 正式版 |
|
||
| RC | `X.Y.Z-rc.N` | RC、正式版 |
|
||
| Beta | `X.Y.Z-beta.N` | Beta、RC、正式版 |
|
||
| Alpha | `X.Y.Z-alpha.N` | Alpha、Beta、RC、正式版 |
|
||
| Exp | `X.Y.Z-exp.N` | 同一轮实验中编号更高的 exp,仅实验实例可选 |
|
||
|
||
普通通道在同一大版本内持续接收更高版本,包括后续小版本和补丁版本的预发行。
|
||
切换通道不会降级,Stable 候选也必须高于当前运行版本。
|
||
下载、安装或待重启期间不能切换通道
|
||
|
||
同一 `X.Y.Z-exp.N` 系列只用于同一轮实验,不能混用不同实验分支;变更基线即进入另一实验线,需手动迁移。
|
||
exp 不进入正式版、alpha、beta 或 rc,普通实例也不能选择 Exp。
|
||
从 exp 切换正式版时需先备份,在目标版本的独立数据库中迁移业务数据,不能直接复用或整库还原实验数据库
|
||
|
||
版本大小遵循 [SemVer](https://semver.org/),构建元数据 `+...` 不参与比较。未知预发行标识不提供在线更新。
|
||
更新器先过滤不允许的目标,再选择版本最高的候选;其他通道或更高大版本领先,不影响当前发行线的更新。
|
||
只有当前官方构建满足在线更新条件且存在允许安装的新版本时,才返回“有可用更新”。否则不显示更新标记、
|
||
其他通道的版本及发布说明,手动检查显示“当前没有可用更新”。检查失败单独报告,不能当作没有更新
|
||
|
||
alpha、beta、rc、exp 在 GitHub 标记为 Pre-release,不覆盖 GitHub Latest 或镜像 `latest`。
|
||
升级前核对目标版本的数据库要求;可连续升级的发行线与约束见[迁移冻结规则](../backend/migrations/README.md#冻结规则)
|
||
|
||
### 管理端在线更新
|
||
|
||
在线更新要求官方构建且起始版本不低于 **v3.18.1**,版本选择遵循上述规则;源码等非官方构建返回不支持原因,不提供可用更新。
|
||
不满足在线更新条件时,使用完整发行包或镜像[手动部署](#镜像升级与源码构建)。
|
||
点击「下载并更新」后,操作日志显示下载、校验与安装过程,完成后显示「待重启」及待生效版本,重启后才使用新程序。
|
||
「当前版本」始终表示正在运行的版本,「通道最新」表示所选通道的检查结果
|
||
|
||
手动更换镜像或程序后,更新器会核对安装文件并校准历史状态,不以旧成功记录推断需要重启。
|
||
运行期间检测到外部文件变动或文件缺失时会报告异常,需要核对完整发行包并重启服务后再检查。
|
||
回滚入口只接受与记录指纹一致的完整备份;缺少指纹,或备份被替换、丢失时,不提供在线回滚,
|
||
但不会删除备份文件。此时需要按人工部署流程恢复已经核实的发行包
|
||
|
||
直接运行二进制时,若未设置 `CPR_ENABLE_SELF_RESTART=true`,管理端自重启默认关闭。
|
||
在现有 `deploy/config.yaml` 的 `host` 下合并以下配置:
|
||
|
||
```yaml
|
||
host:
|
||
system_update:
|
||
deployment_mode: binary
|
||
self_restart_enabled: true
|
||
executable_path: '/opt/codex-proxy-rs/codex-proxy-rs'
|
||
```
|
||
|
||
`executable_path` 为可选项:未指定时,服务在启动时解析并固定当前程序路径;
|
||
显式配置时,将示例改为实际程序的绝对路径。`self_restart_enabled` 控制管理端的「立即重启」操作;
|
||
修改配置后需手动重启当前服务一次才会生效。
|
||
以上三个字段也可分别通过 `CPR_DEPLOYMENT_MODE`、`CPR_ENABLE_SELF_RESTART` 和 `CPR_UPDATE_EXE_PATH` 设置,
|
||
YAML 中显式配置的值优先于对应环境变量
|
||
|
||
Compose 提供以下在线更新运行参数:
|
||
|
||
- `CPR_UPDATE_REPOSITORY`:只接受 `owner/repository`;默认 `zyycn/codex-proxy-rs`
|
||
- `CPR_GITHUB_API_BASE`:正式环境必须为 `https://api.github.com/repos`
|
||
- `CPR_UPDATE_EXE_PATH`、`CPR_WEB_DIST_DIR`:分别指向容器内二进制和前端静态目录;
|
||
`CPR_WEB_DIST_DIR` 同时供页面服务与更新器使用,相对路径以 `deploy/config.yaml` 所在目录为基准
|
||
- 更新临时目录、状态文件和锁文件默认由 `host.runtime_data_dir` 派生;
|
||
`CPR_UPDATE_TEMP_DIR`、`CPR_UPDATE_STATE_FILE`、`CPR_UPDATE_LOCK_FILE` 仅用于显式覆盖
|
||
- `CPR_ENABLE_SELF_RESTART=true`:更新或回滚完成后允许管理端请求重启;Docker 进程退出后由
|
||
Compose 的 `restart: unless-stopped` 拉起新进程
|
||
|
||
管理端自重启遵循统一的[停止与重启](#停止与重启)策略,客户端需在新进程完成迁移与初始化后重新连接
|
||
|
||
Release 必须提供当前 OS/架构的 `codex-proxy-rs_<version>_<os>_<arch>.tar.gz` 与
|
||
`checksums.txt`。服务会在替换前再次查询远端最新版本,校验下载 host、声明大小、SHA-256 和
|
||
归档路径;二进制或静态资源任一替换失败时恢复旧文件。成功后的旧二进制和旧静态目录分别保留为
|
||
`*.backup`,管理端 rollback 会交换当前文件与这份备份。更新状态和跨进程锁可在以下位置排查:
|
||
|
||
```text
|
||
.runtime/data/update-state.json
|
||
.runtime/data/update.lock
|
||
.runtime/data/update-tmp/
|
||
```
|
||
|
||
### 插件兼容与发行目录
|
||
|
||
教学示例在 `codex-proxy-plugins` 独立构建和发布,通过[插件管理](../docs/plugins.md)安装。
|
||
宿主发行物不内置插件,包含用于兼容检查的封口清单
|
||
|
||
| 文件或目录 | 用途 |
|
||
| --- | --- |
|
||
| `plugin-release-manifest.json` | 固定宿主版本、提交、平台与支持的插件合同;插件列表当前为空 |
|
||
| 可执行文件同目录的 `plugins/official` | 随受信宿主发行物部署的只读目录,与二进制、Web 资源成套更新或恢复 |
|
||
|
||
- 下载更新时校验发行身份,插件合同不兼容不阻止安装。点击重启时检查已安装目标版本,列出不兼容插件的名称和原因;确认后重启,不兼容实例保持未启动,启用配置、绑定、密钥和私有数据保留
|
||
- 回滚同样在重启前提示插件兼容风险,启动时重新检查,不改写持久启用配置。更新或回滚期间的并发配置变更会阻止切换,文件替换失败或取消时恢复整组文件
|
||
- 非空官方清单中的包经身份、平台和摘要校验后幂等导入;导入不确认信任、不创建配置、不启用,也不删除旧包。管理员确认信任后才执行默认配置流程;重复摘要保留首次安装出处
|
||
- `sealed` 是构建封口标记,不是密码学签名。普通上传、URL 或 GitHub 安装不能获得 `builtin` 身份,独立教学示例也不例外
|
||
|
||
宿主启动检查覆盖管理页更新、Docker 与手工替换二进制。不兼容实例不挂载任何能力或故障占位,其额外认证和拦截停止生效,宿主原生认证继续执行。
|
||
宿主范围或能力版本不匹配时,可在插件页确认风险后尝试本次启动;清单、RPC、平台或包完整性错误不能绕过。具体操作见[插件故障处理](../docs/plugins.md#故障处理)
|
||
|
||
部署须保留完整的二进制、Web 资源与官方插件目录;不要手动修改制品元数据或迁移 checksum 绕过校验。
|
||
macOS arm64、Windows x64 / ARM64 提供构建产物;插件安装包必须匹配宿主 OS 和架构,部署前需验证目标平台的插件运行与上游调用,不能只以打包成功判断可用
|
||
|
||
## 备份与恢复
|
||
|
||
管理端 S3/R2 提供业务备份;需要完整请求历史时,另做 PostgreSQL 整库备份或基础备份与 WAL 归档
|
||
|
||
不要在 PostgreSQL 写入期间直接复制数据目录作为一致性备份
|
||
|
||
| 需要保留的内容 | 备份方式 |
|
||
| --- | --- |
|
||
| 账号、Key、设置、插件、已用额度与用量汇总 | 管理端 S3/R2 业务备份 |
|
||
| 完整请求、诊断和审计历史 | 独立整库备份,或停库后备份 `.runtime/postgres` |
|
||
| 部署配置与凭据 | 单独备份 `deploy/config.yaml` 及 Compose 自定义配置 |
|
||
| 应用日志与已启用的 OAuth 恢复记录 | 单独备份 `.runtime/logs`,不包含在数据库归档中 |
|
||
| 会话锚点与节点运行文件 | 按需备份 `.runtime/data` |
|
||
| 短期协调状态 | 按需备份 `.runtime/redis`,数据库冷恢复时使用空 Redis |
|
||
|
||
OpenAI 主动额度重置卡及消费结果由上游持有,不属于本地备份内容
|
||
|
||
数据库、配置、插件敏感设置和 OAuth 恢复日志均按凭据保护
|
||
|
||
### 备份内容与限制
|
||
|
||
业务归档为 `.sql.gz`,保留完整数据库结构和迁移记录,但不包含请求明细、诊断与审计日志、
|
||
逐请求费用去重记录和可重建的统计明细。Key 当前日、周的已用额度独立保留
|
||
|
||
原生用量按 UTC 小时保留全局请求数、成功/失败数、Token 和实际费用,每小时至多一行;
|
||
插件维护的用量贡献按其原维度保留。原生汇总只覆盖备份快照中已发布且尚未被历史清理撤销的统计,
|
||
不是永久累计账本,异步投影尚未处理的请求不计入。统计重建期间备份失败,统计就绪后可重试
|
||
|
||
恢复后的原生历史可用于全局总量与小时趋势,不支持请求详情、按 Key/账号等维度筛选或历史延迟分布;
|
||
小时内不完整时间范围无法还原到请求精度。若需这些能力,应使用整库灾备
|
||
|
||
- 插件包体、固定来源、下载凭据、制品接受事实、实例配置、版本配置快照、敏感配置、功能范围绑定和私有状态都保存在
|
||
PostgreSQL,随数据库一起备份。插件通过宿主回调读写的内置平台账号保存在同库的通用账号表。
|
||
来源使用的出站代理及其认证也须随库恢复,不能只备份 `plugin_*` 表;来源代理与账号代理独立选择,
|
||
不依赖进程代理环境,故障时不会自动回退直连。
|
||
包体单个上限为 32 MiB,需要计入数据库及备份容量;解压运行目录和 Redis 协调状态是可重建数据,
|
||
不作为插件安装或制品接受事实。恢复时保留制品接受状态、实例配置、账号引用和状态代次的一致性;插件降级仍须
|
||
通过状态兼容校验,不通过手工替换缓存目录回滚
|
||
- 官方运行镜像内置与 Compose 数据库服务版本一致的 `pg_dump`,无需额外安装。
|
||
自行替换 PostgreSQL 版本时,应同步核对备份工具版本
|
||
- 备份暂存目录为 `host.runtime_data_dir/backup-staging`;Compose 默认对应
|
||
`/app/.runtime/data/backup-staging`,由 `.runtime/data` 卷持久化,权限 `0700`(仅 `cpr`
|
||
用户可读写)。归档流式压缩,只暂存一份压缩文件;部署卷至少预留一个最大业务归档的空间
|
||
- S3/R2 存储、Cron 计划、保留策略与备份记录都保存在 PostgreSQL,备份记录在删除成功后移除。
|
||
人工创建和删除操作写入管理审计,自动保留清理不生成管理员操作审计
|
||
- 手工备份的 `expiresInDays` 在创建时生成独立 `expires_at`;计划备份按当前 `retentionDays` 生成
|
||
`expires_at`,并同时受 `retentionCount` 清理规则约束。到期只进入删除流程,不构成在线恢复点
|
||
|
||
### 人工恢复数据库
|
||
|
||
业务归档是 gzip 压缩的 SQL,使用与数据库版本匹配的 `psql` 恢复到空目标库:
|
||
|
||
```bash
|
||
set -o pipefail
|
||
gzip -dc backup.sql.gz | psql -X --single-transaction --set=ON_ERROR_STOP=1 --password \
|
||
--dbname='postgresql://restore_user@127.0.0.1:5432/restore_db'
|
||
```
|
||
|
||
示例中的账号、空目标库和归档路径需按实际情况替换,密码由命令交互输入。
|
||
先核对文件 SHA-256 与备份记录一致,并执行 `gzip -t backup.sql.gz` 校验归档完整性
|
||
|
||
恢复期间与恢复后的边界见 [架构文档](../docs/architecture.md#11-生命周期安全与恢复)。
|
||
当前没有在线恢复 API,也没有供部署者直接启用的“维护模式”开关:
|
||
|
||
1. 先备份现有数据库,并停止应用;在独立的空数据库中恢复归档,检查数据和迁移版本
|
||
2. 应用保持停止,离线核对备份设置与记录。禁用快照中的旧计划,处理非终态记录
|
||
(`queued/dumping/uploading/deleting`)、旧调度游标和到期清理条件,再决定哪些记录保留
|
||
3. 使用空 Redis 和空插件解包缓存做一次冷启动;逐一核对已启用实例的包摘要/平台、制品接受状态、配置与绑定、账号引用、
|
||
私有状态 schema/代次,并执行实际插件操作。缺少或损坏必需包体、引用不完整或状态不兼容时,恢复不能
|
||
记为成功,也不能用相同插件 ID 的其他包替代
|
||
4. 确认不会误执行旧任务或删除恢复前的远端对象后,再接入业务流量;重新测试 S3 连接并设置计划
|
||
|
||
只关闭计划备份不会停止 Worker 的任务恢复和到期删除。无法确认这些记录的影响时,
|
||
不要把恢复后的数据库直接接入运行中的应用。具体处理应根据目标库数据制定,不提供清空生产记录的通用命令
|