183 lines
8.2 KiB
Markdown
183 lines
8.2 KiB
Markdown
# 智念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` 继续拒绝直接访问后端路径。
|