部署与运维
本页用途
本页定义 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 来源提交。
首次部署与升级顺序
- 记录当前/目标 commit、迁移版本、制品校验值和回退条件。
- 备份 MySQL,并验证备份可读取;确认持久存储快照/备份策略。
- 把制品解压到新的 release 目录,不原位覆盖当前版本。
- 用相同 MySQL 8 版本的隔离库执行全部 migration up/down/up 验证。
- 停止取新任务并等待在途 worker 到优雅退出期限;旧 worker 未结束时不得启动会竞争同一租约的错误版本。
- 在生产执行
migrate ... up;检查版本,禁止任何 AutoMigrate。 - 原子切换
current,启动/重启 portal 和 admin。 - 执行健康、就绪、登录、提交 mock/受控测试和权限检查。
- 验证生产访问 dev-tools 路径为 404/未注册,并核对路由清单。
- 观察错误率、租约过期、磁盘和数据库连接;满足观察窗口后完成发布记录。
健康与就绪
应用必须提供:
/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;制品配置样例保持注册默认关闭且不包含任何凭据。