Major Changes: - Database: Install pg_bigm/pgvector plugins, create test database - Python service: v1.0 -> v1.1, add pymupdf4llm/openpyxl/pypandoc - Node.js backend: v1.3 -> v1.7, fix pino-pretty and ES Module imports - Frontend: v1.2 -> v1.3, skip TypeScript check for deployment - Code recovery: Restore empty files from local backup Technical Fixes: - Fix pino-pretty error in production (conditional loading) - Fix ES Module import paths (add .js extensions) - Fix OSSAdapter TypeScript errors - Update Prisma Schema (63 models, 16 schemas) - Update environment variables (DATABASE_URL, EXTRACTION_SERVICE_URL, OSS) - Remove deprecated variables (REDIS_URL, DIFY_API_URL, DIFY_API_KEY) Documentation: - Create 0126 deployment folder with 8 documents - Update database development standards v2.0 - Update SAE deployment status records Deployment Status: - PostgreSQL: ai_clinical_research_test with plugins - Python: v1.1 @ 172.17.173.84:8000 - Backend: v1.7 @ 172.17.173.89:3001 - Frontend: v1.3 @ 172.17.173.90:80 Tested: All services running successfully on SAE
1402 lines
31 KiB
Markdown
1402 lines
31 KiB
Markdown
# 前端 Nginx - SAE 部署操作手册
|
||
|
||
**文档版本**: v1.0
|
||
**创建时间**: 2025-12-23
|
||
**适用范围**: AIclinicalresearch 平台前端服务
|
||
**部署目标**: 阿里云 SAE(Serverless 应用引擎)
|
||
**镜像仓库**: 阿里云 ACR 个人版
|
||
|
||
---
|
||
|
||
## 📋 目录
|
||
|
||
1. [前置条件检查](#1-前置条件检查)
|
||
2. [镜像信息确认](#2-镜像信息确认)
|
||
3. [创建SAE应用](#3-创建sae应用)
|
||
4. [配置环境变量](#4-配置环境变量)
|
||
5. [配置健康检查](#5-配置健康检查)
|
||
6. [配置公网访问](#6-配置公网访问)
|
||
7. [部署应用](#7-部署应用)
|
||
8. [验证部署](#8-验证部署)
|
||
9. [常见问题排查](#9-常见问题排查)
|
||
10. [日常运维](#10-日常运维)
|
||
|
||
---
|
||
|
||
## 1. 前置条件检查
|
||
|
||
### ✅ **部署前必须完成的准备**
|
||
|
||
| 检查项 | 状态 | 说明 |
|
||
|--------|------|------|
|
||
| **ACR镜像已推送** | ☑️ | 镜像地址:`crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/ai-clinical_frontend-nginx:v1.0.0` |
|
||
| **后端服务已部署** | ⬜ | 需要获取后端VPC内网地址(格式:`172.17.x.x:3001`) |
|
||
| **VPC网络已创建** | ☑️ | VPC ID: `vpc-2ze055cptkew9c38w4r06` |
|
||
| **交换机已创建** | ☑️ | 交换机 ID: `vsw-2zevacop039bxrmj6yc0c` (可用区F) |
|
||
| **安全组已创建** | ☑️ | 安全组 ID: `sg-2zedk6fi8sgmmcwdu7tu` |
|
||
|
||
### 🔑 **必需的配置信息**
|
||
|
||
准备以下信息(在部署过程中需要):
|
||
|
||
```yaml
|
||
# ACR 镜像信息
|
||
ACR域名(专有网络): crpi-cd5ij4pjt65mweeo-vpc.cn-beijing.personal.cr.aliyuncs.com
|
||
命名空间: ai-clinical
|
||
镜像名称: ai-clinical_frontend-nginx
|
||
镜像版本: v1.0.0
|
||
|
||
# VPC网络信息
|
||
VPC ID: vpc-2ze055cptkew9c38w4r06
|
||
交换机ID: vsw-2zevacop039bxrmj6yc0c
|
||
安全组ID: sg-2zedk6fi8sgmmcwdu7tu
|
||
|
||
# 后端服务信息(从后端SAE应用获取)
|
||
后端内网地址: 待获取(格式:172.17.x.x)
|
||
后端端口: 3001
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 镜像信息确认
|
||
|
||
### 📦 **ACR 镜像详情**
|
||
|
||
```yaml
|
||
服务类型: 个人版(免费)
|
||
命名空间: ai-clinical
|
||
仓库名称: ai-clinical_frontend-nginx
|
||
|
||
镜像完整地址(专有网络):
|
||
crpi-cd5ij4pjt65mweeo-vpc.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/ai-clinical_frontend-nginx:v1.0.0
|
||
|
||
镜像标签:
|
||
- v1.0.0 (生产版本,digest: 68e97e28...)
|
||
- latest (指向最新版本)
|
||
|
||
镜像大小: 91.9 MB
|
||
构建时间: 2025-12-23
|
||
```
|
||
|
||
### 🔍 **验证镜像存在**
|
||
|
||
登录ACR控制台验证:https://cr.console.aliyun.com/
|
||
|
||
1. 进入【个人版】→【镜像仓库】
|
||
2. 找到 `ai-clinical_frontend-nginx`
|
||
3. 确认存在 `v1.0.0` 标签
|
||
|
||
---
|
||
|
||
## 3. 创建SAE应用
|
||
|
||
### 📍 **登录SAE控制台**
|
||
|
||
访问:https://sae.console.aliyun.com/
|
||
|
||
选择地域:**华北2(北京)**
|
||
|
||
### 🆕 **创建新应用**
|
||
|
||
点击【创建应用】,按以下步骤填写:
|
||
|
||
#### **步骤1:应用基本信息**
|
||
|
||
```yaml
|
||
应用名称: frontend-nginx-service
|
||
应用描述: AI临床研究平台 - 前端静态资源服务(React SPA + Nginx)
|
||
命名空间: 默认命名空间(或与后端同一命名空间)
|
||
```
|
||
|
||
#### **步骤2:应用部署方式**
|
||
|
||
```yaml
|
||
部署方式: 镜像
|
||
技术栈语言: 不限
|
||
```
|
||
|
||
#### **步骤3:应用实例规格**
|
||
|
||
```yaml
|
||
实例规格: 0.5核 1GB
|
||
|
||
说明:
|
||
- 前端Nginx占用资源极少
|
||
- 0.5核1GB完全够用
|
||
- 对比:后端需要2核4GB,Python需要2核4GB
|
||
|
||
实例数量: 2
|
||
|
||
说明:
|
||
- 最小2个实例保证高可用
|
||
- 1个实例故障时,另1个继续服务
|
||
- 前端很少需要扩容
|
||
```
|
||
|
||
**为什么前端只需要0.5核1GB?**
|
||
|
||
| 服务类型 | 推荐规格 | 原因 |
|
||
|---------|---------|------|
|
||
| Node.js 后端 | 2核4GB | 处理AI对话、数据库查询、业务逻辑 |
|
||
| Python 微服务 | 2核4GB | PDF提取是CPU密集型操作 |
|
||
| **Nginx 前端** | **0.5核1GB** | **仅提供静态文件,消耗极少** |
|
||
|
||
#### **步骤4:VPC网络配置**
|
||
|
||
```yaml
|
||
VPC: vpc-2ze055cptkew9c38w4r06 (172.17.0.0/16)
|
||
|
||
交换机: vsw-2zevacop039bxrmj6yc0c
|
||
可用区: 华北2可用区F
|
||
网段: 172.17.0.0/24
|
||
|
||
安全组: sg-2zedk6fi8sgmmcwdu7tu
|
||
|
||
⚠️ 重要:必须与后端服务在同一VPC,否则无法通信
|
||
```
|
||
|
||
#### **步骤5:镜像配置**
|
||
|
||
```yaml
|
||
镜像类型: 容器镜像服务个人版
|
||
|
||
镜像地址: crpi-cd5ij4pjt65mweeo-vpc.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/ai-clinical_frontend-nginx:v1.0.0
|
||
|
||
⚠️ 注意事项:
|
||
- 使用专有网络域名(带-vpc后缀),不使用公网域名
|
||
- 完整输入镜像地址(包括命名空间和标签)
|
||
- 不要遗漏版本号 :v1.0.0
|
||
|
||
镜像版本: v1.0.0
|
||
|
||
镜像拉取策略: 总是拉取镜像(Always)
|
||
|
||
说明:
|
||
- 每次部署都拉取最新镜像
|
||
- 确保使用最新代码
|
||
- 避免使用缓存的旧镜像
|
||
```
|
||
|
||
#### **步骤6:应用容器配置**
|
||
|
||
```yaml
|
||
容器端口: 80
|
||
|
||
说明:
|
||
- Nginx默认监听80端口
|
||
- 与Dockerfile中的EXPOSE 80一致
|
||
|
||
协议: TCP
|
||
|
||
启动命令:
|
||
- 不填(使用Dockerfile中的ENTRYPOINT)
|
||
|
||
工作目录:
|
||
- 不填(使用Dockerfile中的WORKDIR)
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 配置环境变量
|
||
|
||
### ⚠️ **极其重要:后端服务地址配置**
|
||
|
||
环境变量配置错误是最常见的部署失败原因!
|
||
|
||
### 📍 **获取后端内网地址**
|
||
|
||
**在配置前端之前,必须先获取后端的VPC内网地址:**
|
||
|
||
1. 登录 SAE 控制台
|
||
2. 进入应用列表
|
||
3. 找到 `nodejs-backend-service`(你的后端应用名称)
|
||
4. 点击进入应用详情
|
||
5. 查看【应用访问配置】→【VPC内网访问地址】
|
||
|
||
示例格式:
|
||
```
|
||
172.17.0.15:3001
|
||
```
|
||
|
||
将IP地址和端口分别记录下来。
|
||
|
||
### 🔧 **配置环境变量**
|
||
|
||
在SAE应用配置页面,找到【环境变量】配置项:
|
||
|
||
```yaml
|
||
# 必需配置(必填)
|
||
BACKEND_SERVICE_HOST: 172.17.0.15
|
||
|
||
说明:
|
||
- 这是后端服务的VPC内网IP地址(从上一步获取)
|
||
- 不要加http://前缀
|
||
- 不要包含端口号
|
||
- 如果不配置,容器启动会失败并报错
|
||
|
||
# 可选配置(有默认值)
|
||
BACKEND_SERVICE_PORT: 3001
|
||
|
||
说明:
|
||
- 后端服务的端口号
|
||
- 默认值是3001
|
||
- 如果你的后端使用其他端口,需要修改
|
||
```
|
||
|
||
### ❌ **常见错误**
|
||
|
||
| 错误配置 | 正确配置 | 说明 |
|
||
|---------|---------|------|
|
||
| `http://172.17.0.15` | `172.17.0.15` | 不要加协议前缀 |
|
||
| `172.17.0.15:3001` | `172.17.0.15` | 不要包含端口(端口单独配置) |
|
||
| `backend-service` | `172.17.0.15` | 必须使用IP,不要用主机名 |
|
||
| `localhost` | `172.17.0.15` | 不能用localhost |
|
||
| 不配置 | `172.17.0.15` | 必须配置,否则启动失败 |
|
||
|
||
### ✅ **配置验证**
|
||
|
||
配置完成后,点击【验证】,确保:
|
||
- ✅ 变量名称拼写正确(区分大小写)
|
||
- ✅ 变量值格式正确(纯IP地址)
|
||
- ✅ 没有多余的空格
|
||
|
||
---
|
||
|
||
## 5. 配置健康检查
|
||
|
||
### 🏥 **健康检查配置**
|
||
|
||
```yaml
|
||
检查方式: HTTP 请求
|
||
|
||
检查路径: /health
|
||
|
||
说明:
|
||
- Nginx配置中专门提供的健康检查端点
|
||
- 返回纯文本 "healthy"
|
||
- 不依赖后端服务
|
||
|
||
检查端口: 80
|
||
说明:与容器端口一致
|
||
|
||
检查协议: HTTP
|
||
说明:不是HTTPS
|
||
|
||
超时时间: 3秒
|
||
说明:静态端点,3秒足够
|
||
|
||
检查间隔: 10秒
|
||
说明:每10秒检查一次
|
||
|
||
不健康阈值: 3次
|
||
说明:连续失败3次后标记为不健康
|
||
|
||
健康阈值: 2次
|
||
说明:连续成功2次后标记为健康
|
||
|
||
初始延迟: 30秒
|
||
说明:
|
||
- Nginx启动很快(5-10秒)
|
||
- 30秒足够镜像拉取和容器启动
|
||
```
|
||
|
||
### 🔍 **健康检查原理**
|
||
|
||
```
|
||
SAE健康检查器
|
||
↓ (每10秒)
|
||
curl http://容器IP:80/health
|
||
↓
|
||
Nginx处理 /health 请求
|
||
↓
|
||
返回: HTTP 200 + "healthy"
|
||
↓
|
||
SAE标记实例为健康 ✅
|
||
```
|
||
|
||
**如果健康检查失败,SAE会:**
|
||
1. 标记实例为不健康
|
||
2. 停止向该实例转发流量
|
||
3. 连续失败3次后,重启容器
|
||
|
||
---
|
||
|
||
## 6. 配置公网访问
|
||
|
||
### 🌐 **访问方式选择**
|
||
|
||
#### **方案A:使用SAE提供的公网地址(推荐用于测试)**
|
||
|
||
```yaml
|
||
配置方式:
|
||
1. 在应用配置页面找到【应用访问配置】
|
||
2. 开启【公网访问】
|
||
3. SAE自动分配域名
|
||
|
||
获得的访问地址:
|
||
格式: frontend-nginx-service-xxxxx.cn-beijing.sae.aliyuncs.com
|
||
协议: HTTPS(SAE自动配置证书)
|
||
|
||
优点:
|
||
- ✅ 免费
|
||
- ✅ 自动HTTPS
|
||
- ✅ 立即可用
|
||
|
||
缺点:
|
||
- ❌ 域名难记
|
||
- ❌ 无法自定义域名
|
||
- ❌ 证书是SAE的,不是你的域名
|
||
```
|
||
|
||
#### **方案B:绑定自定义域名(推荐用于生产)**
|
||
|
||
```yaml
|
||
前置条件:
|
||
- 已有自己的域名(如 app.yizheng.ai)
|
||
- 域名已备案(如果服务器在中国大陆)
|
||
- 有SSL证书(可以使用免费的Let's Encrypt)
|
||
|
||
配置步骤:
|
||
1. 在域名服务商添加CNAME解析
|
||
app.yizheng.ai → frontend-nginx-service-xxxxx.cn-beijing.sae.aliyuncs.com
|
||
|
||
2. 在SAE控制台绑定域名
|
||
【应用访问配置】→【自定义域名】→【添加域名】
|
||
|
||
3. 上传SSL证书
|
||
【应用访问配置】→【HTTPS设置】→【上传证书】
|
||
|
||
4. 启用强制HTTPS
|
||
开启【强制HTTPS跳转】
|
||
|
||
获得的访问地址:
|
||
https://app.yizheng.ai
|
||
|
||
优点:
|
||
- ✅ 品牌域名
|
||
- ✅ 用户信任度高
|
||
- ✅ SEO友好
|
||
|
||
缺点:
|
||
- ⚠️ 需要域名和证书
|
||
- ⚠️ 需要ICP备案(如果在中国)
|
||
```
|
||
|
||
### 🔒 **HTTPS配置(生产环境必须)**
|
||
|
||
如果使用方案B(自定义域名),强烈建议配置HTTPS:
|
||
|
||
```yaml
|
||
证书来源选择:
|
||
|
||
方案1: 使用免费的Let's Encrypt证书
|
||
- 阿里云SSL证书服务可以申请免费证书
|
||
- 有效期3个月,需要定期更新
|
||
|
||
方案2: 购买商业证书
|
||
- 有效期1年
|
||
- 品牌信任度更高
|
||
- 支持更多特性
|
||
|
||
证书配置:
|
||
1. 准备证书文件
|
||
- 证书文件:domain.crt 或 domain.pem
|
||
- 私钥文件:domain.key
|
||
|
||
2. 在SAE上传证书
|
||
【应用访问配置】→【HTTPS设置】→【上传证书】
|
||
|
||
3. 启用强制HTTPS
|
||
开启【HTTP自动跳转HTTPS】
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 部署应用
|
||
|
||
### 🚀 **执行部署**
|
||
|
||
确认所有配置无误后:
|
||
|
||
1. 回到应用配置页面顶部
|
||
2. 点击【部署】按钮
|
||
3. 确认部署信息
|
||
4. 点击【确定】
|
||
|
||
### ⏱️ **部署过程(约2-3分钟)**
|
||
|
||
```
|
||
1. 镜像拉取阶段 (30-60秒)
|
||
├─ SAE连接到ACR
|
||
├─ 验证镜像存在
|
||
├─ 下载镜像层(91.9MB)
|
||
└─ 解压镜像
|
||
|
||
2. 容器启动阶段 (10-20秒)
|
||
├─ 创建容器
|
||
├─ 注入环境变量
|
||
├─ 执行docker-entrypoint.sh
|
||
├─ envsubst替换nginx.conf
|
||
├─ 验证Nginx配置
|
||
└─ 启动Nginx进程
|
||
|
||
3. 健康检查阶段 (30-60秒)
|
||
├─ 等待30秒(初始延迟)
|
||
├─ 第1次检查 /health
|
||
├─ 第2次检查 /health(10秒后)
|
||
└─ 标记为健康 ✅
|
||
|
||
4. 流量切换阶段 (5-10秒)
|
||
├─ 将新实例加入负载均衡
|
||
├─ 从负载均衡移除旧实例
|
||
└─ 部署完成 ✅
|
||
```
|
||
|
||
### 📊 **部署状态监控**
|
||
|
||
部署过程中可以实时查看:
|
||
|
||
```yaml
|
||
查看方式:
|
||
【应用详情】→【实例列表】→【查看实例状态】
|
||
|
||
状态说明:
|
||
- 创建中: 正在拉取镜像
|
||
- 启动中: 正在启动容器
|
||
- 运行中: 容器已启动,等待健康检查
|
||
- 健康: 健康检查通过,可以接收流量 ✅
|
||
- 不健康: 健康检查失败 ❌
|
||
- 停止: 实例已停止
|
||
```
|
||
|
||
### ❌ **部署失败常见原因**
|
||
|
||
| 失败阶段 | 错误信息 | 解决方法 |
|
||
|---------|---------|---------|
|
||
| 镜像拉取 | `image not found` | 检查镜像地址是否正确,确认使用-vpc域名 |
|
||
| 镜像拉取 | `unauthorized` | 检查ACR访问权限,确认SAE有拉取权限 |
|
||
| 容器启动 | `BACKEND_SERVICE_HOST required` | 检查环境变量是否配置 |
|
||
| 健康检查 | `connection refused` | 检查容器端口配置(应该是80) |
|
||
| 健康检查 | `404 not found` | 检查健康检查路径(应该是/health) |
|
||
|
||
---
|
||
|
||
## 8. 验证部署
|
||
|
||
### ✅ **验证清单**
|
||
|
||
部署成功后,按以下步骤验证:
|
||
|
||
#### **步骤1:查看实例状态**
|
||
|
||
```bash
|
||
位置: 【应用详情】→【实例列表】
|
||
|
||
检查项:
|
||
☑️ 实例数量: 2个实例
|
||
☑️ 实例状态: 全部显示"健康"
|
||
☑️ 健康检查: 全部通过
|
||
```
|
||
|
||
#### **步骤2:查看启动日志**
|
||
|
||
```bash
|
||
位置: 【应用详情】→【日志】→【实时日志】
|
||
|
||
应该看到:
|
||
============================================
|
||
Starting Frontend Nginx Service
|
||
Backend Service: 172.17.0.15:3001
|
||
Container Timezone: Asia/Shanghai
|
||
Current Time: Tue Dec 23 21:07:35 CST 2025
|
||
============================================
|
||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||
|
||
✅ 如果看到以上日志,说明配置正确
|
||
❌ 如果看到 "BACKEND_SERVICE_HOST required",说明环境变量未配置
|
||
```
|
||
|
||
#### **步骤3:测试健康检查端点**
|
||
|
||
```bash
|
||
# 获取公网访问地址
|
||
公网地址: https://frontend-nginx-service-xxxxx.cn-beijing.sae.aliyuncs.com
|
||
|
||
# 测试健康检查(使用浏览器或curl)
|
||
访问: https://frontend-nginx-service-xxxxx.cn-beijing.sae.aliyuncs.com/health
|
||
|
||
预期返回:
|
||
Status: 200 OK
|
||
Content: healthy
|
||
|
||
✅ 如果返回 "healthy",说明前端服务正常
|
||
```
|
||
|
||
#### **步骤4:测试主页访问**
|
||
|
||
```bash
|
||
# 访问主页
|
||
访问: https://frontend-nginx-service-xxxxx.cn-beijing.sae.aliyuncs.com/
|
||
|
||
检查项:
|
||
☑️ 页面能正常加载(不是空白页)
|
||
☑️ 看到React应用界面
|
||
☑️ CSS样式正确显示
|
||
☑️ JavaScript正常执行
|
||
```
|
||
|
||
#### **步骤5:测试SPA路由**
|
||
|
||
```bash
|
||
# 访问一个不存在的路径(测试React Router)
|
||
访问: https://frontend-nginx-service-xxxxx.cn-beijing.sae.aliyuncs.com/dashboard
|
||
|
||
预期行为:
|
||
✅ 不报404错误
|
||
✅ 返回index.html
|
||
✅ React Router接管路由
|
||
✅ 显示对应的页面组件
|
||
|
||
❌ 如果看到Nginx 404页面,说明nginx.conf配置有问题
|
||
```
|
||
|
||
#### **步骤6:测试API代理(关键)**
|
||
|
||
```bash
|
||
# 打开浏览器开发者工具(F12)→ Console标签
|
||
|
||
# 执行以下代码测试API代理
|
||
fetch('/api/v1/health')
|
||
.then(res => res.json())
|
||
.then(data => console.log(data))
|
||
|
||
预期结果:
|
||
✅ 请求成功(Status 200)
|
||
✅ 返回后端的响应数据
|
||
✅ 没有CORS错误
|
||
|
||
❌ 如果看到CORS错误,说明反向代理配置有问题
|
||
❌ 如果看到连接失败,说明后端地址配置错误
|
||
```
|
||
|
||
#### **步骤7:测试静态资源缓存**
|
||
|
||
```bash
|
||
# 在浏览器开发者工具 → Network标签
|
||
|
||
# 第一次访问
|
||
✅ index.html: 状态200,Cache-Control: no-cache
|
||
✅ index-xxxxx.js: 状态200,Cache-Control: 1年
|
||
|
||
# 刷新页面(第二次访问)
|
||
✅ index.html: 状态200(每次都重新获取)
|
||
✅ index-xxxxx.js: 状态200 (from disk cache)(使用缓存)
|
||
```
|
||
|
||
#### **步骤8:测试Gzip压缩**
|
||
|
||
```bash
|
||
# 在浏览器开发者工具 → Network标签
|
||
# 点击任意JS文件
|
||
|
||
检查Response Headers:
|
||
✅ Content-Encoding: gzip
|
||
✅ 文件大小减少60-70%
|
||
|
||
示例:
|
||
原始大小: 1.2 MB
|
||
传输大小: 350 KB (gzip压缩后)
|
||
```
|
||
|
||
### 🎉 **部署成功标志**
|
||
|
||
如果以上8个步骤全部通过,恭喜!前端服务部署成功!
|
||
|
||
---
|
||
|
||
## 9. 常见问题排查
|
||
|
||
### ❌ **问题1:实例健康检查失败**
|
||
|
||
**症状:**
|
||
```
|
||
实例状态显示"不健康"
|
||
健康检查失败
|
||
```
|
||
|
||
**排查步骤:**
|
||
|
||
```bash
|
||
1. 查看实例日志
|
||
【应用详情】→【日志】→【实时日志】→【选择实例】
|
||
|
||
如果看到:
|
||
- "BACKEND_SERVICE_HOST required" → 环境变量未配置
|
||
- "nginx: configuration file test failed" → nginx.conf语法错误
|
||
- 没有日志 → 容器没有启动
|
||
|
||
2. 检查健康检查配置
|
||
【应用配置】→【健康检查】
|
||
|
||
确认:
|
||
- 检查路径: /health(不是 /)
|
||
- 检查端口: 80(不是3000或其他)
|
||
- 检查协议: HTTP(不是HTTPS)
|
||
|
||
3. 手动测试健康检查
|
||
在SAE Webshell中执行:
|
||
curl http://localhost/health
|
||
|
||
预期返回: healthy
|
||
```
|
||
|
||
**解决方法:**
|
||
- 配置缺失的环境变量
|
||
- 修正健康检查路径
|
||
- 检查容器端口配置
|
||
|
||
---
|
||
|
||
### ❌ **问题2:页面能访问,但API请求失败**
|
||
|
||
**症状:**
|
||
```
|
||
前端页面正常显示
|
||
调用API时报错: Network Error 或 504 Gateway Timeout
|
||
```
|
||
|
||
**排查步骤:**
|
||
|
||
```bash
|
||
1. 检查浏览器Console
|
||
看到的错误:
|
||
- "ERR_CONNECTION_REFUSED" → 后端服务未启动
|
||
- "504 Gateway Timeout" → 后端响应超时
|
||
- "502 Bad Gateway" → 后端地址错误
|
||
|
||
2. 检查环境变量配置
|
||
【应用配置】→【环境变量】
|
||
|
||
验证:
|
||
- BACKEND_SERVICE_HOST 是否配置
|
||
- IP地址是否正确(从后端SAE应用获取)
|
||
- 不要有http://前缀
|
||
- 不要包含端口号
|
||
|
||
3. 测试后端服务可达性
|
||
在前端容器的Webshell中:
|
||
curl http://172.17.0.15:3001/api/v1/health
|
||
|
||
如果失败:
|
||
- 检查后端服务是否运行
|
||
- 检查VPC网络是否相同
|
||
- 检查安全组规则
|
||
|
||
4. 查看Nginx错误日志
|
||
docker logs <container_id> 2>&1 | grep error
|
||
|
||
常见错误:
|
||
- "upstream: "http://172.17.0.15:3001/..." → 后端地址正确
|
||
- "connect() failed" → 后端不可达
|
||
```
|
||
|
||
**解决方法:**
|
||
- 修正环境变量BACKEND_SERVICE_HOST
|
||
- 确保前后端在同一VPC
|
||
- 检查安全组规则允许VPC内网访问
|
||
- 重启前端应用使配置生效
|
||
|
||
---
|
||
|
||
### ❌ **问题3:刷新页面报404**
|
||
|
||
**症状:**
|
||
```
|
||
访问主页正常
|
||
点击链接跳转正常
|
||
刷新页面(F5)→ 404 Not Found
|
||
```
|
||
|
||
**原因:**
|
||
|
||
React Router使用浏览器路由(BrowserRouter),刷新时Nginx需要将所有路由回退到index.html。
|
||
|
||
**排查步骤:**
|
||
|
||
```bash
|
||
1. 访问一个React路由
|
||
例如: https://your-domain.com/dashboard
|
||
|
||
2. 按F5刷新页面
|
||
|
||
❌ 错误现象: 看到Nginx 404页面
|
||
✅ 正确现象: 页面正常显示
|
||
|
||
3. 检查nginx.conf配置
|
||
登录容器Webshell:
|
||
cat /etc/nginx/nginx.conf | grep "try_files"
|
||
|
||
应该看到:
|
||
try_files $uri $uri/ /index.html;
|
||
```
|
||
|
||
**解决方法:**
|
||
|
||
如果nginx.conf中没有`try_files`配置,需要重新构建镜像:
|
||
|
||
```nginx
|
||
# 在nginx.conf中确保有以下配置
|
||
location / {
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### ❌ **问题4:静态资源404**
|
||
|
||
**症状:**
|
||
```
|
||
HTML能加载
|
||
JS/CSS文件404 Not Found
|
||
页面显示为纯文本(无样式)
|
||
```
|
||
|
||
**排查步骤:**
|
||
|
||
```bash
|
||
1. 检查浏览器Network标签
|
||
看到:
|
||
GET https://your-domain.com/assets/index-xxxxx.js 404
|
||
|
||
2. 检查index.html中的资源路径
|
||
curl https://your-domain.com/ | grep assets
|
||
|
||
正确示例:
|
||
<script src="/assets/index-xxxxx.js"></script>
|
||
|
||
错误示例:
|
||
<script src="./assets/index-xxxxx.js"></script> # 相对路径
|
||
<script src="/app/assets/index-xxxxx.js"></script> # 错误base
|
||
|
||
3. 检查Vite配置
|
||
查看frontend-v2/vite.config.ts:
|
||
|
||
正确配置:
|
||
export default defineConfig({
|
||
base: '/', // ✅ 默认值
|
||
})
|
||
|
||
错误配置:
|
||
export default defineConfig({
|
||
base: '/app/', // ❌ 除非确实需要
|
||
})
|
||
```
|
||
|
||
**解决方法:**
|
||
|
||
1. 修改 `vite.config.ts`,确保 `base: '/'`
|
||
2. 重新构建前端:`npm run build`
|
||
3. 重新构建Docker镜像
|
||
4. 推送到ACR
|
||
5. SAE重新部署
|
||
|
||
---
|
||
|
||
### ❌ **问题5:环境变量未生效**
|
||
|
||
**症状:**
|
||
```
|
||
配置了环境变量
|
||
但容器启动日志显示变量为空
|
||
```
|
||
|
||
**排查步骤:**
|
||
|
||
```bash
|
||
1. 查看容器启动日志
|
||
【应用详情】→【日志】→【实时日志】
|
||
|
||
看到:
|
||
Backend Service: ${BACKEND_SERVICE_HOST}:${BACKEND_SERVICE_PORT}
|
||
|
||
❌ 这说明envsubst没有替换变量
|
||
|
||
2. 进入容器Webshell检查
|
||
echo $BACKEND_SERVICE_HOST
|
||
|
||
如果为空 → SAE环境变量未传入
|
||
如果有值 → envsubst执行失败
|
||
|
||
3. 检查docker-entrypoint.sh
|
||
cat /docker-entrypoint.sh
|
||
|
||
确认有:
|
||
envsubst '${BACKEND_SERVICE_HOST} ${BACKEND_SERVICE_PORT}' \
|
||
< /etc/nginx/templates/nginx.conf.template \
|
||
> /etc/nginx/nginx.conf
|
||
```
|
||
|
||
**解决方法:**
|
||
|
||
1. 在SAE控制台重新配置环境变量
|
||
2. 确保变量名拼写正确(区分大小写)
|
||
3. 重启应用使配置生效
|
||
|
||
---
|
||
|
||
## 10. 日常运维
|
||
|
||
### 📊 **监控指标**
|
||
|
||
#### **实时监控**
|
||
|
||
```yaml
|
||
查看位置: 【应用详情】→【监控】
|
||
|
||
关键指标:
|
||
CPU使用率:
|
||
健康范围: < 30%
|
||
告警阈值: > 60%
|
||
说明: Nginx极少超过30%
|
||
|
||
内存使用率:
|
||
健康范围: < 50%
|
||
告警阈值: > 80%
|
||
说明: Nginx内存占用很低
|
||
|
||
请求QPS:
|
||
观察访问量趋势
|
||
|
||
平均响应时间:
|
||
健康范围: < 50ms
|
||
告警阈值: > 200ms
|
||
说明: 静态文件响应极快
|
||
|
||
错误率:
|
||
健康范围: < 0.1%
|
||
告警阈值: > 1%
|
||
```
|
||
|
||
#### **日志查看**
|
||
|
||
```yaml
|
||
实时日志:
|
||
位置: 【应用详情】→【日志】→【实时日志】
|
||
|
||
正常日志示例:
|
||
172.17.0.10 - - [23/Dec/2025:10:30:00 +0800] "GET / HTTP/1.1" 200 1234
|
||
172.17.0.10 - - [23/Dec/2025:10:30:01 +0800] "GET /assets/index.js HTTP/1.1" 200 567890
|
||
|
||
API代理日志:
|
||
172.17.0.10 - - [23/Dec/2025:10:30:02 +0800] "GET /api/v1/projects HTTP/1.1" 200 8765
|
||
|
||
错误日志:
|
||
2025/12/23 10:30:04 [error] connect() failed (111: Connection refused)
|
||
→ 后端服务连接失败
|
||
|
||
历史日志:
|
||
位置: 【应用详情】→【日志】→【历史日志】
|
||
保留时间: 7天
|
||
```
|
||
|
||
---
|
||
|
||
### 🔄 **版本更新流程**
|
||
|
||
当前端代码有更新时,按以下流程部署新版本:
|
||
|
||
#### **步骤1:本地构建新版本**
|
||
|
||
```bash
|
||
# 在frontend-v2目录
|
||
cd AIclinicalresearch/frontend-v2
|
||
|
||
# 测试构建
|
||
npm run build
|
||
|
||
# 构建Docker镜像(版本号+1)
|
||
docker build -t frontend-service:v1.0.1 .
|
||
|
||
# 验证镜像大小
|
||
docker images frontend-service:v1.0.1
|
||
```
|
||
|
||
#### **步骤2:推送到ACR**
|
||
|
||
```bash
|
||
# 登录ACR
|
||
echo fengzhibo117 | docker login --username=gofeng117@163.com --password-stdin crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com
|
||
|
||
# 打标签
|
||
docker tag frontend-service:v1.0.1 crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/ai-clinical_frontend-nginx:v1.0.1
|
||
|
||
docker tag frontend-service:v1.0.1 crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/ai-clinical_frontend-nginx:latest
|
||
|
||
# 推送镜像
|
||
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/ai-clinical_frontend-nginx:v1.0.1
|
||
|
||
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/ai-clinical_frontend-nginx:latest
|
||
```
|
||
|
||
#### **步骤3:SAE灰度发布(推荐)**
|
||
|
||
```yaml
|
||
目的: 降低更新风险,先更新部分实例验证
|
||
|
||
操作步骤:
|
||
1. 登录SAE控制台 → 进入应用详情
|
||
|
||
2. 点击【部署】→【灰度发布】
|
||
|
||
3. 配置灰度策略:
|
||
灰度实例数: 1
|
||
灰度时长: 5分钟
|
||
镜像版本: v1.0.1
|
||
|
||
4. 点击【确定】
|
||
|
||
5. 观察灰度实例:
|
||
- 查看实例状态(健康)
|
||
- 查看错误日志(无错误)
|
||
- 访问测试(功能正常)
|
||
|
||
6. 确认无问题后,点击【全量发布】
|
||
|
||
7. 如果发现问题,点击【回滚】
|
||
- 自动回滚到上一个版本
|
||
- 无需手动操作
|
||
```
|
||
|
||
#### **步骤4:全量发布**
|
||
|
||
如果不使用灰度发布,直接全量更新:
|
||
|
||
```yaml
|
||
操作步骤:
|
||
1. 登录SAE控制台 → 进入应用详情
|
||
|
||
2. 点击【部署】
|
||
|
||
3. 修改镜像版本:
|
||
从: v1.0.0
|
||
到: v1.0.1
|
||
|
||
4. 点击【确定】
|
||
|
||
5. 等待部署完成(2-3分钟)
|
||
|
||
6. 验证新版本:
|
||
- 查看实例状态
|
||
- 访问前端页面
|
||
- 测试核心功能
|
||
```
|
||
|
||
---
|
||
|
||
### 🔙 **回滚操作**
|
||
|
||
如果新版本出现问题,可以快速回滚:
|
||
|
||
```yaml
|
||
操作步骤:
|
||
1. 登录SAE控制台 → 进入应用详情
|
||
|
||
2. 点击【变更记录】
|
||
|
||
3. 找到上一个稳定版本(如v1.0.0)
|
||
|
||
4. 点击【回滚】
|
||
|
||
5. 确认回滚
|
||
|
||
6. 等待回滚完成(1-2分钟)
|
||
|
||
回滚时间:
|
||
- 镜像已缓存: 1-2分钟
|
||
- 镜像未缓存: 2-3分钟
|
||
```
|
||
|
||
---
|
||
|
||
### 🗑️ **清理旧版本镜像**
|
||
|
||
定期清理ACR中的旧版本镜像,节省存储空间:
|
||
|
||
```yaml
|
||
清理策略:
|
||
- 保留最近3个版本
|
||
- 删除30天前的旧版本
|
||
- 保留带有特殊标签的版本(如stable、prod)
|
||
|
||
操作步骤:
|
||
1. 登录ACR控制台
|
||
|
||
2. 进入镜像仓库 → ai-clinical_frontend-nginx
|
||
|
||
3. 查看所有版本
|
||
|
||
4. 选择要删除的版本(勾选)
|
||
|
||
5. 点击【删除】
|
||
|
||
6. 确认删除
|
||
|
||
⚠️ 注意:
|
||
- 不要删除正在使用的版本
|
||
- 删除前确认SAE应用已更新到新版本
|
||
- 建议至少保留2个版本以便回滚
|
||
```
|
||
|
||
---
|
||
|
||
### 📈 **弹性伸缩配置**
|
||
|
||
前端Nginx服务通常不需要频繁扩缩容,但可以配置自动伸缩:
|
||
|
||
```yaml
|
||
配置位置: 【应用配置】→【弹性伸缩】
|
||
|
||
最小实例数: 2
|
||
说明: 保证高可用,至少2个实例
|
||
|
||
最大实例数: 5
|
||
说明: 前端流量波动通常不大,5个实例足够
|
||
|
||
扩容策略:
|
||
指标: CPU使用率
|
||
阈值: > 60% 持续 3分钟
|
||
每次扩容: +1个实例
|
||
冷却时间: 3分钟
|
||
|
||
说明: Nginx很少达到60% CPU
|
||
|
||
缩容策略:
|
||
指标: CPU使用率
|
||
阈值: < 20% 持续 5分钟
|
||
每次缩容: -1个实例
|
||
冷却时间: 5分钟
|
||
最小保留: 2个实例
|
||
|
||
⚠️ 实际情况:
|
||
前端Nginx性能极高,2个0.5核实例可以支撑:
|
||
- 1000+ QPS(静态文件)
|
||
- 100+ QPS(API代理)
|
||
- 几乎不需要扩容
|
||
```
|
||
|
||
---
|
||
|
||
### 🚨 **告警配置**
|
||
|
||
配置关键指标告警,及时发现问题:
|
||
|
||
```yaml
|
||
配置位置: 【云监控】→【应用监控】→【创建告警规则】
|
||
|
||
推荐告警规则:
|
||
|
||
1. 实例健康检查失败
|
||
- 条件: 健康检查失败 > 3次
|
||
- 级别: 紧急
|
||
- 通知: 短信 + 钉钉
|
||
|
||
2. CPU使用率过高
|
||
- 条件: CPU > 60% 持续 5分钟
|
||
- 级别: 警告
|
||
- 通知: 钉钉 + 邮件
|
||
|
||
3. 内存使用率过高
|
||
- 条件: 内存 > 80% 持续 5分钟
|
||
- 级别: 警告
|
||
- 通知: 钉钉 + 邮件
|
||
|
||
4. 错误率过高
|
||
- 条件: 错误率 > 1% 持续 3分钟
|
||
- 级别: 紧急
|
||
- 通知: 短信 + 钉钉
|
||
|
||
5. 平均响应时间过长
|
||
- 条件: 响应时间 > 500ms 持续 5分钟
|
||
- 级别: 警告
|
||
- 通知: 钉钉 + 邮件
|
||
|
||
通知方式配置:
|
||
- 钉钉机器人: 创建群聊机器人,获取Webhook
|
||
- 邮件: 配置告警联系人邮箱
|
||
- 短信: 配置手机号(建议仅用于紧急告警)
|
||
```
|
||
|
||
---
|
||
|
||
## 📚 附录
|
||
|
||
### 📝 **配置文件清单**
|
||
|
||
部署过程中涉及的配置文件:
|
||
|
||
```yaml
|
||
本地文件 (AIclinicalresearch/frontend-v2/):
|
||
- Dockerfile (多阶段构建配置)
|
||
- nginx.conf (Nginx服务器配置)
|
||
- docker-entrypoint.sh (容器启动脚本)
|
||
- .dockerignore (构建排除文件)
|
||
|
||
SAE配置 (在控制台配置):
|
||
- 应用基本信息
|
||
- 实例规格和数量
|
||
- VPC网络配置
|
||
- 镜像地址和版本
|
||
- 环境变量
|
||
- 健康检查
|
||
- 公网访问配置
|
||
```
|
||
|
||
---
|
||
|
||
### 🔗 **相关文档链接**
|
||
|
||
```yaml
|
||
阿里云官方文档:
|
||
- SAE产品文档: https://help.aliyun.com/product/1172298.html
|
||
- ACR产品文档: https://help.aliyun.com/product/60716.html
|
||
- VPC产品文档: https://help.aliyun.com/product/27706.html
|
||
|
||
项目内部文档:
|
||
- 前端Nginx部署指南: docs/05-部署文档/06-前端Nginx-SAE容器部署指南.md
|
||
- 快速部署SOP: docs/05-部署文档/01-快速部署SOP-零基础版.md
|
||
- 部署架构总览: docs/05-部署文档/00-部署架构总览.md
|
||
```
|
||
|
||
---
|
||
|
||
### 💡 **最佳实践总结**
|
||
|
||
```yaml
|
||
安全性:
|
||
✅ 使用专有网络(VPC)隔离
|
||
✅ 环境变量存储敏感信息
|
||
✅ 启用HTTPS(生产环境)
|
||
✅ 配置安全组规则
|
||
✅ 隐藏Nginx版本号
|
||
|
||
性能:
|
||
✅ 启用Gzip压缩(减少70%传输)
|
||
✅ 配置合理的缓存策略
|
||
✅ 使用CDN加速(可选)
|
||
✅ 优化Docker镜像大小
|
||
|
||
可靠性:
|
||
✅ 至少2个实例(高可用)
|
||
✅ 配置健康检查
|
||
✅ 启用自动伸缩
|
||
✅ 配置告警监控
|
||
✅ 灰度发布降低风险
|
||
|
||
可维护性:
|
||
✅ 使用语义化版本号
|
||
✅ 保留最近3个版本以便回滚
|
||
✅ 定期清理旧版本镜像
|
||
✅ 记录变更日志
|
||
✅ 文档及时更新
|
||
```
|
||
|
||
---
|
||
|
||
### ❓ **常见问题FAQ**
|
||
|
||
**Q1: 前端服务需要多大的规格?**
|
||
```
|
||
A: 0.5核1GB足够。
|
||
- Nginx极其高效,占用资源很少
|
||
- 0.5核可以支撑1000+ QPS
|
||
- 不要过度配置浪费资源
|
||
```
|
||
|
||
**Q2: 需要配置多少个实例?**
|
||
```
|
||
A: 生产环境建议2个实例。
|
||
- 保证高可用(1个故障时另1个继续服务)
|
||
- 前端很少需要3个以上实例
|
||
- 流量特别大时可以配置CDN
|
||
```
|
||
|
||
**Q3: 如何知道后端服务的内网地址?**
|
||
```
|
||
A: 从后端SAE应用获取:
|
||
1. 登录SAE控制台
|
||
2. 进入后端应用详情
|
||
3. 查看【应用访问配置】→【VPC内网访问地址】
|
||
4. 复制IP地址(格式:172.17.x.x)
|
||
```
|
||
|
||
**Q4: 环境变量配置后没生效怎么办?**
|
||
```
|
||
A: 需要重启应用:
|
||
1. 修改环境变量
|
||
2. 点击【重启】或【重新部署】
|
||
3. 等待实例重启完成
|
||
4. 查看启动日志确认变量值
|
||
```
|
||
|
||
**Q5: 部署新版本后如何快速回滚?**
|
||
```
|
||
A: SAE支持一键回滚:
|
||
1. 进入【变更记录】
|
||
2. 找到上一个稳定版本
|
||
3. 点击【回滚】
|
||
4. 等待1-2分钟完成回滚
|
||
```
|
||
|
||
**Q6: 如何配置HTTPS?**
|
||
```
|
||
A: 两种方式:
|
||
1. 使用SAE提供的域名(自动HTTPS)
|
||
2. 绑定自定义域名并上传SSL证书
|
||
|
||
生产环境强烈建议使用HTTPS
|
||
```
|
||
|
||
**Q7: 前端需要连接数据库吗?**
|
||
```
|
||
A: 不需要。
|
||
- 前端是纯静态资源(HTML/JS/CSS)
|
||
- 只需要通过Nginx代理访问后端API
|
||
- 数据库连接由后端服务处理
|
||
```
|
||
|
||
**Q8: 每次更新都要重新构建Docker镜像吗?**
|
||
```
|
||
A: 是的。
|
||
1. 修改前端代码
|
||
2. npm run build(生成新的dist/)
|
||
3. docker build(打包到镜像)
|
||
4. docker push(推送到ACR)
|
||
5. SAE重新部署(拉取新镜像)
|
||
|
||
这是标准的容器化部署流程
|
||
```
|
||
|
||
**Q9: Docker镜像太大怎么办?**
|
||
```
|
||
A: 当前镜像只有92MB,已经很小了。
|
||
- 多阶段构建(只保留运行时文件)
|
||
- Alpine基础镜像(最小Linux发行版)
|
||
- 已经是最优化的状态
|
||
```
|
||
|
||
**Q10: 如何查看实时访问日志?**
|
||
```
|
||
A: SAE控制台查看:
|
||
【应用详情】→【日志】→【实时日志】
|
||
|
||
可以看到:
|
||
- 所有HTTP请求(URL、状态码、响应时间)
|
||
- Nginx错误日志
|
||
- 容器启动日志
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 **部署检查清单**
|
||
|
||
最后,用这个清单确认部署完成:
|
||
|
||
```yaml
|
||
部署前准备:
|
||
☑️ ACR镜像已推送(v1.0.0)
|
||
☑️ 后端服务已部署(获取内网地址)
|
||
☑️ VPC和交换机已创建
|
||
☑️ 安全组已配置
|
||
|
||
SAE应用配置:
|
||
☑️ 应用名称:frontend-nginx-service
|
||
☑️ 实例规格:0.5核1GB
|
||
☑️ 实例数量:2
|
||
☑️ 镜像地址:专有网络域名(带-vpc)
|
||
☑️ 镜像版本:v1.0.0
|
||
|
||
环境变量配置:
|
||
☑️ BACKEND_SERVICE_HOST:已配置(172.17.x.x)
|
||
☑️ BACKEND_SERVICE_PORT:已配置(3001)
|
||
|
||
健康检查配置:
|
||
☑️ 检查路径:/health
|
||
☑️ 检查端口:80
|
||
☑️ 检查协议:HTTP
|
||
|
||
部署验证:
|
||
☑️ 实例状态:健康
|
||
☑️ 健康检查:通过
|
||
☑️ 主页访问:正常
|
||
☑️ SPA路由:正常(刷新不404)
|
||
☑️ API代理:正常(无CORS错误)
|
||
☑️ 静态资源:正常(缓存生效)
|
||
☑️ Gzip压缩:已启用
|
||
|
||
监控和告警:
|
||
☑️ 实时监控:已配置
|
||
☑️ 日志查看:可访问
|
||
☑️ 告警规则:已配置
|
||
|
||
全部完成,部署成功!🎉
|
||
```
|
||
|
||
---
|
||
|
||
**文档结束**
|
||
|
||
如有疑问,请查阅相关文档或联系技术支持。
|
||
|
||
祝部署顺利!🚀
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|