Clone
18
Deployment-and-Operations
ila edited this page 2026-08-29 21:42:52 +08:00
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

部署与运维

本页用途

本页定义 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。启动必须显式传入 --config <settings.yml>。监听地址、Provider 和 worker 参数以 YAML 为基础值,同名环境变量可逐项覆盖;监听地址未在 YAML 或环境变量中设置时默认为 127.0.0.1:8080:

YAML 字段 环境覆盖变量 约束
server.listen_address CHORUS_LISTEN_ADDRESS host:port;端口范围 1–65535;示例和默认值仅监听 loopback
worker.lease_seconds CHORUS_WORKER_LEASE_SECONDS 正整数;必须大于 Provider HTTP timeout + 5
worker.poll_milliseconds CHORUS_WORKER_POLL_MILLISECONDS 正整数;控制空队列轮询间隔
provider.http_timeout_seconds CHORUS_PROVIDER_HTTP_TIMEOUT_SECONDS 正整数;安全 HTTP client 总超时和响应头超时
provider.max_response_bytes CHORUS_PROVIDER_MAX_RESPONSE_BYTES 正整数;Provider JSON/Base64/下载响应读取上限
provider.allowed_ports CHORUS_PROVIDER_ALLOWED_PORTS 非空端口列表;仅在受控变更后加入所需公开端口

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;密码不得进入服务文件、命令历史、日志或文档。

单一外部 Host 与内部端口(#38)

config/local-services.yml 和 scripts/*-all.bat 仅用于 Windows 本地开发,不是生产服务管理器。生产继续由 systemd/等价服务管理器和反向代理托管,敏感配置分别保存在 Portal 环境与 Admin settings 中。

推荐同一外部 HTTPS Host 使用路径分流:

/             -> chorus-portal 内部 loopback 地址
/admin/       -> admin-ui 静态构建产物
/admin-api/   -> chorus-admin 内部 loopback 地址

反向代理必须正确剥离或保留前缀,并把 Admin UI 的构建期 API 基址设置为同源 /admin-api;不得把 Admin API 或 Portal 直接绑定到公网地址。Portal 与 Admin API 内部端口仍由各自配置控制,Admin UI 生产态没有独立 Node 端口。路径、Cookie scope、CORS、CSP、上传上限和 HTTPS 终止必须在独立部署工单验证后才能发布;#38 不执行发布或修改生产反向代理。

Windows 本机 Supervisor 运行

D:\supervisor 是当前 Windows 本地常驻运行方式,不替代生产反向代理或正式服务管理器。通过 scripts/install-supervisor.bat 生成三个独立实例:Portal、Admin API 和 Admin UI。Supervisor program 配置只保存进程路径、配置路径、Host/Port 与日志策略;Portal 数据库/Session 配置从 portal.environment_file 读取,Provider/worker 参数从 portal.settings_file 读取,Admin 敏感值继续只存在于 admin/config/settings.yml。

安装或更新:

scripts\install-supervisor.bat
D:\supervisor\supervisord.exe ctl /c D:\supervisor\supervisord.conf reload
D:\supervisor\supervisord.exe ctl /c D:\supervisor\supervisord.conf status

回退时停止 chorus-user、chorus-admin-api、chorus-admin-ui,移除 D:\supervisor\programs\chorus.conf 后 reload,再按需使用 scripts/start-all.bat。不得同时使用两套入口占用相同端口。

MVP-2 迁移 000006 运维门禁(#41)

  • 发布包含 #41 或后续 MVP-2 服务代码前,必须先备份并执行 000006_mvp2_openapi_governance.up.sql;应用启动仍不会自动迁移或 AutoMigrate。
  • 000006 为 generations 增加非空 available_at 并重建队列索引,同时新增 API Key、安全审计表和管理端授权种子。升级后要检查 migration version、两张表、队列索引和稳定键 seed。
  • down 会删除 API Key 与安全审计结构,因此在任一新表非空时必定失败。生产不得使用 migrate force 绕过保护;需要回退时先停止 portal/worker、备份并核验、明确处置数据、取得人工确认,再在已演练步骤下执行。
  • 仅回退二进制不能撤销本次数据库结构。旧二进制与 available_at 的兼容性必须在具体发布工单中验证,不能把隔离库测试替代真实发布演练。

MVP-2 三维限流部署要求(#44)

生产 Portal 环境必须显式设置并校验以下六个正整数:CHORUS_USER_RATE_LIMIT_CAPACITY、CHORUS_USER_RATE_LIMIT_WINDOW_SECONDS、CHORUS_API_KEY_RATE_LIMIT_CAPACITY、CHORUS_API_KEY_RATE_LIMIT_WINDOW_SECONDS、CHORUS_PROVIDER_RATE_LIMIT_CAPACITY、CHORUS_PROVIDER_RATE_LIMIT_WINDOW_SECONDS。具体容量应由受控压测和 Provider 合同确定,不从开发默认值直接推断。

当前限流器与终端用户会话一样是 portal 进程内状态:

  • 重启或发布切换会清空所有限流窗口;发布记录应明确这一行为。
  • 不得通过启动多个 portal 实例提高限流容量;多实例会分别计数,必须先建立共享限流设计和工单。
  • 队列延后使用 generations.available_at,部署 #44 前必须已经完成迁移 000006。
  • 监控应区分本地 local_rate_limit 延后事件与真实 Provider 429;本地限流不得计入 Provider 失败率、熔断或真实 attempt。
  • 发布验证使用 mock Provider 检查:用户/API Key 429 与 Retry-After、Provider 限流不触发上游、全部候选受限后可在 available_at 到期重新认领。不得用真实 Provider 额度反复验证。

Admin 生成媒体配置(#72)

Admin API 需要对 Portal 使用的同一生成物目录拥有只读权限:

settings:
  extend:
    chorus:
      storage_root: ../var/storage

storage_root 按 Admin API 进程的工作目录解析。Supervisor 本机实例从 admin/ 启动,因此项目内默认目录写为 ../var/storage;也可以配置绝对路径。目录必须已经存在,Admin 启动不会创建或写入该目录。

部署顺序:备份并确认数据库版本与 dirty 状态,执行 migration 10,安装新的 Admin API 与 Admin UI 制品,再重启 chorus-admin-api 和 chorus-admin-ui。migration 10 只登记四个 GET API、菜单关联和 chorus_operator Casbin 权限,不修改生成记录或媒体对象。回退时先回退 UI/API 制品,再执行 migration 10 down。

Portal 自助注册发布与回退(迁移 12)

发布包含 000012_portal_registration。执行迁移后自助注册默认关闭,因此升级本身不会开放访客注册;管理员确认部署边界、可信代理 CIDR 和独立限流参数后,才可在“用户与访问 → 终端用户”显式开放。开关只影响自助注册,已有用户登录和管理员建号保持可用。

Portal 配置必须显式设置 registration.attempts 与 registration.window_seconds;位于反向代理后时只把实际代理网段加入 server.trusted_proxy_cidrs。错误配置可信代理可能允许伪造来源地址并削弱限流,不能使用全网 CIDR。管理员策略 API 继续由 JWT/Casbin 保护,修改使用版本 CAS 并写脱敏审计。

迁移 12 的 down 会先尝试把 users.email 恢复为非空;只要存在自助注册产生的空邮箱账号就会失败,并在删除策略表和事件表之前停止。回退前必须由人工决定这些账号的处置方案,不得猜测邮箱、静默删除用户或强制迁移。Windows 制品必须包含 migration 12、更新后的 Portal/Admin 二进制和 Admin UI;制品配置样例保持注册默认关闭且不包含任何凭据。