Files
ftb-project-management/docs/deployment.md

282 lines
9.8 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.

# 部署指南
本文档覆盖三种部署方式:
- 本地开发:前端、后端直接用 pnpm 跑PostgreSQL 和 Redis 用 `docker-compose.yml` 启动。
- 本地服务器部署:一台本机或局域网服务器用 `docker-compose.local.yml` 跑完整 Web、Server、PostgreSQL、Redis、Nginx。
- 云服务器生产:所有服务用 `docker-compose.prod.yml` 编排Nginx 对外暴露 HTTP业务数据写入 PostgreSQL volumeAI 配置文件写入 server volume。
## 本地开发部署
```bash
pnpm install
cp apps/server/.env.example apps/server/.env
docker compose up -d
pnpm db:migrate
pnpm dev
```
访问地址:
- Web: `http://localhost:3000`
- API: `http://localhost:3001/api/v1`
- API 健康探测:`http://localhost:3001/api/v1/config/ai`
## 本地服务器部署
本地服务器部署适合这些场景:
- 在自己的电脑上用接近生产的方式跑完整系统。
- 在办公室内网或测试机上给同事访问。
- 不想安装 Node/pnpm 到目标机器,只想用 Docker Compose 管理全栈服务。
它不同于“本地开发”:不会用 `pnpm dev`,而是构建生产镜像并通过 Nginx 统一入口访问。
### 1. 准备配置
```bash
cp .env.local-server.example .env.local-server
```
默认访问地址是:
```text
http://localhost:8080
```
如果要给局域网其他人访问,修改 `.env.local-server`
```env
LOCAL_ACCESS_HOST=192.168.1.50
NEXTAUTH_URL=http://192.168.1.50:8080
```
其中 `192.168.1.50` 换成这台机器在局域网里的 IP。`NEXT_PUBLIC_API_URL` 保持 `/api/v1`,让浏览器通过同一个 Nginx 入口访问后端。
### 2. 校验并启动
```bash
pnpm deploy:verify
pnpm deploy:local:build
pnpm deploy:local:up
```
首次启动后执行数据库 migration
```bash
docker compose --env-file .env.local-server -f docker-compose.local.yml exec server pnpm --filter server db:deploy
```
检查服务:
```bash
docker compose --env-file .env.local-server -f docker-compose.local.yml ps
curl http://localhost:8080/api/v1/config/ai
```
停止本地服务器部署:
```bash
pnpm deploy:local:down
```
本地服务器部署使用独立 volume
- `local_postgres_data`
- `local_redis_data`
- `local_server_data`
这些 volume 与云服务器生产部署的 `postgres_data``redis_data``server_data` 分开,避免本机测试数据和生产数据混在一起。
## 云服务器生产部署
### 1. 准备服务器
推荐一台 2 核 4G 以上 Linux 云服务器,并安装:
- Docker Engine
- Docker Compose v2
- Git
开放 80 端口。如果 HTTPS 由云厂商负载均衡、CDN 或宝塔/Nginx 外层托管,则在外层终止 TLS 后转发到本机 80。
### 2. 准备配置
```bash
cp .env.production.example .env.production
```
必须修改这些值:
- `POSTGRES_PASSWORD`:强密码。
- `DATABASE_URL`:密码要与 `POSTGRES_PASSWORD` 一致,主机保持 `postgres`
- `SERVER_NAME`:你的域名,例如 `pm.example.com`
- `NEXTAUTH_URL`:正式访问地址,例如 `https://pm.example.com`
- `NEXTAUTH_SECRET`:随机长密钥。
- `NEXT_PUBLIC_API_URL`:同域部署保持 `/api/v1`;如果前后端分域,填完整 API 地址。
- `ANTHROPIC_API_KEY`:可先留空或占位,后续也可以在 `/admin/ai-config` 页面配置。
### 3. 校验部署文件
```bash
pnpm deploy:verify
```
该命令会检查 `Dockerfile.web``Dockerfile.server``docker-compose.prod.yml``docker-compose.local.yml`、Nginx 模板、env 示例和本文档是否齐全。
### 4. 构建并启动
```bash
docker compose --env-file .env.production -f docker-compose.prod.yml build
docker compose --env-file .env.production -f docker-compose.prod.yml up -d
```
首次启动后执行数据库迁移:
```bash
docker compose --env-file .env.production -f docker-compose.prod.yml exec server pnpm --filter server db:deploy
```
然后检查服务状态:
```bash
docker compose --env-file .env.production -f docker-compose.prod.yml ps
curl http://localhost/api/v1/config/ai
```
## 全自动生产发布(推荐)
生产环境推荐走 `.github/workflows/deploy-production.yml`,不再要求部署侧手工执行 `docker compose build` 或手工判断是否需要 `up`
GitHub 仓库需要配置这些 Secrets
- `PROD_HOST`:生产服务器地址。
- `PROD_USER`SSH 用户。
- `PROD_SSH_KEY`SSH 私钥。
- `PROD_APP_DIR`:服务器上的项目目录。
- `GHCR_READ_TOKEN`可选。GHCR 镜像为私有包时,用于服务器 `docker login ghcr.io` 拉镜像。
服务器首次准备仍然需要完成一次:
```bash
git clone <repo-url> ftb-project-management
cd ftb-project-management
cp .env.production.example .env.production
```
然后按实际环境改好 `.env.production` 里的数据库密码、域名、`NEXTAUTH_SECRET`、AI key 等。`APP_VERSION``APP_BUILD_TIME``WEB_IMAGE``SERVER_IMAGE` 会由 GitHub Actions 在每次发布时自动更新。
之后只要代码合并或 push 到 `master`Actions 会自动执行:
1. 用当前 commit SHA 构建 `web``server` Docker 镜像。
2. 推送镜像到 GHCR镜像 tag 同时包含 commit SHA 和 `master`
3. SSH 到生产服务器,`git pull --ff-only origin master` 更新 Compose 和部署脚本。
4. 写入 `.env.production``APP_VERSION``APP_BUILD_TIME``WEB_IMAGE``SERVER_IMAGE`
5. 执行 `docker compose --env-file .env.production -f docker-compose.prod.yml pull web server` 拉取本次 SHA 镜像。
6. 启动数据库与 Redis执行 `pnpm --filter server db:deploy`
7. 执行 `docker compose --env-file .env.production -f docker-compose.prod.yml up -d --remove-orphans` 重启服务。
8. 通过 `/api/v1/health/version` 校验运行中的后端版本是否等于本次 commit SHA。
如果最后一步失败Actions 会红掉,说明“代码已合并”不等于“线上容器已更新”。本地或服务器也可以手工运行:
```bash
pnpm deploy:check-runtime http://localhost/api/v1/health/version <expected-commit-sha>
```
## Nginx 路由
`deploy/nginx/default.conf.template` 使用官方 Nginx 镜像的模板机制生成配置:
- `/api/` 转发到 `server:3001`
- `/` 转发到 `web:3000`
- `X-Forwarded-Proto``X-Forwarded-For` 等反向代理头会透传给后端
生产 Compose 默认只监听 HTTP 80。HTTPS 建议优先交给云负载均衡、CDN 或宿主机外层证书管理工具;如果要让本 Compose 内的 Nginx 直接处理 HTTPS可以在后续增加证书 volume 和 443 server block。
## 数据持久化
生产 Compose 使用三个命名 volume
- `postgres_data`PostgreSQL 数据库,包含业务主数据和 `app_data`
- `redis_data`Redis AOF 数据。
- `server_data`NestJS 写入的 AI Provider 配置文件,例如 `data/ai-config.json`
浏览器不再作为业务数据主存储。清浏览器缓存不会删除产品、项目、版本、任务、测试用例、Bug 等业务数据。
## AppData 归档与校验V2.5
V2.5 起,已迁移业务 key 的 `PUT /api/v1/data/:key` 会返回 `409 APP_DATA_WRITE_FROZEN`,读路径仍保留给历史核对和回滚。停用 AppData 写入前后都建议导出一份只读归档:
```bash
pnpm appdata:archive:export -- --out backups/appdata-archive-$(date +%Y%m%d%H%M%S).json
pnpm appdata:archive:verify -- --archive backups/appdata-archive-20260708120000.json
```
归档 JSON 包含:
- `metadata.appVersion` / `metadata.appBuildTime` / `metadata.sourceCommit`:导出时的应用版本信息。
- `keys`:本次导出的 AppData key 列表。
- `rows[].valueChecksum`:每个 key 的 JSON 内容 SHA-256。
- `metadata.payloadChecksum`:整份 key list + rows payload 的 SHA-256。
生产环境执行导出前需要确保 `DATABASE_URL` 指向当前 PostgreSQL。默认输出文件名为 `appdata-archive-<timestamp>.json`,该模式已加入 `.gitignore`;真实归档应放入服务器备份目录或对象存储,不提交到代码仓库。
## V2.5 一致性校验
完成数据库 migration、AppData 写冻结和归档导出后,建议在发布窗口内运行一致性校验:
```bash
pnpm consistency:v25 -- --url http://localhost/api/v1/consistency
```
本地开发默认检查 `http://localhost:3001/api/v1/consistency`,也可以不传 `--url`。校验内容包括 counts、分区键、孤儿引用和审计覆盖
- `error` 必须在发布前修复。
- `warn` 需要记录原因;历史数据没有审计事件属于预期 warning不阻断 V2.5。
- 后台页面 `/admin/consistency` 展示同一份报告,需要当前用户具备 `consistency:view`
## 升级流程
```bash
git pull
pnpm deploy:verify
docker compose --env-file .env.production -f docker-compose.prod.yml build
docker compose --env-file .env.production -f docker-compose.prod.yml up -d
docker compose --env-file .env.production -f docker-compose.prod.yml exec server pnpm --filter server db:deploy
```
升级前建议先备份数据库:
```bash
docker compose --env-file .env.production -f docker-compose.prod.yml exec postgres pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" > ftb_pm_backup.sql
```
## 常见排查
后端是否启动:
```bash
curl http://localhost/api/v1/config/ai
```
数据库是否可用:
```bash
docker compose --env-file .env.production -f docker-compose.prod.yml exec postgres pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
```
查看日志:
```bash
docker compose --env-file .env.production -f docker-compose.prod.yml logs -f server
docker compose --env-file .env.production -f docker-compose.prod.yml logs -f web
docker compose --env-file .env.production -f docker-compose.prod.yml logs -f nginx
```
如果前端能打开但数据为空,优先检查:
1. `NEXT_PUBLIC_API_URL` 是否为 `/api/v1` 或正确的完整 API 地址。
2. `curl http://localhost/api/v1/config/ai` 是否返回 200。
3. `server` 日志里是否有 Prisma 或 `DATABASE_URL` 错误。
4. 是否已执行 `pnpm --filter server db:deploy`