停止持久化 provider 原始响应、兼容响应快照、attempt/event/outbox 重复 JSON,并由标准任务结果动态生成 Kling/Keling/Volces 兼容响应。 增加事件去重与预算、极简 callback 投递、7/30 天分批清理、安全删除条件、并发迁移索引及可实际恢复的任务域排除备份。历史清理默认关闭,待兼容协议和异步恢复在线验证后单独启用。 验证:Go 全量测试与 go vet、PostgreSQL 18 集成与实际备份恢复、迁移安全测试、bash -n、ShellCheck、Compose 配置和人工发布脚本测试均通过。
171 lines
7.9 KiB
Markdown
171 lines
7.9 KiB
Markdown
# 人工生产发布运行手册
|
||
|
||
## 原则
|
||
|
||
生产发布没有自动触发器。允许 Agent 直接提交和推送 `main`,但 Git 操作不会授权 publish 或 deploy:
|
||
|
||
1. 用户明确要求发布镜像后,Agent 执行本地 publish。
|
||
2. Agent 报告 manifest、digest、组件和冒烟结果并停止。
|
||
3. 用户再次明确确认上线后,Agent 执行 deploy。
|
||
|
||
不得把两步放进同一个命令、Git hook、Gitea Action、Webhook、服务、Timer 或轮询脚本。
|
||
|
||
## 一次性停用旧自动化
|
||
|
||
源码仓已经删除 `.gitea/workflows` 中的全部工作流,仓库级 Actions 开关必须为关闭;`main` 分支保护和 required status 也必须保持关闭。可用下列只读命令验收:
|
||
|
||
```bash
|
||
tea api /repos/BCAI/easyai-ai-gateway | node -e 'let s="";process.stdin.on("data",d=>s+=d);process.stdin.on("end",()=>console.log(JSON.parse(s).has_actions))'
|
||
tea branches list --repo BCAI/easyai-ai-gateway --output yaml | sed -n '/^- name: main$/,+3p'
|
||
```
|
||
|
||
第一条预期输出 `false`;分支状态预期 `protected: "false"` 且 `user-can-push: "true"`。
|
||
|
||
确认没有运行中任务后,在旧 Runner 主机执行:
|
||
|
||
```bash
|
||
systemctl disable --now easyai-gateway-ci-v2-runner.service
|
||
systemctl is-active easyai-gateway-ci-v2-runner.service
|
||
```
|
||
|
||
预期第二条命令返回 `inactive`。保留 `easyai-gateway-ci-v2-data` volume 观察一段时间,不立即删除。
|
||
|
||
生产部署仓不得保留 Tag/Main dispatcher、Webhook receiver 或 Timer。先只读列出相关单元,确认精确名称后逐个 `disable --now`,不得用模糊匹配批量停止其他 EasyAI 服务:
|
||
|
||
```bash
|
||
systemctl list-unit-files --type=service --type=timer | grep -Ei 'easyai.*(release|dispatch|deploy)'
|
||
```
|
||
|
||
## 安装固定生产助手
|
||
|
||
生产助手只解析 manifest、拉取 digest、备份/迁移、更新 Compose、探活和回滚;它不是常驻服务,也不读取源码 checkout。
|
||
|
||
把以下三个已审查文件复制到生产服务器:
|
||
|
||
- `deploy/manual/easyai-ai-gateway-release`
|
||
- `deploy/manual/easyai-ai-gateway-release.conf.example`
|
||
- `scripts/release-manifest.mjs`
|
||
|
||
在可信 checkout 中可运行一次安装器:
|
||
|
||
```bash
|
||
sudo ./deploy/manual/install-release-helper.sh
|
||
sudoedit /etc/easyai-ai-gateway-release.conf
|
||
sudo /usr/local/sbin/easyai-ai-gateway-release status
|
||
```
|
||
|
||
配置文件必须为 root 所有、`0600`。助手安装后不会创建或启用 service、timer、webhook 或 watcher。
|
||
|
||
默认生产资源:
|
||
|
||
- 部署目录:`/root/easyai-ai-gateway-deploy`
|
||
- 发布清单:`/root/easyai-ai-gateway-deploy/.releases`
|
||
- 数据库备份:`/var/backups/easyai-ai-gateway`
|
||
- 公网入口:`https://ai.51easyai.com`
|
||
|
||
## 阶段一:本地 publish
|
||
|
||
前置条件:Docker Desktop/Engine、Buildx、Compose v2、Node、Go、可用 Registry 登录和生产只读 SSH。源码必须无 tracked/untracked 改动,HEAD 必须属于最新获取的 `origin/main` 历史。
|
||
|
||
```bash
|
||
docker login --username=<your-aliyun-account> registry.cn-shanghai.aliyuncs.com
|
||
./scripts/publish-release-images.sh --components auto
|
||
```
|
||
|
||
`auto` 读取线上当前 manifest,与 HEAD 比较后选择:
|
||
|
||
- `api`:API、Go workspace 或迁移变化;
|
||
- `web`:Web、contracts、pnpm 或 Nginx 容器配置变化;
|
||
- `all`:Dockerfile/Compose 等共享运行时变化;
|
||
- `none`:无运行时变化,不产生发布物。
|
||
|
||
首次发布或线上状态不可验证时只能显式使用:
|
||
|
||
```bash
|
||
./scripts/publish-release-images.sh --components all
|
||
```
|
||
|
||
此路径保守地把迁移标记为变化:生产助手会先备份数据库,再运行新 API 镜像内的幂等 migrator。
|
||
|
||
publish 会执行快速 Go 测试、迁移安全校验、`linux/amd64` 构建、临时完整栈迁移和 API simulation 冒烟。全部通过后才推送完整 SHA Tag,并生成带 SHA-256 内容完整性字段的固定 schema manifest:
|
||
|
||
```text
|
||
dist/releases/<40 位 Git SHA>.json
|
||
```
|
||
|
||
成功输出必须包含 `production_changed=false`。此时 Agent 应报告结果并停止,不得继续上线。
|
||
|
||
## 阶段二:人工 deploy
|
||
|
||
收到用户再次确认后执行:
|
||
|
||
```bash
|
||
./scripts/deploy-production-release.sh dist/releases/<40 位 Git SHA>.json
|
||
```
|
||
|
||
客户端和生产助手都会确认当前生产 SHA 仍等于 manifest 的 `baseReleaseSha`。不相等说明 publish 之后生产发生变化,必须重新 publish,不能覆盖或使用强制参数。
|
||
|
||
助手随后验证:
|
||
|
||
- API/Web 均为完整 Registry digest;
|
||
- 镜像为 `linux/amd64`;
|
||
- 本次变化组件的 OCI revision 等于 release SHA;
|
||
- 迁移变化时生成排除任务历史数据的完整 schema/业务状态备份,并实际恢复到一次性数据库验证长期数据、外键和空任务历史表;
|
||
- 内部 API health/readiness/OpenAPI;
|
||
- Web 页面和 Web API 反代;
|
||
- 公网 health/readiness/OpenAPI。
|
||
|
||
失败时自动恢复上一组应用 digest 并重复探活。已经执行的 expand-only 数据库迁移不会自动恢复。
|
||
|
||
## 任务历史治理
|
||
|
||
首次上线极简写入时保持 `AI_GATEWAY_TASK_CLEANUP_ENABLED=false`。先验证 Kling V1/V2、Volces 的提交、查询、失败响应和异步恢复均不依赖兼容快照或 provider 原始响应,再单独开启 cleanup。不要把首次清理和应用切换放在同一个维护动作中。
|
||
|
||
开启后每 5 分钟确认 7 天分析数据和 30 天主任务的过期积压;任一积压最长时间超过 24 小时、callback 待投递增长、结算待办增长或事件跳过指标异常时暂停治理并排查。以下查询只用于只读验收:
|
||
|
||
```sql
|
||
SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS total_size
|
||
FROM pg_catalog.pg_statio_user_tables
|
||
WHERE relname IN (
|
||
'gateway_tasks',
|
||
'gateway_task_attempts',
|
||
'gateway_task_events',
|
||
'gateway_task_callback_outbox',
|
||
'gateway_task_param_preprocessing_logs'
|
||
)
|
||
ORDER BY pg_total_relation_size(relid) DESC;
|
||
|
||
SELECT
|
||
(SELECT count(*) FROM gateway_task_attempts WHERE created_at < now() - interval '7 days') AS expired_attempts,
|
||
(SELECT count(*) FROM gateway_task_events WHERE created_at < now() - interval '7 days') AS expired_events,
|
||
(SELECT count(*) FROM gateway_task_param_preprocessing_logs WHERE created_at < now() - interval '7 days') AS expired_parameter_logs,
|
||
(SELECT count(*) FROM gateway_tasks WHERE finished_at < now() - interval '30 days') AS candidate_tasks;
|
||
```
|
||
|
||
逻辑删除稳定后执行 `VACUUM (ANALYZE)` 更新统计信息;它不会把空间归还给操作系统。物理回收只在独立维护窗口中逐表执行 `pg_repack`,执行前确认扩展、额外磁盘空间、主从延迟和锁等待均满足要求。优先处理 `gateway_task_events`、`gateway_task_attempts`、`gateway_tasks`,不得与 deploy、迁移或大批量清理并行。
|
||
|
||
治理控制目标:
|
||
|
||
- 新任务的兼容 body/Header、event/outbox payload、attempt 大 JSON 均为空;
|
||
- 普通成功任务不超过 4 条事件,每任务非终态事件不超过 16 条;
|
||
- 分析数据过期积压低于 24 小时,稳态分析表总量不超过主任务表的 20%;
|
||
- 首轮治理后任务域低于治理前 10.9GB 的 40%;
|
||
- 发布备份不超过 1GB,备份和实际恢复各不超过 5 分钟。
|
||
|
||
## 状态与回滚
|
||
|
||
```bash
|
||
./scripts/deploy-production-release.sh --status
|
||
./scripts/deploy-production-release.sh --rollback <服务器已有的历史 SHA>
|
||
```
|
||
|
||
回滚也需要用户明确指令。服务器只接受 `.releases/<SHA>.json` 中已有版本,拒绝远端任意 Tag、`latest` 或临时镜像。
|
||
|
||
## 故障排查
|
||
|
||
- publish 前失败:没有推送镜像,也没有可部署 manifest;修复本地环境后重试。
|
||
- 镜像已经部分推送但 manifest 未生成:SHA Tag 被视为占用,先审查 Registry 中的 digest,不得覆盖重推。
|
||
- deploy 报基线过期:重新读取线上状态并重新 publish。
|
||
- 迁移备份失败:生产不切换。
|
||
- 生产探活和自动应用回滚都失败:停止发布,保留日志、当前数据库和 release manifest,人工恢复服务;不得自动执行 `pg_restore`。
|