# 新主机入网清单(HA 阶段 4) 新机加入前,老机继续全量服务;新机全部验证通过后才切流量。 清单里的路径与实测值以 2026-10-26 主机为准,变了同步改。 ## 1. 系统与用户 - Ubuntu 24.04、Python 3.12、git、Docker(与老机同大版本)、 Node 18(装 codex 用)。 - 建 `codexsvc`(uid 1003、gid 1004)并加入 `docker` 组。 - NTP:`timedatectl` 确认 `NTPSynchronized=yes`(心跳、超时、 sweep 全依赖时钟,不同步会出现“活任务被误扫”)。 ## 2. 代码与目录 - `/opt/codex-app-server`:部署 tar 解包,`chown -R codexsvc`。 - `/var/lib/codex-app-server/`:建 `tenants/`、`repos/`、 `codex-home/`(模板)、`backups/`(700)。 - `/etc/codex-app-server/env`(0600 root):与老机一致,尤其 `CLOUD_DB_PASSWORD`(新卷 Postgres 的初始化口令必须等于它, 见 `postgres-rebuild.md` 场景 B)、relay key、GitHub OAuth secret、webhook secret。密钥绝不进仓库、不进部署 tar。 ## 3. 数据库 - 有现成 Postgres 时:env 里改 `CLOUD_DB_HOST/PORT` 指向它。 - 没有时:按 `postgres-rebuild.md` 场景 B 重建(新卷 + 最新备份 恢复 + 四表校验),再起 server。 ## 4. 服务 units(都在 `deploy/` 里有参考文件,unit 文件不在 ## deploy tar 里,须单独 scp 到 `/etc/systemd/system/` 再 ## daemon-reload + enable + start) - `codex-cloud-server`(8765)与 `codex-cloud-server-2`(8766) 双机;`codex-autoscaler`(min 1 / max 2、per-worker capacity 2、 `--docker-runtime runsc`);`codex-host-worker`(local, `--claim-executor-only`,经 `aa-exec -p codex-host` 跑); `codex-pg-backup.timer`(每天 03:00 CST); `monitor.timer`(每 5 分钟)。 - 注意:`codex-cloud-worker` 已停用(autoscaler 接管),绝不能 与 autoscaler 同时跑。 ## 5. 沙箱与隔离 - AppArmor:加载 `docker-codex` 与 `codex-host` 两个 profile (`deploy/apparmor/` 下);`codex-host` 的坑已修过(capability 放行只是 userns 内假 root;`network netlink raw` 必须允许且 deny raw 须写明 inet/inet6;`/tmp` 不隔离,见 HA-DESIGN)。 - Docker:`daemon.json` 注册 runsc(`/usr/local/bin/runsc` + **必须成对**的 `/usr/local/bin/gvisor-bin/`,只拷 runsc 会报 sidecar 不可用);默认仍 runc,生产任务经 worker flag 走 runsc。 - 镜像:见第 7 节。 ## 6. 出口代理(每机一份,改动时同批下发,不要手工各改各的) - 拷 `/etc/tinyproxy/tinyproxy.conf` 与 `/etc/tinyproxy/tinyproxy-filter`(**644**,tinyproxy 用户可读; Filter 是 **POSIX BASIC regex**,extended 写法会把允许域名也 挡成 403;没有合法的 `Upstream none` 写法,直接连接就是默认)。 - 白名单只放行 xcmapi.org、github.com、pypi.org、npmjs.org 及子域;iptables 只放行 `172.16.0.0/12` 与 `127.0.0.1` (`iptables-save` 导出、到新机 `iptables-restore`)。 - Docker 任务容器出口走宿主 8888 代理(worker 的 `--egress-proxy-port 8888`)。 ## 7. 镜像传输(现阶段:docker save/load,不建无鉴权 registry) - 没有带鉴权 + TLS 的 registry 时,**不要**在本机跑无鉴权 registry(谁都能推恶意镜像,等于把 worker 的执行环境交给 任何人)。新机用 save/load 传镜像,流程已实弹验过: ```sh # 老机导出(919MB,注意磁盘余量) docker save codex-app-server:latest -o /tmp/codex-image.tar # 传到新机(scp 或 chunk_send,传完 sha256 比对) # 新机导入(幂等,已有同名镜像时只是重加已有层,不删东西) docker load -i /tmp/codex-image.tar docker images codex-app-server ``` - host 2 落地后,再评估带鉴权 + TLS 的 registry(或云厂商的 容器镜像服务),届时把“新机拉镜像”一步换成 `docker pull`。 ## 8. 入口(nginx) - 拷 vhost(含三个 codex location)与 `codex-upstream.conf` (upstream 块只能在 http 段,不能放进 server 块)。 - 宝塔重生成 vhost 会吃掉 location,备份在 `/etc/codex-app-server/` 与 vhost 目录的 `.pre-*` 文件里, 被吃时从备份贴回,再 `nginx -t` + reload。 - 双机 nginx(浮动 IP keepalived,同机房;或 DNS 轮询 + 健康 检查,跨机房)**需要第二台主机才能演练**,现阶段单机 nginx 仍是单点,见 HA-DESIGN 阶段 4。 ## 9. 入网验证(全部通过才切流量) 1. `monitor.sh` 全过(含 backup 项)。 2. 双 server health(8765、8766)均为 ok 且 db_ok。 3. autoscaler 正常(`--host-name` 已传宿主名,见 worker 进程 命令行),worker 注册表里有新机 worker。 4. 一个 Docker 任务端到端完成(worktree_host 应为新机主机名)。 5. `/pr` 同步路(本机 worktree)与队列路(改写 worktree_host 后排队、pr-runner 跑通)各一次。 6. 备份 timer 下次触发时间正常(`systemctl list-timers`)。 ## 10. 回滚 - 新机任何一步失败:停新机服务,老机不受影响(老机从未停过, 流量也从未切过)。 - 切流量后发现问题:nginx upstream 先摘掉新机端口(或把三个 location 的 proxy_pass 指回老机单端口),reload 即可回单机。