13 KiB
AI Contract Platform 生产部署指南
本文档记录了实际部署过程中遇到的问题和解决方案,避免以后部署走弯路。
部署架构
| 组件 | 部署位置 | 目录 | 端口 |
|---|---|---|---|
| 前端 Next.js | 阿里云 ECS (47.94.178.212) | /opt/apps/ai-contract-web/current |
容器内 3000 |
| 前端 Gateway | 阿里云 ECS | 同上 | 127.0.0.1:18083 |
| 后端 FastAPI | 192.168.3.90 | /opt/apps/ai-contract-platform |
8001:8000 |
| 后端 Gateway | 192.168.3.90 | /opt/apps/ai-contract-platform-gateway |
10.20.0.11:18083 |
| PostgreSQL | 192.168.3.90 | - | 5432 |
| Redis | 192.168.3.90 | - | 6379 |
| MinIO | 192.168.3.90 | - | 9000 |
域名: contact.yuqei.com
流量路径:
contact.yuqei.com → 阿里云 nginx (127.0.0.1:18083)
├── /api/ → 后端服务器 10.20.0.11:18083 → backend:8000
├── /health → 后端服务器 health check
└── 其他 → 阿里云 Next.js 容器:3000
上线稳定性基线
生产发布前先跑本地预检,再跑生产健康/发布门禁:
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\run-deployment-preflight.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\run-production-env-check.ps1 `
-Remote `
-ServerHost "192.168.3.90" `
-RemoteEnvPath "/opt/apps/ai-contract-platform/.env"
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\run-production-health-check.ps1 `
-BackendBaseUrl "http://192.168.3.90:8001" `
-FrontendBaseUrl "https://contact.yuqei.com"
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\run-production-release-gate.ps1 `
-BackendBaseUrl "http://192.168.3.90:8001" `
-FrontendBaseUrl "https://contact.yuqei.com" `
-ApiUrl "https://contact.yuqei.com/api/v1" `
-ReadinessUrl "https://contact.yuqei.com/api/v1/system/readiness"
本地预检覆盖:
backend/tests/test_system_api.py、合同台账/导出、统一搜索回归。npm --prefix frontend run lint。npm --prefix frontend run build。git diff --check。
后端健康检查分两层:
GET /health:只确认 FastAPI 进程可响应,适合容器存活探针。GET /api/v1/system/readiness:确认数据库、运行配置、对象存储、共享 AI 配置和默认管理员密码状态,适合上线门禁。
生产环境 APP_ENV=production 时,readiness 会把以下问题作为必需项失败并返回 status=degraded:
SECRET_KEY为空、过短或仍为change_me...占位值。DATABASE_URL为空或仍包含占位密码。FILE_STORAGE_BACKEND=minio时,MINIO_ENDPOINT、MINIO_BUCKET、MINIO_ACCESS_KEY、MINIO_SECRET_KEY缺失或仍为占位值;生产环境下minioadmin默认凭据也会失败。SHARED_AI_PLATFORM_LEGAL_REVIEW_ENABLED=true时,SHARED_AI_PLATFORM_BASE_URL或SHARED_AI_PLATFORM_API_KEY缺失/占位。
.env.example 只作为模板,生产 .env 必须替换所有 change_me... 值。若暂时不启用远端共享 AI 法律审查,应明确设置:
SHARED_AI_PLATFORM_LEGAL_REVIEW_ENABLED=false
远端 .env 核对脚本不会把密钥值写入报告,只保存键名清单和 pass/fail 状态。MINIO_ACCESS_KEY / MINIO_SECRET_KEY 不能继续使用 minioadmin、admin、password 这类默认弱值;替换后需要同步重启后端和 worker,再复跑 run-production-env-check.ps1 与 run-production-deployment-rehearsal.ps1。
构建注意事项(重要!)
⚠️ Git Bash 路径转换问题
问题: 在 Windows Git Bash 环境下构建 Next.js 时,环境变量 /api/v1 会被错误地转换成 Windows 路径 C:/Program Files/Git/api/v1。
解决方案: 使用 MSYS_NO_PATHCONV=1 环境变量防止路径转换:
# 正确的构建命令(在 Git Bash 中执行)
MSYS_NO_PATHCONV=1 NEXT_PUBLIC_API_BASE_URL="/api/v1" BACKEND_BASE_URL="http://10.20.0.11:18083" npm run build
错误示例:
# 这会导致 API_BASE_URL = "C:/Program Files/Git/api/v1"
NEXT_PUBLIC_API_BASE_URL=/api/v1 npm run build
# 这会导致 API_BASE_URL = "//api/v1",浏览器解析为 "http://api/v1"
NEXT_PUBLIC_API_BASE_URL="//api/v1" npm run build
Next.js 环境变量特性
关键点: NEXT_PUBLIC_* 和 BACKEND_BASE_URL 是构建时嵌入的环境变量,运行时设置无效!
NEXT_PUBLIC_API_BASE_URL- 嵌入到浏览器 JS 中,用于客户端 API 请求BACKEND_BASE_URL- 嵌入到server.js中,用于 Next.js rewrites(服务器端代理)
构建后这些值被硬编码到 JS 文件中,docker-compose 的 environment 设置无法覆盖。
部署步骤
1. 本地构建前端
cd frontend
# 使用 MSYS_NO_PATHCONV=1 防止 Git Bash 路径转换
MSYS_NO_PATHCONV=1 NEXT_PUBLIC_API_BASE_URL="/api/v1" BACKEND_BASE_URL="http://10.20.0.11:18083" npm run build
# 打包 standalone 输出
rm -rf .next/standalone/.next/static
cp -r .next/static .next/standalone/.next/
tar -czf frontend-standalone.tar.gz -C .next/standalone .
2. 上传到阿里云
scp frontend-standalone.tar.gz root@47.94.178.212:/tmp/
3. 在阿里云部署
ssh root@47.94.178.212
# 解压到 app 目录
cd /opt/apps/ai-contract-web/current/app
rm -rf .next server.js node_modules public
tar -xzf /tmp/frontend-standalone.tar.gz
# 重启容器
cd /opt/apps/ai-contract-web/current
docker compose down
docker compose up -d --build
4. 后端部署(192.168.3.90)
当前线上目录
/opt/apps/ai-contract-platform可能是发布包解压目录,不一定是 Git 工作区。 执行git pull前必须先确认git rev-parse --is-inside-work-tree返回true。
方式 A:服务器目录是 Git 工作区
ssh root@192.168.3.90
cd /opt/apps/ai-contract-platform
git rev-parse --is-inside-work-tree
git pull origin main
docker compose -f docker-compose.server.yml build backend legal-change-impact-worker
docker compose -f docker-compose.server.yml up -d backend legal-change-impact-worker
方式 B:服务器目录是发布包目录(当前线上常见形态)
此方式用于后端单独热修复或后端发布包部署。发布前先备份将被覆盖的文件,发布后只重建后端服务,避免无关前端变更进入线上。
# 本地同步修复文件示例
scp backend/vendor/shared-ai-platform-sdk/shared_ai_platform_sdk/client.py \
root@192.168.3.90:/opt/apps/ai-contract-platform/backend/vendor/shared-ai-platform-sdk/shared_ai_platform_sdk/client.py
# 服务器上重建并重启后端相关容器
ssh root@192.168.3.90
cd /opt/apps/ai-contract-platform
docker compose -f docker-compose.server.yml build backend legal-change-impact-worker
docker compose -f docker-compose.server.yml up -d backend legal-change-impact-worker
发布后检查:
docker ps --format "table {{.Names}}\t{{.Status}}" | grep ai-contract-platform
docker logs --since 5m ai-contract-platform-backend-1
配置文件
docker-compose.yml(阿里云前端)
services:
ai-contract-web:
build:
context: ./app
dockerfile: Dockerfile
image: local/ai-contract-web:0.0.1
container_name: ai-contract-web
restart: unless-stopped
environment:
NODE_ENV: production
HOSTNAME: 0.0.0.0
PORT: 3000
# 注意:以下环境变量在运行时设置无效,必须在构建时设置
# BACKEND_BASE_URL: http://10.20.0.11:18083
# NEXT_PUBLIC_API_BASE_URL: /api/v1
extra_hosts:
- "10.20.0.11:host-gateway" # 让容器能访问内网 IP
networks:
- ai-contract-web-proxy
ai-contract-web-gateway:
image: nginx:1.27-alpine
container_name: ai-contract-web-gateway
restart: unless-stopped
ports:
- "127.0.0.1:18083:80"
volumes:
- ./nginx/web-gateway.conf:/etc/nginx/conf.d/default.conf:ro
- /opt/apps/ai-contract-web/logs/nginx:/var/log/nginx
depends_on:
- ai-contract-web
networks:
- ai-contract-web-proxy
networks:
ai-contract-web-proxy:
name: ai-contract-web-proxy
nginx/web-gateway.conf(阿里云)
upstream ai_contract_web {
server ai-contract-web:3000;
}
upstream ai_contract_api {
server 10.20.0.11:18083; # 后端服务器 gateway
}
server {
listen 80;
server_name contact.yuqei.com;
charset utf-8;
client_max_body_size 100m;
# API 代理到后端服务器
location /api/ {
proxy_pass http://ai_contract_api/api/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_connect_timeout 10s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
# 前端 Next.js
location / {
proxy_pass http://ai_contract_web;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
默认账号
| 账号 | 密码 | 角色 |
|---|---|---|
| admin@example.com | 123456 | admin |
常见问题排查
1. 登录报"网络异常"
原因: NEXT_PUBLIC_API_BASE_URL 构建时值错误(如 C:/Program Files/Git/api/v1 或 //api/v1)
排查:
# 检查部署的 JS 文件中的 API_BASE_URL
curl -s 'http://contact.yuqei.com/_next/static/chunks/*.js' | grep -oP 'tl="[^"]*"'
# 正确值应为: tl="/api/v1"
# 错误值: tl="C:/Program Files/Git/api/v1" 或 tl="//api/v1"
解决: 使用 MSYS_NO_PATHCONV=1 重新构建前端
2. 容器无法访问后端
原因: 容器内无法访问内网 IP 10.20.0.11
解决: 在 docker-compose.yml 中添加 extra_hosts
3. API 请求 404
检查: nginx 是否正确代理 /api/ 到后端 gateway
# 测试 nginx gateway 代理
ssh root@47.94.178.212 "curl -s http://127.0.0.1:18083/api/v1/auth/login -X POST -H 'Content-Type: application/json' -d '{\"account\":\"admin@example.com\",\"password\":\"123456\"}'"
4. 检查后端日志
ssh root@192.168.3.90 "docker logs ai-contract-platform-backend-1 --tail 30"
5. Chat 页面历史会话 500
现象: 登录后进入 http://contact.yuqei.com/chat,页面提示 历史会话加载失败 Request failed with status code 500。
排查:
ssh root@192.168.3.90 "docker logs ai-contract-platform-backend-1 --tail 120"
如果日志出现如下错误,说明后端容器内的 vendored shared AI SDK 与业务代码配置不一致:
TypeError: SharedAIPlatformClient.__init__() got an unexpected keyword argument 'api_prefix'
解决: 发布包含 backend/vendor/shared-ai-platform-sdk/shared_ai_platform_sdk/client.py 修复的后端代码,并重建 backend 与 legal-change-impact-worker。
验证部署
# 1. 测试前端页面
curl -s -o /dev/null -w "%{http_code}" http://contact.yuqei.com/
# 2. 测试 API 登录
curl -s -X POST 'http://contact.yuqei.com/api/v1/auth/login' \
-H 'Content-Type: application/json' \
-d '{"account":"admin@example.com","password":"123456"}'
# 3. 测试后端 health check
curl -s http://10.20.0.11:18083/health
# 4. 测试生产 readiness
curl -s http://contact.yuqei.com/api/v1/system/readiness
登录态页面验证重点:
http://contact.yuqei.com/workspace可正常进入工作台http://contact.yuqei.com/contracts可加载合同列表http://contact.yuqei.com/chat不再出现历史会话 500,后端日志中/api/v1/conversations?page=1&page_size=20返回 200
目录结构
阿里云 /opt/apps/ai-contract-web/current/
├── docker-compose.yml
├── nginx/
│ └── web-gateway.conf
└── app/
├── Dockerfile
├── server.js # Next.js standalone 服务器
├── .next/
│ ├── static/ # 静态 JS/CSS
│ └── server/ # 服务端渲染代码
├── public/
├── node_modules/
└── package.json
后端服务器 /opt/apps/ai-contract-platform/
├── docker-compose.server.yml
├── .env
├── backend/
│ ├── Dockerfile
│ ├── app/
│ ├── migrations/
│ └── requirements.txt
└── frontend/ # 本地开发用,不部署
相关文档
- deploy/README.md - 部署说明
- deploy/web-gateway.conf - nginx 配置模板
- deploy/docker-compose.yml - docker-compose 模板