Files
NianAIGC/docs/DEPLOYMENT.md
2026-08-18 00:36:05 +08:00

177 lines
7.6 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. 应用角色授权语句(参考 `scripts/migrate-postgres.mjs`)
生产不部署迁移 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 凭据
- 开放 API Key、Webhook 签名密钥等
生产配置的事实来源是 ACK Secret/ConfigMap。不要通过静态页面或修改 Pod 文件来更新
生产密钥;修改 Secret/ConfigMap 后滚动 Go Deployment。
Go API 在 PostgreSQL 生产模式默认会校验即梦、EvoLink、百炼和 Seedance 的真实凭据;
任一服务商缺少凭据都会拒绝启动,不会自动切换到占位或模拟生成。若需要先上线配置页,
可在 Go API 的 ConfigMap 临时设置 `ZHINIAN_ALLOW_UNCONFIGURED_PROVIDERS=true`:服务可以
启动,但选中未配置服务商的报价和生成请求会返回 503。通过设置页填写凭据后,必须滚动重启
Go Deployment,确认健康后再将该开关恢复为 `false`。
## 探针与验收
- Web `/healthz`:Nginx 静态进程存活检查。
- Go `/api/health`:Go 进程存活检查,不查询数据库。
- 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、登录密钥和真实服务商凭据
# 临时引导:可将 ZHINIAN_ALLOW_UNCONFIGURED_PROVIDERS=true,先启动配置页;保存凭据后重启并恢复为 false
./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` 继续拒绝直接访问后端路径。