Files
NianAIGC/docs/DEPLOYMENT.md
T
2026-10-12 13:57:23 +08:00

10 KiB
Raw Blame History

智念AIGC平台部署说明

生产拓扑

生产环境只有两类运行时:

  • zhinian-web:Nginx 静态容器,只包含 Next.js out/ 导出结果,不读取 Secret、 ConfigMap、数据库或 Go 内网地址。
  • zhinian-go-api:唯一应用后端,负责 /api、/uploads、 /generated-results、登录会话、权限、数据库、供应商调用及内嵌 WorkerLoop。

浏览器始终请求同一个公网域名。Ingress 将后端路径转发给 Go,其余页面和静态资源转发给 Web。登录 Cookie 为 HttpOnly Cookie,由浏览器自动随同域 API 请求携带;静态 Web 不解析 Cookie,也不持有会话签名密钥。

公网路径 工作负载
/api/internal/worker selectorless deny Service,不对公网开放
/api/* zhinian-go-api:8080
/uploads/* zhinian-go-api:8080
/generated-results/* zhinian-go-api:8080
其他路径 zhinian-web:3000

Web Nginx 对 Go 所有的三类路径也会直接返回 404,避免绕过 Ingress 后形成第二套 API 边界。

构建镜像

Web 镜像在 builder 阶段执行静态导出,runner 只保留非 root Nginx 和 out/:

docker build -t REGISTRY/PROJECT/zhinian-aigc:TAG .

Go 镜像构建方式见 backend/README.md。国内 CI 可使用:

docker build -f backend/Dockerfile.alpine \
  -t REGISTRY/PROJECT/zhinian-go-api:TAG backend/

发布前把 deploy/ack/web.yaml 和 deploy/ack/go-api.yaml 中的镜像占位符替换为 不可变标签或 digest。

RDS 与首次初始化

生产固定使用 PostgreSQL。数据库连接只通过 Go Deployment 的 Secret 注入,不得进入 Web 镜像或 ConfigMap。优先使用 RDS 内网地址,并仅对白名单中的 ACK 工作负载网段放行。

DATABASE_URL 和 DATABASE_MIGRATION_URL 必须使用 sslmode=disable。Go 和手工迁移使用的 Node 客户端都会强制 归一化为该值;即使旧 Secret 仍包含 TLS 参数,客户端也不会读取 CA 或协商 TLS。 这表示 ACK 到 RDS 的数据库流量不使用 TLS、链路内容为明文。只应使用 RDS 内网地址, 并通过 VPC、安全组和白名单严格限制访问;若未来需要链路加密,必须同时修改客户端策略 和 RDS 配置后再部署。

ACK 模板设置 DATABASE_AUTO_MIGRATE=true。Go API 启动时先连接 DATABASE_MIGRATION_URL(zhinian-migration-db Secret),按版本顺序执行镜像内已 同步的 database/migrations/*.sql,并用 DATABASE_APP_ROLE 给应用账号授权;全部成功后 才启动 HTTP 服务。该变量未提供时,启动器会回退使用 DATABASE_URL,但 ACK 明确使用 独立迁移账号。DATABASE_APP_ROLE 必须填写为 zhinian-go-db 连接串中的 PostgreSQL 用户名,且迁移账号须具备建表、修改表和授权权限。DATABASE_MIGRATION_URL 与 DATABASE_URL 的主机、端口和数据库名必须相同,连接用户名可以不同;启动器会检查。 默认启动迁移超时 180 秒;对应的 startupProbe 提供约 300 秒预算。每个迁移版本的 DDL 与版本记录在同一事务中;失败的版本会回滚,之前已成功提交的版本保留。迁移失败时 Pod 不监听端口,查看 Go Pod 日志并修复原因后再重启。回滚镜像不会逆向撤销数据库迁移。 遇到历史重复数据,先人工审计,不能跳过或 自动删除计费与用量记录。

每次新增迁移,在仓库根目录运行 npm run db:bundle,把根目录 SQL 同步成提交到 Git 的 Go 迁移包,然后构建 Go 镜像;不能修改已应用的历史 SQL。新镜像启动时会核对版本记录, 只执行未应用的迁移,重启不会重复建表。手工处理仍可从仓库根目录执行 npm run db:migrate;其连接账号也应具备上述权限。生产不部署迁移 Job 或 Node Worker, Go 镜像运行时不需要 Node/npm,也不用在 ACK「启动执行」中填写命令。

虽然当前 replicas: 1,RollingUpdate 的 maxSurge: 1 可能短暂同时运行新旧两个 Go Pod。迁移器使用 PostgreSQL 锁串行化迁移,不能以单副本假定没有并发。旧版 Pod 可能在 新版迁移期间继续接流量,因此新增 SQL 需兼容滚动期间的新旧程序。数据库迁移不能靠 回滚镜像自动撤销。

Go 首次启动会读取 zhinian-go-bootstrap Secret 中的 ZHINIAN_BOOTSTRAP_ADMIN_PHONE 和 ZHINIAN_BOOTSTRAP_ADMIN_PASSWORD,仅在不存在 超级管理员时创建一次。当前 ACK 清单不注入 ZHINIAN_BOOTSTRAP_ADMIN_NAME,因此名称 使用 Go 的默认值“平台超级管理员”。

ACK 配置与发布

先根据 deploy/ack/secrets.example.yaml 创建不提交 Git 的 deploy/ack/secrets.production.yaml,替换所有占位值。Web 不需要任何运行时 Secret;会话密钥只属于 Go。

将 configmap.yaml 中 DATABASE_APP_ROLE: REPLACE_WITH_APP_DB_ROLE 替换为应用数据库 用户(zhinian-go-db 的 APP_USER),并确认 zhinian-migration-db 的连接串指向同一 数据库;两条连接串的主机、端口和数据库名须一致。先发布 Secret/ConfigMap 再更新 Go Deployment。ACK 控制台的「启动执行」命令 与参数保持空白,镜像原本的 zhinian-api 启动入口会自行迁移。

仓库内静态检查:

npm run deploy:check

提交到集群前还应使用目标集群验证准入策略:

# 首次部署必须先真实创建 Namespace;否则后续 namespaced Secret/资源无法 dry-run。
kubectl apply -f deploy/ack/namespace.yaml

kubectl apply --dry-run=server -f deploy/ack/secrets.production.yaml
kubectl apply --dry-run=server -f deploy/ack/configmap.yaml
kubectl apply --dry-run=server -f deploy/ack/go-api.yaml
kubectl apply --dry-run=server -f deploy/ack/web.yaml
kubectl apply --dry-run=server -f deploy/ack/service.yaml
kubectl apply --dry-run=server -f deploy/ack/ingress.yaml

确认无误后发布:

# Namespace 已在上一步单独创建;Secret 必须先于引用它的工作负载发布。
kubectl apply -f deploy/ack/secrets.production.yaml
kubectl apply -f deploy/ack/configmap.yaml
kubectl apply -f deploy/ack/go-api.yaml
kubectl apply -f deploy/ack/web.yaml
kubectl apply -f deploy/ack/service.yaml
kubectl apply -f deploy/ack/ingress.yaml

Web 使用固定 UID/GID 101、只读根文件系统和 /tmp 临时卷,监听容器端口 3000。它是无状态静态服务,可以独立扩容。Go 模板默认一个副本;未接入 OSS 或其他 共享对象存储前,不要扩容 Go,否则上传及生成文件不能在副本间共享。此时文件位于 Go Pod 的 emptyDir,Pod 重建或滚动升级同样会永久丢失上传文件和生成结果;生产持久化这些 文件必须先接入 OSS 或其他共享对象存储。

配置边界

zhinian-go-runtime ConfigMap 只保存非敏感 Go 运行参数。下列敏感值必须放在 Kubernetes Secret:

  • DATABASE_URL
  • DATABASE_MIGRATION_URL(独立迁移账号,来自 zhinian-migration-db)
  • ZHINIAN_AUTH_SESSION_SECRET(Secret 名 zhinian-go-auth,仅注入 Go)
  • ZHINIAN_BOOTSTRAP_ADMIN_PHONE、ZHINIAN_BOOTSTRAP_ADMIN_PASSWORD
  • OSS 凭据;服务商凭据也可以先放 Secret 作为数据库尚未配置时的回退值
  • 开放 API Key、Webhook 签名密钥等

DATABASE_URL、首个超级管理员凭据、开放 API Key 和 Webhook 密钥仍以 ACK Secret/ConfigMap 为事实来源。设置页全部 27 项配置由超级管理员写入 platform_runtime_settings,数据库值优先于环境变量和本地文件回退值;环境中的认证、OSS 和服务商值只负责首次启动兜底。服务商配置、引擎选择和对公账户信息保存后立即生效;认证、 计费开关和 OSS 配置保存后需要重启 Go Deployment。未配置的服务商不会阻止 Go API 启动, 但选择该服务商的报价或生成请求会返回 503,也不会自动切换到占位或模拟生成。

探针与验收

  • Web /healthz:Nginx 静态进程存活检查。
  • Go /api/health:迁移完成且服务开始监听后的进程存活检查;以 250ms 上限刷新服务商状态,失败时使用进程内缓存并仍返回存活状态。
  • Go /api/ready:检查 PostgreSQL schema、表权限和业务函数权限。

发布后执行:

kubectl rollout status deployment/zhinian-go-api -n zhinian
kubectl rollout status deployment/zhinian-web -n zhinian

curl -f https://你的域名/
curl -f https://你的域名/api/health
curl -f https://你的域名/api/ready
curl https://你的域名/api/v1/openapi.json

还应验证:

  • 未登录访问受保护页面后,浏览器跳转到 /auth/login。
  • 登录成功后,GET /api/auth/me 返回当前用户,刷新页面仍保持登录态。
  • 普通用户无法访问管理员 API;管理员页面与 API 权限一致。
  • 上传、生成结果访问分别通过 /uploads、/generated-results 命中 Go。
  • Web Pod 中不存在数据库、服务商、会话签名密钥或 Go 内网地址。

本地开发

npm run dev 仍用于只开发前端;npm run build 生成可静态托管的 out/。需要完整联调 或在服务器上使用仓库内编排时:

cp .env.example .env.local
# 正常发布:保持 ZHINIAN_DATA_BACKEND=postgres,并填写 DATABASE_URL 和登录密钥
# 设置页配置写入 PostgreSQL;环境中的认证、OSS 和服务商值作为首次启动回退值
./scripts/deploy.sh

ACK 和 Compose 会向 Go 进程注入环境变量。若直接在本地执行 go run 或 Go 二进制, 需显式导出 DATABASE_AUTO_MIGRATE、DATABASE_MIGRATION_URL、DATABASE_URL 等 数据库启动参数;仅填写 .env.local 不等于已向 Go 进程注入这些变量。

根目录 Compose 会启动 zhinian-web 与 zhinian-go-api,挂载 deploy/nginx-compose.conf 将 /api、/uploads、/generated-results 转发给 Go;Go 内嵌 WorkerLoop,不再启动独立 Node Worker。ACK 生产仍使用上面的两镜像 + Ingress 拓扑, Web 镜像中的 deploy/nginx.conf 继续拒绝直接访问后端路径。