Files
easyai-ai-gateway/docs/runbooks/production-ci-cd.md
T
easyai 762d61c9cf docs(release): 补充仓库 Actions 停用验收
记录仓库级 has_actions=false 和 main 无保护规则的只读验收命令。
2026-07-22 15:17:11 +08:00

5.6 KiB
Raw Blame History

人工生产发布运行手册

原则

生产发布没有自动触发器。允许 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 也必须保持关闭。可用下列只读命令验收:

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 主机执行:

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 服务:

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 中可运行一次安装器:

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 历史。

docker login --username=<your-aliyun-account> registry.cn-shanghai.aliyuncs.com
./scripts/publish-release-images.sh --components auto

auto 读取线上当前 manifest,与 HEAD 比较后选择:

  • apiAPI、Go workspace 或迁移变化;
  • webWeb、contracts、pnpm 或 Nginx 容器配置变化;
  • allDockerfile/Compose 等共享运行时变化;
  • none:无运行时变化,不产生发布物。

首次发布或线上状态不可验证时只能显式使用:

./scripts/publish-release-images.sh --components all

此路径保守地把迁移标记为变化:生产助手会先备份数据库,再运行新 API 镜像内的幂等 migrator。

publish 会执行快速 Go 测试、迁移安全校验、linux/amd64 构建、临时完整栈迁移和 API simulation 冒烟。全部通过后才推送完整 SHA Tag,并生成带 SHA-256 内容完整性字段的固定 schema manifest

dist/releases/<40 位 Git SHA>.json

成功输出必须包含 production_changed=false。此时 Agent 应报告结果并停止,不得继续上线。

阶段二:人工 deploy

收到用户再次确认后执行:

./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
  • 迁移变化时备份可被 pg_restore -l 读取;
  • 内部 API health/readiness/OpenAPI
  • Web 页面和 Web API 反代;
  • 公网 health/readiness/OpenAPI。

失败时自动恢复上一组应用 digest 并重复探活。已经执行的 expand-only 数据库迁移不会自动恢复。

状态与回滚

./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