|
| 1 | +# codex2api 线上部署手册(Node2 · cx.wyzai.top) |
| 2 | + |
| 3 | +> 真值以 `docker ps` 为准,不信文档。文档滞后时以线上状态为准并反向更新本文。 |
| 4 | +
|
| 5 | +## 1. 线上状态(截至 2026-05-10) |
| 6 | + |
| 7 | +| 项 | 值 | |
| 8 | +|---|---| |
| 9 | +| 镜像 | `codex2api:v1.7.45-sse-keepalive`(`latest`)| |
| 10 | +| 容器 | `codex2api` | |
| 11 | +| 端口 | `8122`(nginx upstream 指向 127.0.0.1:8122)| |
| 12 | +| 部署目录 | `/data/codex2api/` | |
| 13 | +| Admin | https://cx.wyzai.top/admin/ secret = `65187777` | |
| 14 | +| 数据库 | PG `codex2api-postgres`, Redis `codex2api-redis`, 网络 `codex2api_codex2api-net` | |
| 15 | +| 图像卷 | `/data/codex2api-images:/app/images`(独立持久卷,蓝绿不会重建)| |
| 16 | +| nginx conf | `/www/server/panel/vhost/nginx/cx.wyzai.top.conf` | |
| 17 | +| nginx body 上限 | `client_max_body_size 500m` | |
| 18 | +| 本地 git HEAD | 见 `git log -1` | |
| 19 | + |
| 20 | +## 2. SSH 接入 |
| 21 | + |
| 22 | +```bash |
| 23 | +# Node2(cx.wyzai.top 落点) |
| 24 | +sshpass -p 'f3t7uCBeTCizT12' ssh -p 22222 -o StrictHostKeyChecking=no root@152.53.240.159 |
| 25 | + |
| 26 | +# 跳板(shell.wyzai.top 的 CF DNS 轮询:两条 A 记录) |
| 27 | +sshpass -p 'vCbeY8FXcSGw' ssh -p 18604 root@156.238.226.23 |
| 28 | +sshpass -p '2R18UapfDNoT' ssh -p 24598 root@156.238.226.55 |
| 29 | +``` |
| 30 | + |
| 31 | +## 3. 蓝绿 SOP(端口轮换) |
| 32 | + |
| 33 | +端口:`8120 → 8121 → 8122 → 8123 → 8120 …` |
| 34 | + |
| 35 | +**🚨 不要把多步串成一行 bash——stdout buffer 会让你误以为卡死。每步独立跑。** |
| 36 | + |
| 37 | +```bash |
| 38 | +SSH='sshpass -p f3t7uCBeTCizT12 ssh -p 22222 -o StrictHostKeyChecking=no root@152.53.240.159' |
| 39 | +SCP='sshpass -p f3t7uCBeTCizT12 scp -P 22222 -o StrictHostKeyChecking=no' |
| 40 | +OLD=8122; NEW=8123; TAG=v1.7.46-xxx |
| 41 | +``` |
| 42 | + |
| 43 | +### Step 1 · 本地 tar 打包(5-10s) |
| 44 | + |
| 45 | +```bash |
| 46 | +rm -f /tmp/codex2api-src.tar.gz |
| 47 | +time tar --exclude='.git' --exclude='node_modules' --exclude='frontend/dist' \ |
| 48 | + --exclude='data' --exclude='codex2api.db*' --exclude='logs' --exclude='*.log' \ |
| 49 | + --exclude='.DS_Store' \ |
| 50 | + -czf /tmp/codex2api-src.tar.gz -C /Users/yinghua/Documents/fly/codex2api . |
| 51 | +``` |
| 52 | + |
| 53 | +### Step 2 · scp 上传(30s 内) |
| 54 | + |
| 55 | +```bash |
| 56 | +time $SCP /tmp/codex2api-src.tar.gz root@152.53.240.159:/tmp/ |
| 57 | +``` |
| 58 | + |
| 59 | +### Step 3 · 解压 + 继承旧 .env |
| 60 | + |
| 61 | +```bash |
| 62 | +$SSH "rm -rf /data/codex2api-new && mkdir -p /data/codex2api-new && \ |
| 63 | + tar -xzf /tmp/codex2api-src.tar.gz -C /data/codex2api-new 2>&1 | grep -v 'Ignoring unknown extended header' | head -3 && \ |
| 64 | + cp /data/codex2api/.env /data/codex2api-new/.env && echo OK" |
| 65 | +``` |
| 66 | + |
| 67 | +### Step 4 · docker build(独立跑!增量 20s,全量 2 min) |
| 68 | + |
| 69 | +```bash |
| 70 | +$SSH "cd /data/codex2api-new && time docker build --build-arg BUILD_VERSION=$TAG -t codex2api:$TAG . 2>&1 | tail -10" |
| 71 | +``` |
| 72 | + |
| 73 | +### Step 5 · 启 codex2api-new |
| 74 | + |
| 75 | +```bash |
| 76 | +$SSH "docker rm -f codex2api-new 2>/dev/null || true; \ |
| 77 | + docker run -d --name codex2api-new \ |
| 78 | + --network codex2api_codex2api-net \ |
| 79 | + -p 127.0.0.1:$NEW:$NEW \ |
| 80 | + --env-file /data/codex2api-new/.env \ |
| 81 | + -e CODEX_PORT=$NEW \ |
| 82 | + -v /data/codex2api-new/logs:/app/logs \ |
| 83 | + -v /data/codex2api-images:/app/images \ |
| 84 | + --restart unless-stopped \ |
| 85 | + codex2api:$TAG && \ |
| 86 | + sleep 8 && curl -s http://127.0.0.1:$NEW/health && \ |
| 87 | + docker exec codex2api-new strings /usr/local/bin/codex2api | grep -oE 'v1\\.7\\.[0-9]+[a-z0-9-]*' | head -2" |
| 88 | +``` |
| 89 | + |
| 90 | +### Step 6 · nginx 切流量 |
| 91 | + |
| 92 | +```bash |
| 93 | +$SSH "cp /www/server/panel/vhost/nginx/cx.wyzai.top.conf{,.bak-\$(date +%Y%m%d-%H%M%S)} && \ |
| 94 | + sed -i 's|proxy_pass http://127.0.0.1:$OLD;|proxy_pass http://127.0.0.1:$NEW;|g' \ |
| 95 | + /www/server/panel/vhost/nginx/cx.wyzai.top.conf && \ |
| 96 | + nginx -t && nginx -s reload" |
| 97 | +curl -s https://cx.wyzai.top/health |
| 98 | +``` |
| 99 | + |
| 100 | +### Step 7-8 · 老容器 graceful 退出 + 固化 |
| 101 | + |
| 102 | +`docker stop -t 120` 必须:默认 10s 不够 `main.go` 的 90s shutdownTimeout。 |
| 103 | + |
| 104 | +```bash |
| 105 | +$SSH "sleep 30 && time docker stop -t 120 codex2api && \ |
| 106 | + docker logs codex2api --tail 30 2>&1 | grep -E '收到关闭信号|HTTP 存量请求|已关闭' ; \ |
| 107 | + docker rm codex2api && docker rename codex2api-new codex2api && \ |
| 108 | + docker tag codex2api:$TAG codex2api:latest && \ |
| 109 | + mv /data/codex2api /data/codex2api-old-\$(date +%Y%m%d-%H%M%S) && \ |
| 110 | + mv /data/codex2api-new /data/codex2api && \ |
| 111 | + sed -i 's/^CODEX_PORT=.*/CODEX_PORT=$NEW/' /data/codex2api/.env && \ |
| 112 | + docker ps --filter name=codex2api --format '{{.Names}} {{.Image}} {{.Status}}'" |
| 113 | +``` |
| 114 | + |
| 115 | +## 4. 关键提醒 |
| 116 | + |
| 117 | +- **每次必传 `--build-arg BUILD_VERSION`**:否则前端徽章显示 `dev` |
| 118 | +- **`docker stop -t 120` 必须**:默认 10s 会强 kill 长 SSE 连接 |
| 119 | +- **图像卷必须 `/data/codex2api-images`**:避免蓝绿 mv 时连带迁走 |
| 120 | +- **每次必跑 `docker tag latest`** |
| 121 | +- **取版本登 `docker ps`,不信 doc** |
| 122 | + |
| 123 | +## 5. 应急回滚 |
| 124 | + |
| 125 | +```bash |
| 126 | +$SSH "docker run -d --name codex2api-rollback \ |
| 127 | + --network codex2api_codex2api-net \ |
| 128 | + -p 127.0.0.1:<OLD_PORT>:<OLD_PORT> \ |
| 129 | + --env-file /data/codex2api-old-YYYYMMDD-HHMMSS/.env \ |
| 130 | + -v /data/codex2api-images:/app/images \ |
| 131 | + -v /data/codex2api-old-YYYYMMDD-HHMMSS/logs:/app/logs \ |
| 132 | + --restart unless-stopped \ |
| 133 | + codex2api:<OLD_TAG>" |
| 134 | +$SSH "sed -i 's|:<NEW_PORT>|:<OLD_PORT>|' /www/server/panel/vhost/nginx/cx.wyzai.top.conf && nginx -s reload" |
| 135 | +``` |
| 136 | + |
| 137 | +## 6. 常用运维 |
| 138 | + |
| 139 | +```bash |
| 140 | +# 健康 |
| 141 | +curl -s http://127.0.0.1:8122/health |
| 142 | +docker logs codex2api --tail 200 2>&1 | grep -vE '历史数据修复' |
| 143 | + |
| 144 | +# plan_type 实时同步日志(v1.7.29+) |
| 145 | +docker logs codex2api -f 2>&1 | grep '同步上游 plan_type' |
| 146 | + |
| 147 | +# AT-only active 账号 + 最早到期 |
| 148 | +docker exec codex2api-postgres psql -U codex2api -d codex2api -c \ |
| 149 | + "SELECT COUNT(*), MIN(NULLIF(credentials->>'expires_at','')::timestamptz) earliest \ |
| 150 | + FROM accounts WHERE COALESCE(credentials->>'refresh_token','')='' AND status='active'" |
| 151 | + |
| 152 | +# plan 分布 |
| 153 | +docker exec codex2api-postgres psql -U codex2api -d codex2api -c \ |
| 154 | + "SELECT COALESCE(NULLIF(credentials->>'plan_type',''),'(empty)') plan, \ |
| 155 | + COUNT(*) total, COUNT(*) FILTER (WHERE status='active') active \ |
| 156 | + FROM accounts GROUP BY 1 ORDER BY total DESC" |
| 157 | +``` |
| 158 | + |
| 159 | +## 7. 版本史 |
| 160 | + |
| 161 | +| 版本 | 端口 | 日期 | 关键改动 | |
| 162 | +|---|---|---|---| |
| 163 | +| v1.7.39-batch-add-3000 | 8120 | - | 批量添加 3000 账号 | |
| 164 | +| v1.7.40-ua-proxy | 8121 | - | UA 池 20→93 + 前端代理归一化 | |
| 165 | +| v1.7.41-dedupe | 8122 | - | 邮箱去重 | |
| 166 | +| v1.7.42-dual-fp | 8123 | - | 双轨 TLS + UA 双层保险 | |
| 167 | +| v1.7.43-free-55 | 8120 | - | free 账号承接 gpt-5.5 运行时开关 | |
| 168 | +| v1.7.44-unlock-banned | 8121 | - | 封禁 plus 账号自动解锁可清理 | |
| 169 | +| v1.7.45-sse-keepalive | 8122 | 2026-05-04 | SSE/JSON 双路径 keepalive 防 CF 100s idle | |
| 170 | +| **v1.7.46-upstream-may10** | **8123** | **2026-05-10** | **port 4 上游 commit:流式 usage 追踪 / 5h 紧迫性 / 工具参数剥离 / validation 扩充** | |
| 171 | + |
| 172 | +## 8. 不要再做的事 |
| 173 | + |
| 174 | +- ❌ **基于 cooldown 时长推断 plan**:v1.7.29 之前误伤过 plus 被 7d ban 的合法账号 |
| 175 | +- ❌ **id_token 解析回滚 plan_type**:用户拒绝,已废弃 |
| 176 | +- ❌ **多步骤 bash 命令串成一行**:stdout buffer 误判卡死 |
| 177 | +- ❌ **全局改 `/` 的 buffering**:会关掉 new-api UI 静态资源缓冲 |
| 178 | +- ❌ **删除跳板 `sub_filter`**:前端暗色主题依赖它 |
| 179 | +- ✅ **plan_type 校正的正道**:让 OpenAI 的 429 `error.plan_type` 自动同步(v1.7.29 已实现) |
| 180 | + |
| 181 | +## 9. 常量速查 |
| 182 | + |
| 183 | +| 常量 | 值 | 位置 | |
| 184 | +|---|---|---| |
| 185 | +| shutdownTimeout | 90s | `main.go` | |
| 186 | +| freeUsageRateLimitThresholdPct | 90.0 | `auth/store.go` | |
| 187 | +| team score_bias | +100 | v1.7.28+ | |
| 188 | +| plus/pro score_bias | +50 | - | |
| 189 | +| AT-only 过期窗口 | 5 min | - | |
| 190 | +| MaxRequestBodySize 默认 | 32 MB | `security/validator.go` | |
| 191 | +| CF idle timeout | 100s | Cloudflare 硬限 | |
| 192 | + |
| 193 | +## 10. 本次(v1.7.46)新增环境变量 |
| 194 | + |
| 195 | +```bash |
| 196 | +# /data/codex2api/.env 追加 |
| 197 | +CODEX_MAX_REQUEST_BODY_SIZE_MB=64 # 默认 32MB,调到 64MB 容纳长上下文 / 图片 |
| 198 | +``` |
0 commit comments