generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Deployment-and-Operations wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Deployment-and-Operations.- wiki_revision: 80dac27043770615620a5d346e5b0e6f72451ab4 synchronized_at: 2026-08-21T04:34:00Z # 部署与运维 ## 本页用途 本页定义 chorus 常驻服务的生产部署基线。当前尚无产品代码和真实生产环境,命令是后续实现必须满足的运维契约;域名、容量、保留期和阈值在首发工单确认后补充,不能由 Agent 猜测。 ## 安全边界 - 生产只部署已审核制品和 `migrations/`;不从 `D:\github\goadmin` 构建,不运行 AutoMigrate。 - dev-tools 生成路由、`/gen/todb` 和写源码能力不得注册或被 nginx 代理;仅隐藏菜单不合格。 - DSN、会话密钥、Provider 主密钥/key ring、TLS 私钥只来自受控环境文件或密钥管理系统,权限最小化,不写入 Git/Wiki/日志。 - 原图、结果和缩略图保存在受保护目录,通过 portal 鉴权读取,不由 nginx 直接暴露。 - 数据迁移、回退、删除、清理和密钥轮换均是高风险操作,必须有工单、备份和人工确认。 ## 服务概览 ```text 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//`,`/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_MASTER_KEY` / key ring | Provider Key 加解密与轮换 | 是 | | `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` | 历史查询上限 | 否 | | 保留期与磁盘告警 | 清理和容量保护 | 否 | 生产启动必须校验必需配置、密钥长度/key_id、目录权限、超时与 lease 关系;缺失或不安全时 fail closed。`CHORUS_ENV=production` 下若检测到 AutoMigrate 或 dev-tools 注册必须拒绝启动。 ## 制品构建 在干净 CI 环境按固定提交来源导入后的 chorus 代码构建: ```bash 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 build -trimpath -o dist/chorus-admin ./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、路由或密钥。 ```bash 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/下载响应读取上限 | `CHORUS_MASTER_KEY` 的原始字节长度必须是 AES 支持的 16、24 或 32 字节;MVP-0 写入信封使用 `key_id=primary`。生产缺失或长度无效时 portal 启动失败且不输出密钥。开发环境未配置主密钥时只生成进程内临时密钥,适用于默认 `auth_type=none` mock;要测试持久化 Bearer 凭据必须显式提供稳定的仓库外密钥。 `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 阈值待首发部署工单确认; - 在这些门禁完成前,本页不能作为“已验证可上线”的证据。