# 部署指南 本文档覆盖三种部署方式: - 本地开发:前端、后端直接用 pnpm 跑,PostgreSQL 和 Redis 用 `docker-compose.yml` 启动。 - 本地服务器部署:一台本机或局域网服务器用 `docker-compose.local.yml` 跑完整 Web、Server、PostgreSQL、Redis、Nginx。 - 云服务器生产:所有服务用 `docker-compose.prod.yml` 编排,Nginx 对外暴露 HTTP,业务数据写入 PostgreSQL volume,AI 配置文件写入 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 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 ``` ## 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-.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`。