Ops batch: deploy automation, backup restore drill, monitoring, multi-user verification
Deploy: deploy.sh builds tarball, backs up current code (keep 3), deploys, restarts, health-gates 60s (ok/backend=postgres/db_ok), auto-rolls back on failure; rollback.sh restores latest backup. Rollback is code-only, DB migrations are additive-only so old code can still read new columns. Backup: restore_postgres refuses to restore into the production DB name unless explicitly allowed (drill never allows it); restore-drill.sh backs up fresh, restores to a test DB, compares tasks/projects/users/audit row counts, drops the test DB. Monitoring: worker writes heartbeat file every loop (atomic replace, failure only logged); GET /health adds worker_last_seen_seconds_ago + worker_ok (180s default, env tunable, still HTTP 200 when worker is stale); monitor.sh checks server/worker/health/pg_isready/docker-postgres/disk/ app dir as one JSON line, exit non-zero on any fail, plus systemd timer every 5 minutes. Push alerting to chat still needs a separate channel decision (SSH key or webhook URL). Multi-user: verify-multiuser.py creates test users/projects in memory-key only, checks RBAC via real HTTP API (owner/ maintainer/contributor/viewer, cross-project isolation, merge 200/403/409, no-key 401), test tasks carry a never- satisfiable dependency so the production worker never claims them, cleanup deletes only multiuser-test- rows (audit log is append-only and keeps test entries by design). Tests: host 27 pass, cloud 298 pass (8 skipped are environment-dependent real-Postgres tests).
This commit is contained in:
1 parent
6afef78ca8
commit
89a918b543
14 files changed
+1386
-2
No files matched your search
@@ -570,3 +570,59 @@ interrupted 且终态任务 cancel 返回 400、心跳后 sweep 不扫刚心跳
|
||||
任务仍需 worker 认领执行;`CloudExecutor` 仍是占位符;公网 https
|
||||
入口仍需部署侧反向代理 + TLS。
|
||||
|
||||
## 运维批(本轮新增:部署、恢复演练、监控、心跳、多用户验证)
|
||||
|
||||
细则在 `deploy/README.md`,这里只记代码侧的接口与默认值:
|
||||
|
||||
- 部署与回滚(`deploy/deploy.sh` / `deploy/rollback.sh`,VM 侧跑,
|
||||
不碰 `cloud/` 代码):部署前在主机上备份当前 `/opt/codex-app-server`
|
||||
到 `/opt/codex-app-server-backups/`(时间戳命名,只留最近 3 份,排除
|
||||
work/),解包新代码、chown codexsvc、重启 server+worker、循环验
|
||||
`localhost:8765/health` 最多 60 秒(要求 ok=true、backend=postgres、
|
||||
db_ok=true),失败自动恢复备份并重启;回滚只回代码,不回数据库
|
||||
(迁移只加列、幂等,老代码仍能读新列,见 deploy/README 的回滚边界)。
|
||||
两个脚本都不接受密码参数、不读 env 文件、不打印密钥;需经代理时用
|
||||
环境变量 `SSH_PROXY_CMD` 传入 ProxyCommand,未设则直连。
|
||||
- 备份恢复演练(`cloud.backup.restore_postgres` + `deploy/restore-drill.sh`,
|
||||
主机侧跑):`restore_postgres(backup_file, target_dbname, runner=None)`,
|
||||
用 psql 把 .sql 备份恢复到目标库;目标库名等于 `CLOUD_DB_NAME` 时
|
||||
直接拒绝(除非显式传 `allow_production=True`,演练脚本永远不设它);
|
||||
备份文件不存在或为空时报错;密码只经 PGPASSWORD 环境变量传,绝不进
|
||||
命令行、返回值或日志。演练脚本先跑一次 `backup_postgres` 生成新鲜
|
||||
备份,再恢复到 `codex_control_restore_test_<时间戳>` 测试库,比对
|
||||
tasks/projects/users/audit_log 四张表行数(只比行数,不打印行内容),
|
||||
比对完立即删测试库(trap 保证中途失败也删);绝不恢复到生产库,
|
||||
绝不删生产库。
|
||||
- 监控与心跳:worker 的 `run_loop` 每次循环结束时写心跳文件,内容只有
|
||||
`{"last_seen": epoch秒, "pid": 进程号}`,写失败只记日志不炸 worker;
|
||||
路径由 `--heartbeat-file` 指定,未指定时看环境变量
|
||||
`CLOUD_WORKER_HEARTBEAT_FILE`,再未指定则为 `./worker-heartbeat.json`
|
||||
(VM 本地默认,主机路径不在代码里硬编码;主机部署时两边指到同一个
|
||||
`/var/lib/codex-app-server/worker-heartbeat.json`,server 侧在 env 文件
|
||||
里加 `CLOUD_WORKER_HEARTBEAT_FILE` 一行,worker 单元加
|
||||
`--heartbeat-file` 参数,deploy.sh 不自动改 env 文件)。`GET /health`
|
||||
在原有 ok/service/backend/db_ok 之外加 `worker_last_seen_seconds_ago`
|
||||
(无文件或读失败时为 None)与 `worker_ok`(心跳 180 秒内为 true,
|
||||
阈值可经 `CLOUD_WORKER_HEARTBEAT_MAX_AGE_SECONDS` 调,默认 180);
|
||||
health 本身仍返回 200,worker 不 ok 由调用方看字段判断,不要改成 503。
|
||||
`deploy/monitor.sh`(主机侧,codexsvc 可读,不读 env 文件)检查
|
||||
server/worker 的 systemctl 状态、health 四项、pg_isready、docker ps 里
|
||||
codex-postgres 在运行、磁盘 <90%、app 目录存在,输出一行 JSON,任一
|
||||
项 fail 则退出非零;`--self-test` 只验证脚本机制(health 解析 + JSON
|
||||
合法),不验证主机健康。`deploy/monitor.service` /
|
||||
`deploy/monitor.timer` 每 5 分钟跑一次,日志进 journal。
|
||||
- 多用户真实验证(`deploy/verify-multiuser.py`,主机侧以 codexsvc 身份
|
||||
跑):内存里建 4 个测试用户与 2 个测试项目(P2 只有 owner 是成员),
|
||||
用 HTTP API 验证建任务、读任务、merge 的角色权限与跨项目隔离;
|
||||
merge 用的 completed 测试任务是直接在 DB 里置 completed,不跑模型;
|
||||
测试任务全部带永不存在的依赖 `multiuser-test-never-completes`,生产
|
||||
worker 只派依赖全部 completed 的任务,所以测试任务永远不会被认领去
|
||||
跑模型,不花模型钱、不改仓库;测试行全部用 `multiuser-test-` 前缀,
|
||||
清理时只删这些行,清理失败退出非零并列出残留 id(不含 key);审计
|
||||
日志是 append-only 的,验证审计条目会留下,脚本不删审计表。
|
||||
- 仍未做(不要宣称已做):推送告警通道(monitor 失败时通知到 Muse 聊天
|
||||
的通道还没定);回滚不回数据库(改类型/删列的迁移需单独评估);恢复
|
||||
演练只比行数,恢复到生产库的真实灾难恢复流程还没演练过;部署脚本、
|
||||
恢复演练、monitor、多用户验证四项的主机实跑还没做(本轮只在 VM 上
|
||||
跑了单测、deploy 干跑、monitor 自测与 VM 实跑)。
|
||||
|
||||
Reference in new issue
Block a user