Features: - PatientWechatCallbackController for URL verification and message handling - PatientWechatService for template and customer messages - Support for secure mode (message encryption/decryption) - Simplified route /wechat/patient/callback for WeChat config - Event handlers for subscribe/unsubscribe/text messages - Template message for visit reminders Technical details: - Reuse @wecom/crypto for encryption (compatible with Official Account) - Relaxed Fastify schema validation to prevent early request blocking - Access token caching (7000s with 5min pre-refresh) - Comprehensive logging for debugging Testing: Local URL verification passed, ready for SAE deployment Status: Code complete, waiting for WeChat platform configuration
40 KiB
AI临床研究平台 - 完整部署实战手册(2025版)
文档版本:v1.0(实战版)
创建日期:2025-12-25
基于实际部署:2025-12-24 ~ 2025-12-25 完整部署经历
部署成功时间:约6小时(包含问题排查)
部署人员:开发团队
部署结果:✅ 完全成功,所有功能正常运行
📋 文档说明
本文档的特点
- ✅ 基于真实部署经历:记录了2025年12月实际部署的完整过程
- ✅ 包含所有坑点:遇到的4个关键问题及解决方案
- ✅ 经验总结:提炼出的最佳实践和注意事项
- ✅ 可直接复制:所有配置和命令都经过验证
- ✅ 适合快速部署:跟着做就能成功
与其他文档的关系
| 文档名称 | 用途 | 何时使用 |
|---|---|---|
| 本文档 | 实战部署完整流程 | ⭐ 新部署或重新部署时 |
01-快速部署SOP-零基础版.md |
零基础完整指南 | 从零开始学习时 |
00-部署进度总览.md |
资源速查索引 | 查询资源信息时 |
12-Node.js后端-SAE部署操作手册.md |
单服务部署 | 只部署某个服务时 |
🎯 部署架构总览
用户浏览器
↓ HTTPS (公网)
CLB负载均衡器 (公网IP: 8.140.53.236)
↓ HTTP (内网)
前端Nginx (SAE: 172.17.173.72:80)
↓ HTTP (内网, /api/v1/)
Node.js后端 (SAE: 172.17.173.73:3001)
↓ HTTP (内网, /api/dc/)
Python服务 (SAE: 172.17.173.66:8000)
↓ SQL (内网)
RDS PostgreSQL (内网: pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432)
关键依赖:
- 所有服务在同一个VPC内网(
172.17.0.0/16) - 使用NAT网关访问外网(拉取镜像、调用LLM API)
- CLB绑定前端,提供公网访问
📊 部署顺序(严格按顺序执行)
| 步骤 | 服务 | 预计时间 | 依赖项 |
|---|---|---|---|
| 1️⃣ | VPC网络 + NAT网关 | 15分钟 | 无 |
| 2️⃣ | RDS PostgreSQL | 30分钟 | VPC |
| 3️⃣ | OSS对象存储 | 10分钟 | 无 |
| 4️⃣ | ACR容器镜像仓库 | 10分钟 | 无 |
| 5️⃣ | Python微服务镜像构建 | 20分钟 | ACR |
| 6️⃣ | Python微服务部署到SAE | 15分钟 | VPC, ACR |
| 7️⃣ | Node.js后端镜像构建 | 30分钟 | ACR, RDS |
| 8️⃣ | Node.js后端部署到SAE | 20分钟 | VPC, ACR, RDS, Python |
| 9️⃣ | 前端Nginx镜像构建 | 15分钟 | ACR |
| 🔟 | 前端Nginx部署到SAE | 15分钟 | VPC, ACR, Node.js |
| 1️⃣1️⃣ | CLB负载均衡器 | 10分钟 | 前端Nginx |
| 1️⃣2️⃣ | 全链路测试验证 | 30分钟 | 所有服务 |
总计:约3.5小时(不含问题排查)
🔑 一、前置准备(30分钟)
1.1 阿里云账号与资源
检查清单:
- 阿里云账号已实名认证
- 账户余额 ≥ 500元(建议1000元)
- 地域选择:华北2(北京)
- 已创建RAM访问密钥(用于Docker登录)
1.2 本地开发环境
必需工具:
- Docker Desktop(Windows/Mac)或 Docker Engine(Linux)
- Git(用于代码管理)
- Node.js 22+(用于本地构建)
- PowerShell 或 Bash(执行脚本)
验证命令:
docker --version # 应显示 Docker version 24.0+
git --version # 应显示 git version 2.x+
node --version # 应显示 v22.x.x
1.3 项目代码准备
# 克隆项目(如果还没有)
git clone <your-repo-url> AIclinicalresearch
cd AIclinicalresearch
# 确认项目结构
ls -la
# 应该看到:backend/, frontend-v2/, extraction_service/, docs/
🌐 二、基础设施部署(1小时)
2.1 VPC网络(15分钟)
登录VPC控制台:https://vpc.console.aliyun.com/
创建VPC
- 点击【创建专有网络】
- 配置参数:
VPC名称: ai-clinical-vpc 地域: 华北2(北京) IPv4网段: 172.17.0.0/16 - 点击【确定】
创建交换机(2个,提高可用性)
交换机1:
名称: ai-clinical-vsw-f
可用区: 华北2 可用区F
IPv4网段: 172.17.160.0/20
交换机2:
名称: ai-clinical-vsw-a
可用区: 华北2 可用区A
IPv4网段: 172.17.192.0/20
创建安全组
- 进入【安全组】页面
- 创建安全组:
名称: ai-clinical-sg 网络: ai-clinical-vpc - 配置入方向规则:
# 允许VPC内网互通 协议: 全部 端口: -1/-1 授权对象: 172.17.0.0/16 # 允许HTTPS (443) 协议: TCP 端口: 443/443 授权对象: 0.0.0.0/0 # 允许HTTP (80) 协议: TCP 端口: 80/80 授权对象: 0.0.0.0/0
2.2 NAT网关(15分钟)
用途:让VPC内的SAE应用能访问外网(拉取镜像、调用LLM API)
创建NAT网关
登录NAT网关控制台:https://vpc.console.aliyun.com/nat/
- 点击【创建NAT网关】
- 配置参数:
名称: NAT_airesearch 地域: 华北2(北京) VPC: ai-clinical-vpc 交换机: ai-clinical-vsw-f 网关类型: 增强型 付费模式: 按量付费 - 点击【立即购买】
创建并绑定EIP
- 在NAT网关详情页,点击【绑定弹性公网IP】
- 选择【购买新的EIP】:
计费方式: 按使用流量 带宽峰值: 5Mbps(测试环境够用) - 购买并绑定
配置SNAT条目(关键!)
- 进入NAT网关详情 → 【SNAT管理】
- 添加SNAT条目1:
交换机: ai-clinical-vsw-f 公网IP: 选择刚才的EIP - 添加SNAT条目2:
交换机: ai-clinical-vsw-a 公网IP: 选择刚才的EIP
验证:
- SNAT条目状态显示【可用】✅
2.3 RDS PostgreSQL(30分钟)
登录RDS控制台:https://rdsnext.console.aliyun.com/
创建实例
- 点击【创建实例】
- 配置参数:
# 基础配置 地域: 华北2(北京) 可用区: 可用区F(与交换机对应) 数据库引擎: PostgreSQL 版本: 15 系列: 高可用版 存储类型: ESSD云盘 # 规格 规格类型: 通用型 规格: 2核4GB (pg.n2.2c.1m) 存储空间: 100GB # 网络 网络类型: 专有网络 VPC: ai-clinical-vpc 交换机: ai-clinical-vsw-f # 付费 付费类型: 按量付费(测试)或 包年包月(生产) - 点击【立即购买】
创建数据库和账号
等待实例创建完成(约10分钟),然后:
-
进入实例详情 → 【账号管理】
-
创建高权限账号:
账号名称: airesearch 账号密码: Xibahe@fengzhibo117 # 改为您的强密码 账号类型: 高权限账号 -
进入【数据库管理】
-
创建数据库:
数据库名称: ai_clinical_research 字符集: UTF8 排序规则: en_US.utf8 授权账号: airesearch (读写)
配置白名单
- 进入【数据安全性】→ 【白名单设置】
- 修改默认分组:
白名单: 172.17.0.0/16 # VPC网段
配置时区(重要!)
- 进入【参数设置】
- 搜索
timezone - 修改为:
Asia/Shanghai - 点击【提交参数】
导入数据
方法1:使用本地数据库导出(推荐)
# 从本地Docker导出
docker exec ai-clinical-postgres pg_dump \
-U postgres \
-d ai_clinical_research \
--format=plain \
--no-owner \
--no-acl \
--encoding=UTF8 \
> rds_init.sql
# 临时开启RDS外网地址(仅用于导入)
# 在RDS控制台 → 数据库连接 → 申请外网地址
# 导入到RDS(需要容器内有psql客户端)
cat rds_init.sql | docker exec -i -e PGPASSWORD="Xibahe@fengzhibo117" \
ai-clinical-postgres psql \
-h pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com \
-p 5432 -U airesearch -d ai_clinical_research
# 导入完成后,立即关闭外网地址(安全)
验证:
# 连接到RDS检查
docker exec -e PGPASSWORD="Xibahe@fengzhibo117" \
ai-clinical-postgres psql \
-h pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com \
-p 5432 -U airesearch -d ai_clinical_research \
-c "SELECT nspname FROM pg_namespace WHERE nspname LIKE '%_schema' ORDER BY nspname;"
# 应该看到:asl_schema, dc_schema, platform_schema 等
2.4 OSS对象存储(10分钟)
登录OSS控制台:https://oss.console.aliyun.com/
创建Bucket
- 点击【创建Bucket】
- 配置参数:
Bucket名称: ai-clinical-research 地域: 华北2(北京) 存储类型: 标准存储 读写权限: 私有 存储冗余类型: 同城冗余存储 版本控制: 关闭 - 点击【确定】
创建RAM访问密钥
-
进入【访问控制RAM】:https://ram.console.aliyun.com/
-
创建用户:
登录名称: oss-bucket-writer 显示名称: OSS Bucket写入用户 访问方式: ✅ OpenAPI调用访问 -
保存AccessKey:
AccessKeyId: LTAI5tB2Dt3NdvBL3G7nYGv7 AccessKeySecret: 1iSN9k39RkApP93QjUhC1DcPIeMG4V⚠️ 立即复制保存,后续无法查看!
-
为用户授权:
- 选择用户 → 【添加权限】
- 选择权限:
AliyunOSSFullAccess(或自定义权限)
2.5 ACR容器镜像仓库(10分钟)
登录容器镜像服务:https://cr.console.aliyun.com/
创建命名空间
- 进入【个人实例】→ 【命名空间】
- 点击【创建命名空间】:
命名空间名称: ai-clinical
创建镜像仓库(3个)
仓库1:Python微服务
命名空间: ai-clinical
仓库名称: python-extraction
仓库类型: 私有
摘要: Python文档提取服务
仓库2:Node.js后端
命名空间: ai-clinical
仓库名称: backend-service
仓库类型: 私有
摘要: Node.js后端API服务
仓库3:前端Nginx
命名空间: ai-clinical
仓库名称: ai-clinical_frontend-nginx
仓库类型: 私有
摘要: React前端Nginx服务
设置访问凭证
在本地Docker登录ACR:
# 个人版ACR登录(公网地址)
docker login \
--username=gofeng117@163.com \
--password=fengzhibo117 \
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com
# 登录成功提示
# Login Succeeded
记录镜像地址格式:
# 公网地址(本地推送用)
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/<仓库名>:<标签>
# VPC地址(SAE拉取用)
crpi-cd5ij4pjt65mweeo-vpc.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/<仓库名>:<标签>
🐍 三、Python微服务部署(35分钟)
3.1 构建Docker镜像(20分钟)
进入目录
cd AIclinicalresearch/extraction_service
检查Dockerfile
确认Dockerfile存在且内容正确:
# 应该包含:
FROM python:3-slim
# ... 安装依赖
COPY requirements-prod.txt .
RUN pip install --no-cache-dir -r requirements-prod.txt
# ... 复制代码
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
构建镜像
# 构建镜像
docker build -t python-extraction:v1.0 .
# 等待构建完成(约10-15分钟)
# Successfully built...
# Successfully tagged python-extraction:v1.0
本地测试(可选但推荐)
# 运行容器
docker run -d -p 8000:8000 --name python-test python-extraction:v1.0
# 测试健康检查
curl http://localhost:8000/api/health
# 应该返回:
# {
# "status": "healthy",
# "checks": {...}
# }
# 停止容器
docker stop python-test
docker rm python-test
推送到ACR
# 打标签
docker tag python-extraction:v1.0 \
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/python-extraction:v1.0
# 推送镜像
docker push \
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/python-extraction:v1.0
# 等待推送完成(约5-10分钟,镜像约1.1GB)
# The push refers to repository [...]
# v1.0: digest: sha256:... size: ...
3.2 部署到SAE(15分钟)
登录SAE控制台:https://sae.console.aliyun.com/
创建命名空间(首次)
- 进入【命名空间】
- 点击【创建命名空间】:
命名空间ID: cn-beijing:test-airesearch 命名空间名称: 测试环境
创建应用
-
点击【创建应用】
-
应用基本信息:
应用名称: python-extraction-test 命名空间: cn-beijing:test-airesearch # 实例规格 CPU: 1核 内存: 2GB 实例数: 1 # 应用部署方式 部署方式: 镜像 镜像类型: 容器镜像服务企业版/个人版 -
选择镜像:
地域: 华北2(北京) 命名空间: ai-clinical 仓库: python-extraction 镜像版本: v1.0 # ⚠️ 使用VPC地址(SAE内网拉取,免流量费) 镜像地址会自动转为VPC地址: crpi-cd5ij4pjt65mweeo-vpc.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/python-extraction:v1.0 -
配置镜像访问凭证:
用户名: gofeng117@163.com 密码: fengzhibo117 -
应用配置:
# 启动命令配置 启动命令: 留空(使用Dockerfile的CMD) # 端口配置 容器端口: 8000 协议: TCP -
环境变量(可选):
LOG_LEVEL=INFO TEMP_DIR=/tmp/extraction_service -
网络配置:
VPC: ai-clinical-vpc 交换机: ai-clinical-vsw-f 安全组: ai-clinical-sg -
健康检查:
# 存活检查(Liveness) 检查方式: HTTP 检查路径: /api/health 端口: 8000 初始延迟: 30秒 检查间隔: 10秒 检查超时: 5秒 不健康阈值: 3次 # 就绪检查(Readiness) 检查方式: HTTP 检查路径: /api/health 端口: 8000 初始延迟: 10秒 -
点击【确认创建】
等待部署完成
- 查看【实例部署】页面
- 状态变为【Running】(约5-10分钟)
- 记录内网地址:
http://172.17.173.66:8000
验证部署
# 方法1:在SAE应用内查看日志
# 应该看到:
# INFO: Uvicorn running on http://0.0.0.0:8000
# INFO: Application startup complete
# 方法2:从Node.js应用测试(部署后)
curl http://172.17.173.66:8000/api/health
🟢 四、Node.js后端部署(50分钟)
4.1 准备工作(10分钟)
生成强JWT密钥
# 在PowerShell或Bash中生成
openssl rand -hex 32
# 或
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# 示例输出(使用您自己生成的):
# 8a3f9e7c2d1b5a4e6f8c9d0a3b5e7f1c2a4b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4
准备环境变量清单
创建文件 backend/.env.production(不提交到Git):
# ==================== 基础配置 ====================
NODE_ENV=production
PORT=3001
LOG_LEVEL=info
# ==================== 数据库连接 ====================
DATABASE_URL=postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432/ai_clinical_research?connection_limit=18&pool_timeout=10
# ==================== Python微服务 ⚠️ 关键!====================
EXTRACTION_SERVICE_URL=http://172.17.173.66:8000
# ==================== OSS对象存储 ====================
OSS_ACCESS_KEY_ID=LTAI5tB2Dt3NdvBL3G7nYGv7
OSS_ACCESS_KEY_SECRET=1iSN9k39RkApP93QjUhC1DcPIeMG4V
OSS_BUCKET=ai-clinical-research
OSS_ENDPOINT=oss-cn-beijing-internal.aliyuncs.com
OSS_REGION=cn-beijing
# ==================== JWT认证 ====================
JWT_SECRET=8a3f9e7c2d1b5a4e6f8c9d0a3b5e7f1c2a4b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4
JWT_EXPIRES_IN=7d
# ==================== LLM API密钥 ====================
DEEPSEEK_API_KEY=sk-7f8cc37a79fa4799860b38fc7ba2e150
DASHSCOPE_API_KEY=sk-75b4ff29a14a49e79667a331034f3298
# ==================== CloseAI配置 ====================
CLOSEAI_API_KEY=sk-cu0iepbXYGGx2jc7BqP6ogtSWmP6fk918qV3RUdtGC3Ed1po
CLOSEAI_OPENAI_BASE_URL=https://api.openai-proxy.org/v1
CLOSEAI_CLAUDE_BASE_URL=https://api.openai-proxy.org/anthropic
# ==================== Dify配置 ====================
DIFY_API_URL=http://localhost/v1
DIFY_API_KEY=dataset-mfvdiKvQ213NvxWm7RoYMN3c
# ==================== Postgres-Only架构 ====================
QUEUE_TYPE=pgboss
CACHE_TYPE=postgres
4.2 构建Docker镜像(30分钟)
方案选择
我们使用方案B:本地编译 + Docker打包(最稳定)
步骤1:本地编译TypeScript
cd AIclinicalresearch/backend
# 安装依赖
npm install
# 编译TypeScript
npm run build
# 验证编译结果
ls dist/
# 应该看到:index.js, common/, modules/ 等
步骤2:检查Dockerfile
确认Dockerfile内容正确:
FROM node:22-alpine
WORKDIR /app
# 复制package.json和package-lock.json
COPY package*.json ./
# 只安装生产依赖
RUN npm ci --only=production --ignore-scripts
# 复制编译后的代码
COPY dist ./dist
# 复制Prisma客户端
COPY node_modules/.prisma ./node_modules/.prisma
# 复制Prisma schema(用于runtime)
COPY prisma ./prisma
# ⚠️ 复制config目录(重要!)
COPY config ./config
# 暴露端口
EXPOSE 3001
# 启动命令
CMD ["node", "dist/index.js"]
⚠️ 关键修复1:必须包含 COPY config ./config,否则会报错 ENOENT: no such file or directory, open '/app/config/agents.yaml'
步骤3:构建镜像
# 构建镜像
docker build -t backend-service:v1.3 .
# 等待构建完成(约5分钟)
步骤4:本地测试(推荐)
# 运行容器(连接本地数据库测试)
docker run -d -p 3001:3001 \
-e NODE_ENV=production \
-e DATABASE_URL="postgresql://postgres:postgres@host.docker.internal:5432/ai_clinical_research" \
-e EXTRACTION_SERVICE_URL=http://host.docker.internal:8000 \
--name backend-test \
backend-service:v1.3
# 查看日志
docker logs -f backend-test
# 应该看到:
# ✅ Loaded 12 agent configurations
# ✅ 数据库连接成功
# 🚀 AI临床研究平台 - 后端服务器启动成功!
# 📍 服务地址: http://localhost:3001
# 测试健康检查
curl http://localhost:3001/health
# 停止测试容器
docker stop backend-test
docker rm backend-test
步骤5:推送到ACR
# 打标签
docker tag backend-service:v1.3 \
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.3
# 推送镜像
docker push \
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.3
# 等待推送完成(约10分钟,镜像约838MB)
4.3 部署到SAE(10分钟)
登录SAE控制台:https://sae.console.aliyun.com/
创建应用
-
点击【创建应用】
-
应用基本信息:
应用名称: nodejs-backend-test 命名空间: cn-beijing:test-airesearch # 实例规格 CPU: 1核 内存: 2GB 实例数: 1 -
选择镜像:
镜像类型: 容器镜像服务企业版/个人版 地域: 华北2(北京) 命名空间: ai-clinical 仓库: backend-service 镜像版本: v1.3 # 镜像访问凭证 用户名: gofeng117@163.com 密码: fengzhibo117 -
应用配置:
# 启动命令 启动命令: 留空(使用Dockerfile的CMD) # ⚠️ 关键:不要配置 /bin/bash,Alpine Linux没有bash! # 端口配置 容器端口: 3001 协议: TCP -
环境变量(⚠️ 非常重要!):
复制上面准备的完整环境变量清单,特别注意:
# ⚠️ 最容易出错的环境变量 EXTRACTION_SERVICE_URL=http://172.17.173.66:8000 # 不是 PYTHON_SERVICE_URL!名字必须正确! DATABASE_URL=postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432/ai_clinical_research?connection_limit=18&pool_timeout=10 # 注意:@ 要写成 %40 -
网络配置:
VPC: ai-clinical-vpc 交换机: ai-clinical-vsw-f 安全组: ai-clinical-sg -
健康检查:
# 存活检查(Liveness) 检查方式: HTTP 检查路径: /health 端口: 3001 初始延迟: 60秒 # Node.js启动较慢 检查间隔: 10秒 # 就绪检查(Readiness) 检查方式: HTTP 检查路径: /health 端口: 3001 初始延迟: 30秒 -
点击【确认创建】
等待部署完成
- 查看【实例部署】页面
- 状态变为【Running】(约10分钟)
- 记录内网地址:
http://172.17.173.73:3001
验证部署
查看日志,确认以下信息:
# ✅ 应该看到的日志:
✅ Loaded 12 agent configurations
[PgBossQueue] Using PgBossQueue (Postgres-Only架构)
[PostgresCacheAdapter] Cleanup task started
✅ 数据库连接成功!
📊 数据库版本: PostgreSQL 15.14
PythonExecutorService initialized: http://172.17.173.66:8000 # ⚠️ 确认这个!
🚀 AI临床研究平台 - 后端服务器启动成功!
📍 服务地址: http://localhost:3001
Server listening at http://172.17.173.73:3001
# ❌ 如果看到错误:
ENOENT: no such file or directory, open '/app/config/agents.yaml'
→ Dockerfile缺少 COPY config ./config
Error: unable to determine transport target for "pino-pretty"
→ src/index.ts 日志配置问题,需要条件判断环境
ReferenceError: require is not defined
→ healthCheck.ts 使用了require,需改为import
4.4 常见问题修复
问题1:config目录缺失
错误:
ENOENT: no such file or directory, open '/app/config/agents.yaml'
解决:
# 在Dockerfile中添加
COPY config ./config
问题2:pino-pretty生产环境报错
错误:
Error: unable to determine transport target for "pino-pretty"
解决:修改 src/index.ts
const fastify = Fastify({
logger: config.nodeEnv === 'production'
? { level: config.logLevel } // 生产环境:简单JSON日志
: { // 开发环境:pino-pretty
level: config.logLevel,
transport: {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname',
},
},
},
});
问题3:healthCheck require报错
错误:
ReferenceError: require is not defined in ES module
解决:修改 src/common/health/healthCheck.ts
import os from 'os'; // 添加导入
// 修改使用方式
checks.cpu = {
usage: process.cpuUsage(),
loadAverage: process.platform !== 'win32' ? os.loadavg() : 'N/A' // 使用import的os
}
问题4:bash路径错误
错误:
executable '/bin/bash' not found in $PATH
解决:
- SAE启动命令留空,使用Dockerfile的CMD
- 或使用
/bin/sh代替/bin/bash
🎨 五、前端Nginx部署(30分钟)
5.1 构建Docker镜像(15分钟)
进入目录
cd AIclinicalresearch/frontend-v2
检查关键文件
1. Dockerfile(多阶段构建):
# 第一阶段:构建React应用
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 第二阶段:Nginx托管
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/templates/nginx.conf.template
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
EXPOSE 80
ENTRYPOINT ["/docker-entrypoint.sh"]
2. docker-entrypoint.sh(动态配置):
#!/bin/bash
set -e
# ⚠️ 检查必需的环境变量
if [ -z "$BACKEND_SERVICE_HOST" ]; then
echo "❌ ERROR: BACKEND_SERVICE_HOST environment variable is required!"
exit 1
fi
if [ -z "$BACKEND_SERVICE_PORT" ]; then
export BACKEND_SERVICE_PORT=3001 # 默认值
fi
# 使用envsubst动态替换配置
envsubst '${BACKEND_SERVICE_HOST} ${BACKEND_SERVICE_PORT}' \
< /etc/nginx/templates/nginx.conf.template \
> /etc/nginx/nginx.conf
# 启动Nginx
exec nginx -g 'daemon off;'
3. nginx.conf(使用变量占位符):
upstream backend {
server ${BACKEND_SERVICE_HOST}:${BACKEND_SERVICE_PORT} fail_timeout=30s max_fails=3;
keepalive 32;
}
server {
listen 80;
root /usr/share/nginx/html;
# API反向代理
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# ...
}
# SPA路由支持
location / {
try_files $uri $uri/ /index.html;
}
}
构建镜像
# 构建镜像
docker build -t ai-clinical_frontend-nginx:v1.0 .
# 等待构建完成(约10分钟)
# 第一阶段会构建React应用,第二阶段打包到Nginx
本地测试
# 运行容器
docker run -d -p 3000:80 \
-e BACKEND_SERVICE_HOST=host.docker.internal \
-e BACKEND_SERVICE_PORT=3001 \
--name frontend-test \
ai-clinical_frontend-nginx:v1.0
# 访问前端
open http://localhost:3000
# 或
curl http://localhost:3000
# 停止容器
docker stop frontend-test
docker rm frontend-test
推送到ACR
# 打标签
docker tag ai-clinical_frontend-nginx:v1.0 \
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/ai-clinical_frontend-nginx:v1.0
# 推送镜像
docker push \
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/ai-clinical_frontend-nginx:v1.0
# 等待推送完成(约3分钟,镜像约50MB)
5.2 部署到SAE(15分钟)
登录SAE控制台:https://sae.console.aliyun.com/
创建应用
-
点击【创建应用】
-
应用基本信息:
应用名称: frontend-nginx-service 命名空间: cn-beijing:test-airesearch # 实例规格 CPU: 0.5核 内存: 1GB 实例数: 1 -
选择镜像:
镜像类型: 容器镜像服务企业版/个人版 地域: 华北2(北京) 命名空间: ai-clinical 仓库: ai-clinical_frontend-nginx 镜像版本: v1.0 # 镜像访问凭证 用户名: gofeng117@163.com 密码: fengzhibo117 -
应用配置:
# 启动命令 启动命令: 留空(使用ENTRYPOINT) # 端口配置 容器端口: 80 协议: TCP -
环境变量(⚠️ 关键配置):
BACKEND_SERVICE_HOST=172.17.173.73 # Node.js后端内网IP BACKEND_SERVICE_PORT=3001 -
网络配置:
VPC: ai-clinical-vpc 交换机: ai-clinical-vsw-f 安全组: ai-clinical-sg -
健康检查:
# 存活检查 检查方式: HTTP 检查路径: /health 端口: 80 初始延迟: 10秒 检查间隔: 10秒 # 就绪检查 检查方式: HTTP 检查路径: /health 端口: 80 初始延迟: 5秒 -
点击【确认创建】
等待部署完成
- 查看【实例部署】页面
- 状态变为【Running】(约5分钟)
- 记录内网地址:
http://172.17.173.72:80
验证部署
查看日志:
# ✅ 应该看到:
============================================
Starting Frontend Nginx Service
Backend Service: 172.17.173.73:3001
============================================
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
🌐 六、CLB负载均衡配置(10分钟)
6.1 配置公网访问
方式1:SAE自动创建CLB(推荐)
- 进入SAE应用:
frontend-nginx-service - 点击【公网访问】或【访问配置】Tab
- 点击【添加公网访问】
- 配置参数:
负载均衡类型: 公网 负载均衡产品: CLB (传统型负载均衡) 付费模式: 按量付费 # 监听配置 协议: HTTP 端口: 80 后端端口: 80 # 健康检查 健康检查: 启用 检查路径: /health - 点击【确定】
- 等待2-3分钟,自动分配公网IP
方式2:手动创建CLB
- 登录CLB控制台:https://slb.console.aliyun.com/
- 创建负载均衡实例
- 配置监听器(HTTP:80)
- 添加后端服务器(前端Nginx的内网IP)
- 配置健康检查
6.2 获取公网地址
- 在SAE应用详情页查看【公网访问地址】
- 示例:
http://8.140.53.236/ - 复制此地址用于测试
✅ 七、完整链路测试(30分钟)
7.1 基础功能测试
测试1:前端页面访问
# 浏览器访问
http://8.140.53.236/
# 应该看到:
✅ React应用正常加载
✅ 页面样式正常
✅ 没有CORS错误
测试2:健康检查
# 后端健康检查(通过前端代理)
curl http://8.140.53.236/api/v1/health
# 或
curl http://172.17.173.73:3001/health
# 应该返回:
{
"status": "ok",
"timestamp": "...",
"services": {
"database": "connected",
"cache": "available",
"queue": "running"
}
}
测试3:Python服务连接
# 直接测试Python服务
curl http://172.17.173.66:8000/api/health
# 应该返回:
{
"status": "healthy",
"checks": {
"pymupdf": {"available": true, "version": "1.26.7"},
...
}
}
7.2 业务功能测试
测试1:用户登录
- 访问 http://8.140.53.236/
- 输入用户名密码
- 点击登录
- 查看浏览器Network面板:
Request URL: http://8.140.53.236/api/v1/auth/login Status: 200 OK ✅ Response: { "token": "...", "user": {...} }
测试2:数据清洗模块(工具C)
- 进入【数据清洗】→ 【工具C】
- 上传Excel文件
- 验证上传成功
- 测试7大功能之一(如:数值映射):
- 选择列
- 配置映射规则
- 点击【执行映射】
- 查看结果
关键验证:
浏览器Network面板:
POST http://8.140.53.236/api/v1/dc/tool-c/quick-action
Status: 200 OK ✅
后端日志:
[QuickActionService] 调用重编码API: 性别
PythonExecutorService initialized: http://172.17.173.66:8000 ✅
[QuickActionService] 重编码成功 ✅
测试3:文献筛查模块(ASL)
- 进入【文献筛查】模块
- 创建项目
- 上传文献
- 执行智能筛查
- 验证结果
7.3 性能测试
响应时间检查
# 使用curl测试响应时间
curl -w "@-" -o /dev/null -s http://8.140.53.236/ <<'EOF'
time_namelookup: %{time_namelookup}\n
time_connect: %{time_connect}\n
time_appconnect: %{time_appconnect}\n
time_pretransfer: %{time_pretransfer}\n
time_redirect: %{time_redirect}\n
time_starttransfer: %{time_starttransfer}\n
----------\n
time_total: %{time_total}\n
EOF
# 期望结果:
# time_total < 1秒(首次访问)
# time_total < 0.3秒(后续访问)
📊 八、部署成果总结
8.1 已部署服务清单
| 服务 | 状态 | 内网地址 | 公网访问 |
|---|---|---|---|
| RDS PostgreSQL | ✅ | pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432 | ❌ |
| Python微服务 | ✅ | 172.17.173.66:8000 | ❌ |
| Node.js后端 | ✅ | 172.17.173.73:3001 | ❌ |
| 前端Nginx | ✅ | 172.17.173.72:80 | ✅ |
| CLB负载均衡 | ✅ | - | http://8.140.53.236/ |
8.2 关键配置速查
Python微服务环境变量
LOG_LEVEL=INFO
TEMP_DIR=/tmp/extraction_service
Node.js后端关键环境变量
NODE_ENV=production
PORT=3001
DATABASE_URL=postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432/ai_clinical_research
EXTRACTION_SERVICE_URL=http://172.17.173.66:8000 # ⚠️ 关键!不是PYTHON_SERVICE_URL
前端Nginx关键环境变量
BACKEND_SERVICE_HOST=172.17.173.73 # Node.js后端内网IP
BACKEND_SERVICE_PORT=3001
⚠️ 九、关键经验与注意事项
9.1 环境变量名必须精确
问题:工具C的7大功能全部报错
connect ECONNREFUSED 127.0.0.1:8000
原因:Node.js代码使用 EXTRACTION_SERVICE_URL,但SAE配置了 PYTHON_SERVICE_URL
解决:
# ❌ 错误
PYTHON_SERVICE_URL=http://172.17.173.66:8000
# ✅ 正确
EXTRACTION_SERVICE_URL=http://172.17.173.66:8000
教训:环境变量名必须与代码完全一致,一个字母都不能错!
9.2 "重启应用" vs "部署应用"
问题:点击"部署应用"导致IP地址变更
| 操作 | 用途 | IP是否变 | 何时使用 |
|---|---|---|---|
| 重启应用 | 重启容器 | ❌ 不会 | 修改环境变量后 |
| 部署应用 | 更新镜像 | ✅ 会变 | 更新代码/镜像时 |
教训:
- 只修改环境变量 → 用"重启应用"
- 更新代码/镜像 → 用"部署应用"
9.3 Dockerfile必须包含config目录
问题:
ENOENT: no such file or directory, open '/app/config/agents.yaml'
解决:
# ✅ 必须添加
COPY config ./config
9.4 pino-pretty仅用于开发环境
问题:
Error: unable to determine transport target for "pino-pretty"
原因:pino-pretty是devDependencies,生产环境没有安装
解决:条件判断环境
logger: config.nodeEnv === 'production'
? { level: config.logLevel }
: { level: config.logLevel, transport: { target: 'pino-pretty', ... } }
9.5 ES Module兼容性
问题:
ReferenceError: require is not defined in ES module
解决:
// ❌ 错误
require('os').loadavg()
// ✅ 正确
import os from 'os';
os.loadavg()
9.6 Alpine Linux没有bash
问题:
executable '/bin/bash' not found in $PATH
解决:
- SAE启动命令留空
- 或使用
/bin/sh代替
9.7 数据库URL中的@符号
格式:
# ✅ 正确(URL编码)
DATABASE_URL=postgresql://airesearch:Xibahe%40fengzhibo117@pgm-xxx.pg.rds.aliyuncs.com:5432/ai_clinical_research
# ❌ 错误(未编码)
DATABASE_URL=postgresql://airesearch:Xibahe@fengzhibo117@pgm-xxx.pg.rds.aliyuncs.com:5432/ai_clinical_research
规则:密码中的 @ 必须编码为 %40
9.8 使用VPC镜像地址(SAE拉取)
SAE部署时选择镜像:
# ✅ 正确(VPC地址,免流量费)
crpi-cd5ij4pjt65mweeo-vpc.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.3
# ❌ 不推荐(公网地址,收流量费)
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.3
规则:
- 本地推送镜像 → 使用公网地址
- SAE拉取镜像 → 使用VPC地址
9.9 健康检查初始延迟
不同服务的启动时间:
| 服务 | 启动时间 | 初始延迟建议 |
|---|---|---|
| Python微服务 | 10秒 | 30秒 |
| Node.js后端 | 30秒 | 60秒 |
| 前端Nginx | 5秒 | 10秒 |
教训:Node.js启动较慢,需要更长的初始延迟
9.10 前端动态配置后端地址
架构优势:
- 前端使用环境变量
BACKEND_SERVICE_HOST - 通过
envsubst在容器启动时动态注入 - 后端IP变更时,只需重启前端应用,无需重新构建镜像
关键文件:
docker-entrypoint.sh:动态替换配置nginx.conf:使用${BACKEND_SERVICE_HOST}占位符
🚀 十、未来优化建议
10.1 使用服务发现代替IP地址
当前配置(使用IP):
BACKEND_SERVICE_HOST=172.17.173.73
EXTRACTION_SERVICE_URL=http://172.17.173.66:8000
优化方案(使用服务名):
BACKEND_SERVICE_HOST=nodejs-backend-test.cn-beijing:test-airesearch.svc.cluster.local
EXTRACTION_SERVICE_URL=http://python-extraction-test.cn-beijing:test-airesearch.svc.cluster.local:8000
优点:
- ✅ IP变更不影响通信
- ✅ 自动负载均衡
- ✅ 更稳定可靠
10.2 配置HTTPS
步骤:
- 申请域名
- 申请SSL证书(阿里云免费证书或Let's Encrypt)
- 在CLB配置HTTPS监听(端口443)
- 上传SSL证书
- 配置HTTP自动跳转HTTPS
10.3 配置自动扩缩容
SAE弹性伸缩:
# 根据CPU使用率自动扩缩容
最小实例数: 1
最大实例数: 3
目标CPU使用率: 70%
适用场景:
- 流量波动大
- 高峰期需要更多实例
- 低峰期节约成本
10.4 配置日志分析
阿里云SLS(日志服务):
- 自动收集SAE日志
- 实时查询和分析
- 配置告警规则
10.5 配置监控告警
云监控配置:
CPU使用率 > 80% → 告警
内存使用率 > 80% → 告警
健康检查失败 → 告警
错误日志增多 → 告警
📝 十一、快速命令参考
11.1 Docker镜像管理
# 登录ACR
docker login --username=gofeng117@163.com \
--password=fengzhibo117 \
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com
# 构建并推送Python镜像
cd AIclinicalresearch/extraction_service
docker build -t python-extraction:v1.0 .
docker tag python-extraction:v1.0 \
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/python-extraction:v1.0
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/python-extraction:v1.0
# 构建并推送Node.js镜像
cd ../backend
npm run build
docker build -t backend-service:v1.3 .
docker tag backend-service:v1.3 \
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.3
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.3
# 构建并推送前端镜像
cd ../frontend-v2
docker build -t ai-clinical_frontend-nginx:v1.0 .
docker tag ai-clinical_frontend-nginx:v1.0 \
crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/ai-clinical_frontend-nginx:v1.0
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/ai-clinical_frontend-nginx:v1.0
11.2 数据库连接
# 连接到RDS(临时开启外网)
docker exec -e PGPASSWORD="Xibahe@fengzhibo117" \
ai-clinical-postgres psql \
-h pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com \
-p 5432 -U airesearch -d ai_clinical_research
# 查看Schema列表
docker exec -e PGPASSWORD="Xibahe@fengzhibo117" \
ai-clinical-postgres psql \
-h pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com \
-p 5432 -U airesearch -d ai_clinical_research \
-c "SELECT nspname FROM pg_namespace WHERE nspname LIKE '%_schema';"
11.3 健康检查
# Python服务
curl http://172.17.173.66:8000/api/health
# Node.js后端
curl http://172.17.173.73:3001/health
# 前端Nginx
curl http://172.17.173.72:80/health
# 公网访问
curl http://8.140.53.236/
🎉 十二、部署完成检查清单
最终验证
- ✅ Python微服务健康检查通过
- ✅ Node.js后端健康检查通过
- ✅ 前端Nginx健康检查通过
- ✅ 公网地址可以访问
- ✅ 用户可以正常登录
- ✅ 文献筛查功能正常
- ✅ 数据清洗工具C的7大功能正常
- ✅ 文件上传功能正常
- ✅ AI对话功能正常
- ✅ 没有CORS错误
- ✅ 没有502/504错误
- ✅ 响应时间 < 1秒
文档更新
- 更新
00-部署进度总览.md中的内网IP地址 - 记录公网访问地址
- 记录遇到的问题和解决方案
- 更新环境变量清单
📞 十三、技术支持
常见问题排查
问题1:工具C功能报错 "connect ECONNREFUSED 127.0.0.1:8000"
- 检查Node.js环境变量
EXTRACTION_SERVICE_URL - 确保值为
http://172.17.173.66:8000 - 重启Node.js应用
问题2:前端访问后端报502错误
- 检查前端环境变量
BACKEND_SERVICE_HOST - 确保值为Node.js后端的内网IP
- 重启前端应用
问题3:SAE应用启动失败
- 查看实例日志,找到具体错误信息
- 参考本文档第9节"关键经验与注意事项"
- 对照检查Dockerfile和环境变量
🎯 总结
恭喜!您已经完成了AI临床研究平台的完整部署!
部署成果:
- ✅ 4个服务全部成功部署(Python、Node.js、前端、数据库)
- ✅ 所有功能正常运行
- ✅ 公网可以访问
- ✅ 生产环境就绪
部署时间:
- 预计时间:3.5小时
- 实际时间:约6小时(包含问题排查)
关键经验:
- 环境变量名必须精确匹配
- 区分"重启应用"和"部署应用"
- Dockerfile必须包含所有必需文件
- 使用VPC地址拉取镜像
- 健康检查初始延迟要足够
下一步:
- 配置HTTPS
- 绑定域名
- 配置监控告警
- 优化性能
- 配置自动备份
文档维护:本文档基于2025-12-25实际部署经历编写,请根据实际情况更新
最后更新:2025-12-25
部署状态:✅ 完全成功