Files
chorus/docs/10-deployment-and-operations.md
T

11 KiB
Raw Blame History

generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Deployment-and-Operations wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Deployment-and-Operations.- wiki_revision: 0f7962051108f7a4bfb711cb41ffe0c0ab8b6915 synchronized_at: 2026-08-22T03:08:54Z

部署与运维

本页用途

本页定义 chorus 常驻服务的生产部署基线。当前尚无产品代码和真实生产环境,命令是后续实现必须满足的运维契约;域名、容量、保留期和阈值在首发工单确认后补充,不能由 Agent 猜测。

安全边界

  • 生产只部署已审核制品和 migrations/;不从 D:\github\goadmin 构建,不运行 AutoMigrate。
  • dev-tools 生成路由、/gen/todb 和写源码能力不得注册或被 nginx 代理;仅隐藏菜单不合格。
  • DSN、会话密钥、JWT secret、TLS 私钥只来自受控环境文件或密钥管理系统,权限最小化,不写入 Git/Wiki/日志。
  • 原图、结果和缩略图保存在受保护目录,通过 portal 鉴权读取,不由 nginx 直接暴露。
  • 数据迁移、回退、删除、清理和密钥轮换均是高风险操作,必须有工单、备份和人工确认。

服务概览

Internet
  → nginx/TLS
      → chorus-portal 127.0.0.1:8080(页面、HTMX、JSON、MVP 内嵌 worker)
      → chorus-admin  127.0.0.1:8081(MVP-1)
      → /admin-ui 静态构建产物(MVP-1)
portal/admin → MySQL 8.0
portal worker → Provider(安全 DialContext)
portal → /var/lib/chorus/storage(或后续对象存储适配)
项目 基线
运行账号 独立无登录 chorus 用户
制品目录 /opt/chorus/releases/<commit>/,/opt/chorus/current 指向当前版本
配置 /etc/chorus/chorus.env,仅运行账号可读
持久数据 /var/lib/chorus/storage,不随版本目录切换
日志 journald 或平台日志;结构化且脱敏
进程托管 systemd:chorus-portal.service、chorus-admin.service
数据库 MySQL 8.0,独立最小权限账号
构建 CI 使用 Go 1.26.5;portal/web 与 admin-ui 使用 Node >=22 + pnpm 9.15.1

配置与凭据

配置 用途 敏感
CHORUS_ENV=production 启用生产安全门禁 否
CHORUS_DSN MySQL 是
CHORUS_SESSION_KEY portal 会话 是
CHORUS_STORAGE_ROOT 生成物目录 否
上游/存储超时、lease、worker 数 执行与优雅退出 否
CHORUS_SESSION_TTL_MINUTES 服务端内存会话有效期 否
CHORUS_LOGIN_ATTEMPTS / CHORUS_LOGIN_WINDOW_SECONDS 登录节流窗口 否
CHORUS_MAX_PROMPT_BYTES prompt 字节上限 否
CHORUS_MAX_IMAGES / CHORUS_MAX_IMAGE_BYTES / CHORUS_MAX_UPLOAD_BYTES / CHORUS_MAX_IMAGE_PIXELS 上传边界 否
CHORUS_HISTORY_LIMIT 历史查询上限 否
保留期与磁盘告警 清理和容量保护 否

生产启动必须校验必需配置、会话/JWT 密钥、目录权限、超时与 lease 关系;缺失或不安全时 fail closed。CHORUS_ENV=production 下若检测到 AutoMigrate 或 dev-tools 注册必须拒绝启动。

制品构建

在干净 CI 环境按固定提交来源导入后的 chorus 代码构建:

go version
corepack pnpm --dir portal/web install --frozen-lockfile
corepack pnpm --dir portal/web build
go test ./...
go vet ./...
go build -trimpath -o dist/chorus-portal ./portal
go -C admin build -trimpath -o ../dist/chorus-admin .
corepack pnpm --dir admin-ui install --frozen-lockfile
corepack pnpm --dir admin-ui build:prod

portal 的模板和静态资源嵌入 chorus-portal 二进制,运行环境不需要 Node,也不从 CDN 加载资源;CI 必须先按 portal/web/pnpm-lock.yaml 重建并确认工作区无差异。MVP-0 没有 admin/admin-ui 时跳过对应命令并在发布记录说明。制品记录 chorus commit、Go module 校验、portal/web 与 admin-ui lockfile 和固定 go-admin 来源提交。

首次部署与升级顺序

  1. 记录当前/目标 commit、迁移版本、制品校验值和回退条件。
  2. 备份 MySQL,并验证备份可读取;确认持久存储快照/备份策略。
  3. 把制品解压到新的 release 目录,不原位覆盖当前版本。
  4. 用相同 MySQL 8 版本的隔离库执行全部 migration up/down/up 验证。
  5. 停止取新任务并等待在途 worker 到优雅退出期限;旧 worker 未结束时不得启动会竞争同一租约的错误版本。
  6. 在生产执行 migrate ... up;检查版本,禁止任何 AutoMigrate。
  7. 原子切换 current,启动/重启 portal 和 admin。
  8. 执行健康、就绪、登录、提交 mock/受控测试和权限检查。
  9. 验证生产访问 dev-tools 路径为 404/未注册,并核对路由清单。
  10. 观察错误率、租约过期、磁盘和数据库连接;满足观察窗口后完成发布记录。

健康与就绪

应用必须提供:

  • /healthz:进程存活,不依赖上游;
  • /readyz:必需配置有效、MySQL 可用、迁移版本匹配、存储可写、生产 dev-tools 未注册;
  • admin 健康路径不得泄露版本、DSN、路由或密钥。
systemctl status chorus-portal
curl --fail http://127.0.0.1:8080/healthz
curl --fail http://127.0.0.1:8080/readyz
journalctl -u chorus-portal -n 100 --no-pager

MVP-1 同样检查 admin 8081。外部检查经 TLS/nginx 访问,不把内部端口暴露公网。

portal 会话与请求边界

MVP-0 的终端用户会话保存在单个 portal 进程内存中,Cookie 只携带签名后的不透明 session ID。部署必须保持单实例;portal 重启或切换版本会让全部终端用户重新登录。不要在负载均衡后启动多个 portal 实例,除非先建立新的共享会话设计和工单。

生产 Cookie 必须为 Secure/HttpOnly/SameSite=Lax。反向代理不得记录 Cookie 或 CSRF header;请求日志和 GORM SQL 使用参数化输出。JSON 正文、multipart 总量、单图字节/像素/数量和历史查询都有显式生产配置,缺少任一阈值时启动失败。

上传先在应用层校验,再在 generation 事务回调中保存带 owner/generation metadata 的文件。事务失败只清理本请求保存的 key;存储根目录不能作为 nginx/static 目录暴露。

MVP-0 worker 已实现配置与退出行为

portal 单二进制会同时启动 HTTP server 和内嵌 worker。生产除现有 portal 限制外,还必须显式提供:

变量 约束
CHORUS_WORKER_LEASE_SECONDS 正整数;必须大于 CHORUS_PROVIDER_HTTP_TIMEOUT_SECONDS + 5
CHORUS_WORKER_POLL_MILLISECONDS 正整数;控制空队列轮询间隔
CHORUS_PROVIDER_HTTP_TIMEOUT_SECONDS 正整数;安全 HTTP client 总超时和响应头超时
CHORUS_PROVIDER_MAX_RESPONSE_BYTES 正整数;Provider JSON/Base64/下载响应读取上限

Provider API Key 由授权管理员通过管理端写入 provider_credentials.api_key。数据库、备份和管理员会话泄露会直接暴露完整 Key;必须限制数据库和管理端权限,列表/普通详情/日志/audit 禁止返回完整值,单条读取响应禁止缓存。部署环境不再需要 CHORUS_MASTER_KEY。

CHORUS_TEST_DISABLE_WORKER 是测试专用开关,只在 CHORUS_ENV=test 接受;开发和生产设置为 true 会启动失败。部署制品和服务配置不得设置该变量,portal/web/e2e/fixture 也不得进入生产运行命令。

#13 已在 Windows 前台进程两次用 Ctrl+C 验证停止认领、关闭 HTTP 并等待 worker 返回。真实 Linux/systemd 的 TERM、超时和重启恢复仍属于首次发布前门禁,不能把本机验证写成已上线。

worker 优雅退出

  • 收到 TERM 后先停止认领新任务;
  • 在途任务只在 lease_token 仍有效时提交;
  • systemd 的停止等待时间大于应用优雅退出期限;
  • 到期未完成时退出,让租约自然过期,新 worker 原子重领;
  • 不把 running 改回 pending,不无条件标 failed,不删除其他 worker 文件。
  • 每种 generation kind 必须恰好一个启用的 ProviderModel;0 个或多个都应 fail closed,不随机选取。
  • Provider 请求和结果 URL 只走安全 Client;带 Authorization 的跨 origin 重定向必须拒绝。
  • 上游超时后用独立短超时尝试最终 CAS;token 已陈旧时只清理本 attempt 生成的临时结果。
  • 自动健康/发布验证使用 mock,不把真实 Provider 请求作为启动探针。

存储、备份与监控

必须备份 MySQL 和仍在保留期内的生成物元数据/文件,并定期做恢复演练。至少监控:

  • MySQL 连接、慢查询、迁移版本;
  • pending 数/最老等待时间、running 租约过期数、失败率;
  • Provider 429/5xx/超时和熔断状态;
  • 存储总量、可用空间、临时文件增长、缩略图失败;
  • 登录失败/节流、SSRF 拦截和越权拒绝;
  • portal/admin 健康与重启次数。

保留期、备份频率、RPO/RTO、磁盘阈值尚未确认,是生产上线门禁。清理任务不得在确认前启用。

回滚

  • 优先使用向后兼容迁移并回滚二进制;先停止新任务,再切回上一 release,重启并执行健康检查。
  • 不能仅通过切回代码撤销数据库变更。需要 down 或恢复备份时先评估数据丢失,取得人工确认。
  • 若新版本已写入旧版本无法识别的数据,停止回滚并采用前滚修复或已演练的备份恢复方案。
  • 回滚后检查 migration version、陈旧 worker CAS、任务重复、文件一致性和 dev-tools 路由。
  • 每次真实发布前至少在隔离环境演练相同升级与回滚步骤。

已知限制

  • 尚未完成真实 Linux/systemd/nginx 部署、备份恢复和回滚演练;
  • 高可用、多机 worker、对象存储和滚动升级尚未设计;
  • 生产域名、TLS、容量、保留期、告警、RPO/RTO 和具体超时/lease 阈值待首发部署工单确认;
  • 在这些门禁完成前,本页不能作为“已验证可上线”的证据。

#24 管理端部署补充(2026-08-22)

  • 先执行全部 migrations/,再启动 chorus-admin server --config <受保护 settings.yml>;启动过程不会建库、迁移、seed 或 AutoMigrate。
  • Casbin 只使用 migration 管理的 sys_casbin_rule;发布检查应确认未生成或依赖默认 casbin_rule。
  • admin-ui 静态产物通过 pnpm --dir admin-ui build:prod 构建,API 基址在部署时配置并由反向代理限制到管理网络。
  • 首次管理员只通过仓库外环境变量运行 chorus-admin-bootstrap;密码不得进入服务文件、命令历史、日志或文档。