Files
NianAIGC/docs/DEPLOYMENT.md
2026-09-11 15:33:18 +08:00

183 lines
8.2 KiB
Markdown
Raw Blame History

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.

# 智念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/`:
```bash
docker build -t REGISTRY/PROJECT/zhinian-aigc:TAG .
```
Go 镜像构建方式见 [`backend/README.md`](../backend/README.md)。国内 CI 可使用:
```bash
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` 必须使用 `sslmode=disable`。Go 和手工迁移使用的 Node 客户端都会强制
归一化为该值;即使旧 Secret 仍包含 TLS 参数,客户端也不会读取 CA 或协商 TLS。
这表示 ACK 到 RDS 的数据库流量不使用 TLS、链路内容为明文。只应使用 RDS 内网地址,
并通过 VPC、安全组和白名单严格限制访问;若未来需要链路加密,必须同时修改客户端策略
和 RDS 配置后再部署。
首次发布前,部署负责人按顺序手工执行:
1. `database/migrations/0001_initial_schema.sql`
2. `database/migrations/0002_generation_lifecycle_fencing.sql`
3. `database/migrations/0003_platform_runtime_settings.sql`
4. `database/migrations/0004_seedream_billing_provider.sql`
5. `database/migrations/0005_seedream_layer_compositions.sql`
6. `database/migrations/0006_minimax_h3_billing_provider.sql`
7. 应用角色授权语句(参考 `scripts/migrate-postgres.mjs`)
升级已有生产库接入 MiniMax H3 时,只需在发布新 Go 镜像前执行尚未应用的 `0006`;
新版本 `/api/ready` 会同时检查计费服务商约束中已包含 `seedream` 和 `minimax`,未迁移的
Pod 不会进入 Ready,避免运行后才在报价阶段返回 500。
生产不部署迁移 Job。遇到历史重复数据时迁移会失败,需先人工审计,不能跳过或自动删除
计费与用量记录。仓库不再提供可误执行的 Node Migration Job 或 Node Worker 清单;后台
任务由 Go API 内嵌 WorkerLoop 处理。
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`](../deploy/ack/secrets.example.yaml) 创建不提交
Git 的 `deploy/ack/secrets.production.yaml`,替换所有占位值。Web 不需要任何运行时
Secret;会话密钥只属于 Go。
仓库内静态检查:
```bash
npm run deploy:check
```
提交到集群前还应使用目标集群验证准入策略:
```bash
# 首次部署必须先真实创建 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
```
确认无误后发布:
```bash
# 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`
- `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`:Go 进程存活检查;以 250ms 上限刷新服务商状态,失败时使用进程内缓存并仍返回存活状态。
- Go `/api/ready`:检查 PostgreSQL schema、表权限和业务函数权限。
发布后执行:
```bash
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/`。需要完整联调
或在服务器上使用仓库内编排时:
```bash
cp .env.example .env.local
# 正常发布:保持 ZHINIAN_DATA_BACKEND=postgres,并填写 DATABASE_URL 和登录密钥
# 设置页配置写入 PostgreSQL;环境中的认证、OSS 和服务商值作为首次启动回退值
./scripts/deploy.sh
```
根目录 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` 继续拒绝直接访问后端路径。