# Node.js 后端 - SAE 容器部署完全指南 **文档版本**: v1.1 (修复 Prisma 构建和数据库同步问题) **创建时间**: 2025-12-13 **最后修?*: 2025-12-13 **适用范围**: AIclinicalresearch 平台 - Node.js 后端服务 **目标读?*: 运维工程师、后端开发工程师 **部署目标**: 阿里?SAE(Serverless 应用引擎)容器部? **v1.1 更新日志**: - 🛑 修复:Prisma 目录构建上下文问题(增加预处理步骤) - 🛑 修复:生产环境缺?Prisma CLI 导致迁移失败 - 🔥 新增:针对开发历史不规范?反向同步"流程(关键) - ?优化:数据库迁移策略(pg_dump 导入后不执行 migrate?- ?简化:移除 Init Container 方案,改用启动命? --- ## 📋 目录 1. [为什么选择 SAE 容器部署](#1-为什么选择-sae-容器部署) 2. [部署前准备](#2-部署前准? 3. [后端服务分析](#3-后端服务分析) 4. [🔥 Prisma 反向同步(必读)](#4-prisma-反向同步必读) 5. [构建 Docker 镜像](#5-构建-docker-镜像) 6. [本地测试验证](#6-本地测试验证) 7. [推送到 ACR](#7-推送到-acr) 8. [SAE 应用配置](#8-sae-应用配置) 9. [数据库部署策略](#9-数据库部署策? 10. [端到端测试](#10-端到端测? 11. [监控与维护](#11-监控与维? 12. [故障排查](#12-故障排查) 13. [注意事项与禁忌](#13-注意事项与禁? --- ## 1. 为什么选择 SAE 容器部署 ### ?核心优势 | 优势 | 说明 | 对您的价?| |------|------|----------| | **环境一致?* | Docker 镜像保证本地开发(Node 22 + Prisma)与线上环境完全一?| 杜绝"我本地明明能?的问?| | **弹性伸?* | SAE 根据 CPU/内存使用率自动增减实例数?| 大量用户 AI 对话时自动扩?| | **免运?* | 无需管理服务?OS、安全补丁,SAE 全托?| 1-2 人团队节省运维精?| | **内网互?* | ?SAE ?Python 服务?ECS ?Dify 通过 VPC 内网高速通信 | 毫秒级延迟,无公网流量费 | | **私有化就?* | Docker 镜像可直接交付给医院,部署到内网环境 | 满足医疗数据合规要求 | | **统一架构** | 前端(Nginx)、后端(Node.js)、Python 微服务都?Docker | 一套部署流程,降低学习成本 | ### 🎯 为什么不?Code Package 部署? | 对比?| Code Package | 容器部署(推荐)| |-------|--------------|----------------| | 环境一致?| ⚠️ 云端 Node 版本可能不同 | ?镜像锁定版本 | | 私有化交?| ?无法直接交付 | ?打包即可交付 | | 系统依赖 | ⚠️ 无法自定?| ?Dockerfile 完全控制 | | 构建速度 | ?快(上传即可?| ⚠️ 慢(需构建镜像)| | 适用场景 | 快速原型验?| 生产环境、私有化 | **结论**:对于需要私有化部署的医疗科研产品,容器部署是唯一选择? --- ## 2. 部署前准? ### ?前置条件检查清? #### 本地开发环? - [ ] Docker Desktop 已安装并运行(版?20.10+?- [ ] Node.js 已安装(版本 22+?- [ ] 后端代码已拉取到本地 - [ ] 后端在本地能正常启动(`npm run dev`? 验证命令? ```bash # 检?Docker docker --version # 输出示例:Docker version 24.0.6 # 检?Node.js node --version # 输出示例:v22.11.0 # 检?npm npm --version # 输出示例?0.9.0 ``` #### 阿里云资? - [ ] **RDS PostgreSQL 15** 实例已创建并运行 - 数据库名称:`ai_clinical`(或自定义) - 用户名和密码已准? - 内网地址已获取(?`pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432`? - 白名单已配置(允?SAE VPC 访问? - [ ] **阿里云容器镜像服?ACR** 已开? - 命名空间已创建(?`clinical-research`? - 登录密码已设? - [ ] **SAE 应用** 已创建(或准备创建) - VPC 和交换机已选择(与 RDS 在同一 VPC? - [ ] **依赖服务的内网地址已获?*? - Python 微服务(SAE):`http://172.17.x.x:8000` - Dify 服务(ECS):`http://172.17.x.x:80` #### 敏感信息准备 准备以下配置信息(稍后配置到 SAE 环境变量): ```bash # 数据?DATABASE_URL=postgresql://username:password@pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432/ai_clinical?connection_limit=18&pool_timeout=10 # LLM API Keys(至少配置一个) DEEPSEEK_API_KEY=sk-xxxxx DASHSCOPE_API_KEY=sk-xxxxx CLOSEAI_API_KEY=sk-xxxxx # Dify DIFY_API_KEY=app-xxxxx DIFY_API_URL=http://172.17.x.x:80/v1 # 阿里?OSS OSS_REGION=oss-cn-beijing OSS_BUCKET=clinical-research-files OSS_ACCESS_KEY_ID=LTAI5t... OSS_ACCESS_KEY_SECRET=xxx... # JWT 安全密钥(生产环境必须修改) JWT_SECRET=your-strong-random-secret-min-32-chars ``` --- ## 3. 后端服务分析 ### 📦 技术栈 | 技?| 版本 | 用?| |------|------|------| | **Node.js** | 22+ | 运行时环?| | **Fastify** | 5.6+ | Web 框架(高性能?| | **TypeScript** | 5.9+ | 类型安全 | | **Prisma** | 6.17+ | ORM(数据库访问?| | **pg-boss** | 12.5+ | 队列(Postgres-Only?| | **winston** | 3.18+ | 日志系统 | ### 📊 依赖服务 ``` Node.js 后端(SAE? ? ├──?RDS PostgreSQL 15(数据库? ? ├──?Python 微服务(SAE? - 文档提取 ? └─ http://172.17.x.x:8000 ? ├──?Dify 服务(ECS? - RAG 知识? ? └─ http://172.17.x.x:80/v1 ? └──?阿里?OSS - 文件存储 └─ clinical-research-files ``` ### 🔧 核心功能模块 | 模块 | 路径 | 功能 | 依赖服务 | |------|------|------|---------| | **AIA** | `/api/chat` | AI 智能助理 | DeepSeek/千问/Claude | | **PKB** | `/api/knowledge-bases` | 个人知识?| Dify + OSS | | **ASL** | `/api/asl/*` | 智能文献 | Python 微服?+ OSS | | **DC** | `/api/dc/*` | 数据清洗 | Python 微服?+ OSS | | **RVW** | `/api/review` | 稿件审查 | DeepSeek/千问 | | **Health** | `/health` | 健康检?| RDS | ### 📝 启动流程 ```bash # 1. 安装依赖 npm install # 2. 生成 Prisma Client npm run prisma:generate # 3. 数据库迁移(仅首次或更新时) npx prisma migrate deploy # 4. 编译 TypeScript npm run build # 5. 启动应用 npm start ``` --- ## 4. 🔥 Prisma 反向同步(必读) ### ⚠️ 重要警告:为什么需要这一步? **您的开发历?*?- ?使用 `pg_dump` 导出了本?PostgreSQL 数据库(包含表结构和数据?- ?已经导入到阿里云 RDS PostgreSQL - ⚠️ **但是**:开发过程中,经常直接用 SQL ?Navicat 修改数据库,**没有同步更新 `schema.prisma` 文件** **后果**?```typescript // schema.prisma 中定义: model User { id String email String name String // ?缺少 phone 字段 } // 但数据库里实际有?CREATE TABLE "User" ( id VARCHAR(255), email VARCHAR(255), name VARCHAR(255), phone VARCHAR(50) -- ?这个字段存在,但 Prisma 不知?); // 代码里尝试访问: const user = await prisma.user.findUnique({ where: { id: '123' } }); console.log(user.phone); // ?TypeScript 报错:Property 'phone' does not exist on type 'User' ``` **更严重的后果**?- Prisma Client 生成的类型与数据库不一?- 运行时可能读不到数据或报?- 如果强行运行 `prisma migrate deploy`,可能因为表已存在而失? ### ?解决方案:反向同步(Introspection? #### 步骤 1:连接到 RDS 数据? 在本地开发环境,临时连接?RDS? ```bash # 1. 备份当前?.env 文件 cp backend/.env backend/.env.backup # 2. 创建临时 RDS 连接配置 cat > backend/.env.rds < { process.exit(res.statusCode === 200 ? 0 : 1); })" # 暴露端口 EXPOSE 3001 # 🔥 启动命令(仅启动应用,不执行数据库迁移) # 解释:因为数据库已通过 pg_dump 导入,结构已就绪,无需 migrate CMD ["node", "dist/index.js"] ``` ### 📝 Dockerfile 关键修改说明 #### 修改 1:全局安装 Prisma CLI ```dockerfile # 🔥 在阶?2 新增?RUN npm install -g prisma@6.17.0 ``` **原因**?- `npm ci` + `npm prune --production` 会删?devDependencies 中的 `prisma` ?- 但生产环境可能需要执?`npx prisma db pull` 或排查问?- 全局安装确保 `prisma` 命令始终可用 **代价**?- 镜像体积增加?50MB - 但这是值得的(避免"命令找不?的问题) #### 修改 2:不执行数据库迁? ```dockerfile # ?错误做法(会导致"表已存在"错误): CMD ["sh", "-c", "npx prisma migrate deploy && node dist/index.js"] # ?正确做法(仅启动应用): CMD ["node", "dist/index.js"] ``` **原因**?- 您的数据库是通过 `pg_dump` 导入的,表结构已经存?- 如果执行 `prisma migrate deploy`,可能因为迁移记录对不上而报?- 正确的做法:启动前确?`schema.prisma` 与数据库一致(?4 节已处理? #### 修改 3:移?dumb-init(可选优化) ```dockerfile # 移除?# RUN apk add --no-cache dumb-init # ENTRYPOINT ["dumb-init", "--"] # Node.js 22 在处理信号时已经足够稳定 # 如果你发现下?dumb-init 很慢或报错,可以去掉这个优化 ``` ### 📝 创建 .dockerignore ?`backend/` 目录下创?`.dockerignore`? ``` # Node.js node_modules npm-debug.log yarn-error.log # 开发文?.env .env.* *.local # 构建产物(已?Dockerfile 中生成) dist # 测试文件 test tests *.test.ts *.spec.ts coverage # 文档和临时文?docs *.md .vscode .idea .DS_Store Thumbs.db # 上传文件(运行时生成?uploads/* # Git .git .gitignore # 日志 *.log logs # 临时文件 temp tmp *.swp *.swo *~ # 数据库文件(SQLite,如果有?*.db *.sqlite # 脚本文件(仅开发使用) scripts/*.ts *.bat *.ps1 ``` --- ## 6. 本地测试验证 ### 🛑 前置步骤:复?Prisma 文件(必须执行) ```bash # 1. 回到项目根目?cd AIclinicalresearch # 2. 复制 prisma ?backend 目录(确保构建时能找到) cp -r prisma backend/prisma # 3. 验证复制成功 ls backend/prisma/schema.prisma # 应该输出:backend/prisma/schema.prisma ``` ### 步骤 1:构建镜? ```bash # ?backend 目录下执?cd backend # 🔥 确保已执行上面的复制步骤? # 构建镜像(需?5-10 分钟?docker build -t backend-service:v1.0.0 . # 查看镜像大小 docker images backend-service:v1.0.0 # 预期大小:~300-500MB(Alpine 基础镜像 + Node.js + 依赖?``` **如果构建失败**? ```bash # 常见问题 1:Prisma 文件找不?# 错误信息:COPY failed: file not found in build context or excluded by .dockerignore: stat prisma: file does not exist # 解决:确保执行了复制步骤:cp -r ../prisma ./prisma(在 backend 目录外执行) # 常见问题 2:网络超时(npm install 慢) # 解决:使用国内镜像源 # ?Dockerfile ?npm ci 之前添加?RUN npm config set registry https://registry.npmmirror.com # 常见问题 3:Prisma 生成失败 # 解决:检?backend/prisma/schema.prisma 是否存在且格式正?``` ### 步骤 2:本地运行测? ```bash # 创建测试环境变量文件 cat > .env.docker.test </dev/null || echo "?prisma 目录已清? # 注意:根目录?prisma/ 文件夹保留,这是源文?``` --- ## 7. 推送到 ACR ### 步骤 1:登?ACR ```bash # 获取 ACR 登录地址(阿里云控制??容器镜像服务 ?访问凭证?# 示例:registry.cn-hangzhou.aliyuncs.com # 登录(使?ACR 密码,不是阿里云账号密码?docker login --username=your-aliyun-account registry.cn-hangzhou.aliyuncs.com # 输入密码后看到: # Login Succeeded ``` ### 步骤 2:标记镜? ```bash # 格式:registry地址/命名空间/仓库?版本?docker tag backend-service:v1.0.0 \ registry.cn-hangzhou.aliyuncs.com/clinical-research/backend-service:v1.0.0 # 同时打一?latest 标签(方便测试) docker tag backend-service:v1.0.0 \ registry.cn-hangzhou.aliyuncs.com/clinical-research/backend-service:latest ``` ### 步骤 3:推送镜? ```bash # 推送指定版?docker push registry.cn-hangzhou.aliyuncs.com/clinical-research/backend-service:v1.0.0 # 推?latest docker push registry.cn-hangzhou.aliyuncs.com/clinical-research/backend-service:latest # 推送过程需?5-10 分钟(视网络速度?``` ### 步骤 4:验证推送成? 登录阿里云控制台 ?容器镜像服务 ?镜像仓库 ?`backend-service` - 应该看到版本:`v1.0.0` ?`latest` - 镜像大小:~300-500MB - 推送时间:刚才的时? --- ## 8. SAE 应用配置 ### 步骤 1:创建应? **阿里云控制台** ?**SAE** ?**应用列表** ?**创建应用** | 配置?| ?| 说明 | |-------|-----|------| | **应用名称** | `backend-service` | 后端服务 | | **命名空间** | 选择已创建的命名空间 | ?Python 服务同一命名空间 | | **VPC** | 选择 RDS 所?VPC | 必须?RDS 在同一 VPC | | **交换?* | 选择可用?| 建议多可用区 | | **应用实例规格** | 2?G | 初始规格 | | **实例数量** | 2 | 最?2 个实例(高可用) | ### 步骤 2:配置镜? | 配置?| ?| |-------|-----| | **镜像类型** | 容器镜像服务企业版实?| | **镜像仓库** | `registry.cn-hangzhou.aliyuncs.com/clinical-research/backend-service` | | **镜像版本** | `v1.0.0` | | **镜像拉取策略** | 总是拉取镜像 | ### 步骤 3:配置端? | 配置?| ?| |-------|-----| | **容器端口** | `3001` | | **协议** | TCP | ### 步骤 4:配置环境变量(关键步骤? **⚠️ 重要:请仔细配置以下环境变量** #### 基础配置 ```bash NODE_ENV=production PORT=3001 HOST=0.0.0.0 LOG_LEVEL=info SERVICE_NAME=backend-service ``` #### 数据库配置(必需? ```bash # ⚠️ 使用 RDS 内网地址 DATABASE_URL=postgresql://username:password@pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432/ai_clinical?connection_limit=18&pool_timeout=10 # 连接池配置(根据 RDS 规格调整?DB_MAX_CONNECTIONS=400 MAX_INSTANCES=20 ``` **连接池计算说?*?- 假设 RDS 最大连接数?00 - SAE 最大实例数?0 - 每实例连接数 = (400 / 20) - 预留 = 18 - 预留 10 个连接给管理任务和其他服? #### 存储配置(必需? ```bash # 使用阿里?OSS STORAGE_TYPE=oss OSS_REGION=oss-cn-beijing OSS_BUCKET=clinical-research-files OSS_ACCESS_KEY_ID=LTAI5t... OSS_ACCESS_KEY_SECRET=xxx... ``` #### 缓存和队列配置(Postgres-Only? ```bash # 使用 PostgreSQL 作为缓存(不需?Redis?CACHE_TYPE=postgres # 使用 pg-boss 作为队列(不需?Redis?QUEUE_TYPE=pgboss ``` #### LLM API 配置(至少配置一个) ```bash # DeepSeek(推荐,性价比高?DEEPSEEK_API_KEY=sk-xxxxx DEEPSEEK_BASE_URL=https://api.deepseek.com # 通义千问(备选) DASHSCOPE_API_KEY=sk-xxxxx # CloseAI(OpenAI/Claude 代理?CLOSEAI_API_KEY=sk-xxxxx CLOSEAI_OPENAI_BASE_URL=https://api.openai-proxy.org/v1 CLOSEAI_CLAUDE_BASE_URL=https://api.openai-proxy.org/anthropic ``` #### Dify 配置(必需? ```bash # ⚠️ 使用 ECS 内网 IP(不要使用公网域名) DIFY_API_URL=http://172.17.x.x:80/v1 DIFY_API_KEY=app-xxxxx ``` **如何获取 Dify 内网 IP**?1. 登录 ECS 控制?2. 找到 Dify 所在的 ECS 实例 3. 查看"私有 IP 地址"(如 `172.16.0.20`? #### 安全配置(必需? ```bash # ⚠️ 生产环境必须修改为强密码(至?32 位随机字符串?JWT_SECRET=your-strong-random-secret-min-32-chars-change-in-production JWT_EXPIRES_IN=7d ``` **生成强密?*? ```bash # Linux/Mac openssl rand -base64 32 # Windows PowerShell -join ((65..90) + (97..122) + (48..57) | Get-Random -Count 32 | ForEach-Object {[char]$_}) ``` #### CORS 配置(可选) ```bash # 如果前端使用自定义域名,配置允许的源 # 注意:如果前端使?Nginx 反向代理,则不需要配置(Nginx 已处理) CORS_ORIGIN=https://your-frontend-domain.com ``` ### 步骤 5:配置健康检? **SAE 控制?* ?**应用配置** ?**健康检?* | 配置?| ?| 说明 | |-------|-----|------| | **检查方?* | HTTP 请求 | | | **检查路?* | `/health` | 后端健康检查端?| | **检查端?* | 3001 | 与容器端口一?| | **检查协?* | HTTP | | | **初始延迟** | 60 ?| ?Prisma 初始化足够时?| | **检查间?* | 10 ?| | | **超时时间** | 3 ?| | | **不健康阈?* | 3 ?| 连续失败 3 次标记为不健?| | **健康阈?* | 2 ?| 连续成功 2 次标记为健康 | ### 步骤 6:配置弹性伸? **SAE 控制?* ?**应用配置** ?**弹性伸?* | 配置?| ?| 说明 | |-------|-----|------| | **最小实例数** | 2 | 高可用保?| | **最大实例数** | 10 | 根据预期负载调整 | | **扩容条件** | CPU > 70% 持续 3 分钟 | | | **缩容条件** | CPU < 30% 持续 5 分钟 | | ### 步骤 7:配置日? **SAE 控制?* ?**应用配置** ?**日志配置** | 配置?| ?| |-------|-----| | **日志类型** | 标准输出(stdout?| | **日志存储** | 开启(保存 7 天) | ### 步骤 8:部署应? 点击"部署"按钮,SAE 将: 1. ?ACR 拉取镜像(~2 分钟?2. 启动容器实例(~1 分钟?3. 执行健康检查(~1 分钟?4. 流量切换(~30 秒) **总耗时**:约 5 分钟 --- ## 9. 数据库部署策? ### 🎯 您的实际情况(非常重要) 根据您的开发历史,数据库部署策略与标准流程**完全不同**? **标准流程(不适合您)**?```mermaid graph LR A[代码] --> B[Prisma Migrations] B --> C[空数据库] C --> D[自动创建表结构] ``` **您的实际流程**?```mermaid graph LR A[本地 PostgreSQL
包含数据] --> B[pg_dump 导出] B --> C[RDS 导入] C --> D[表结构已存在] D --> E[Prisma 反向同步] ``` ### ?正确的部署策? #### 策略总览 | 步骤 | 操作 | 何时执行 | 目的 | |------|------|---------|------| | **1. 导出本地数据?* | `pg_dump` | 首次部署?| 备份现有数据和结?| | **2. 导入?RDS** | `psql` ?DMS | 首次部署?| 迁移到云?| | **3. Prisma 反向同步** | `prisma db pull` | 首次部署前(?4 节) | 同步 Schema | | **4. 启动应用** | `node dist/index.js` | SAE 部署?| 正常运行 | | **5. ~~数据库迁移~~** | ~~`prisma migrate deploy`~~ | ?**不执?* | 表已存在,无需迁移 | ### 方案 1:使?pg_dump 导入(您的情况,推荐? #### 步骤 1:导出本地数据库(如果还没做? ```bash # 在本地开发环境执?docker exec ai-clinical-postgres pg_dump -U postgres -d ai_clinical \ --no-owner --no-acl --clean --if-exists \ > ai_clinical_backup_$(date +%Y%m%d).sql # 查看导出文件 ls -lh ai_clinical_backup_*.sql # 应该看到文件大小(如 5.2MB?``` #### 步骤 2:导入到 RDS(如果还没做? **方法 A:使?psql 命令行(推荐?* ```bash # 连接?RDS 并导?psql "postgresql://username:password@pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432/ai_clinical" \ < ai_clinical_backup_20251213.sql # 输出示例?# DROP TABLE # CREATE TABLE # INSERT 0 123 # ... # ?导入完成 ``` **方法 B:使?DMS(阿里云数据管理?* 1. 登录阿里云控制台 ?数据管理 DMS 2. 连接?RDS 实例 3. 数据方案 ?SQL 窗口 ?粘贴 SQL 文件内容 4. 执行 #### 步骤 3:验证导入成? ```bash # 连接?RDS psql "postgresql://username:password@pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432/ai_clinical" # 查看所?Schema \dn # 应该看到?# aia_schema # asl_schema # common_schema # dc_schema # pkb_schema # platform_schema # rvw_schema # ssa_schema # st_schema # public # 查看用户?SELECT count(*) FROM "User"; # 应该返回正确的用户数? # 退?\q ``` #### 步骤 4:确认已执行 Prisma 反向同步 **⚠️ 这一步非常关键!** ```bash # 确保?4 节的步骤已完成: # 1. 已执?npx prisma db pull # 2. 已执?npx prisma generate # 3. 已提?schema.prisma ?Git # 验证:检?schema.prisma 的修改时?ls -l prisma/schema.prisma # 修改时间应该是最近(今天? # 如果还没做,立即返回?4 节执行! ``` ### 方案 2:标?Prisma Migrations(仅适用于新项目? **⚠️ 如果您的数据库是通过 `pg_dump` 导入的,请跳过这一节!**
点击展开:标准迁移流程(仅供参考) #### 适用场景 - 全新项目,RDS 数据库是空的 - 从未手动修改过数据库结构 - 所有表结构都通过 Prisma Migrations 管理 #### 执行步骤 ```bash # 1. 连接?RDS export DATABASE_URL="postgresql://username:password@pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432/ai_clinical" # 2. 执行迁移 cd backend npx prisma migrate deploy # 3. 验证 npx prisma db execute --stdin <<< "SELECT schemaname FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema') GROUP BY schemaname;" ```
### 🚨 常见错误与修? #### 错误 1:启动时执行 `prisma migrate deploy` 导致失败 **错误信息**? ``` Error: P3005: The database schema is not empty. Read more about how to baseline an existing production database: https://pris.ly/d/migrate-baseline ``` **原因**:表已通过 `pg_dump` 导入,再执行 migrate 会冲突? **解决方法**? ```bash # 方法 1:移除启动命令中?migrate(推荐) # SAE 控制??应用配置 ?启动命令 # 确保启动命令是: node dist/index.js # 不要写: # sh -c "npx prisma migrate deploy && node dist/index.js" # ?``` #### 错误 2:应用启动后,访问数据库报错 **错误信息**? ```typescript PrismaClientKnownRequestError: Invalid `prisma.user.findMany()` invocation: column "phone" does not exist ``` **原因**:`schema.prisma` 与数据库不一致? **解决方法**? 返回?4 节,执行 `npx prisma db pull` 反向同步? ### ?部署检查清? 在启?SAE 应用前,确认以下步骤已完成: - [ ] 本地数据库已通过 `pg_dump` 导出 - [ ] SQL 文件已导入到 RDS(表结构和数据都存在?- [ ] 已执?`npx prisma db pull` 同步 Schema - [ ] 已执?`npx prisma generate` 生成客户?- [ ] 已提?`schema.prisma` ?Git - [ ] Docker 镜像的启动命?*不包?* `prisma migrate deploy` - [ ] SAE 环境变量中的 `DATABASE_URL` 指向 RDS **如果以上都确认,可以放心部署?* ? --- ## 10. 端到端测? ### 步骤 1:获取应用访问地址 **SAE 控制?* ?**应用详情** ?**应用访问配置** 复制以下地址? ``` # 公网访问地址(用于前端调用) https://backend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com # VPC 内网访问地址(用于服务间调用?http://172.16.0.30:3001 ``` ### 步骤 2:测试健康检? ```bash # 使用公网地址测试 curl https://backend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com/health # 预期返回?{ "status": "healthy", "timestamp": "2025-12-13T10:30:00.000Z", "uptime": 12345.678, "database": { "status": "connected", "connections": 8 }, "version": "1.0.0" } ``` ### 步骤 3:测试用户注? ```bash curl -X POST https://backend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com/api/auth/register \ -H "Content-Type: application/json" \ -d '{ "email": "sae-test@example.com", "password": "Test123456", "name": "SAE测试用户" }' # 预期返回?{ "success": true, "user": { "id": "...", "email": "sae-test@example.com", "name": "SAE测试用户" }, "token": "eyJhbGciOiJIUzI1NiIs..." } ``` ### 步骤 4:测试文件上传(PKB 模块? ```bash # 获取 Token(从步骤 3?TOKEN="eyJhbGciOiJIUzI1NiIs..." # 上传 PDF 文档 curl -X POST https://backend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com/api/knowledge-bases/kb-xxx/documents \ -H "Authorization: Bearer $TOKEN" \ -F "file=@test.pdf" \ -F "name=测试文档" # 预期返回?{ "success": true, "document": { "id": "...", "name": "测试文档", "status": "processing" } } ``` ### 步骤 5:测?Python 微服务调? ```bash # 测试 ASL 模块?PDF 提取功能 curl -X POST https://backend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com/api/asl/extract \ -H "Authorization: Bearer $TOKEN" \ -F "file=@paper.pdf" # 查看后端日志(SAE 控制??应用详情 ?日志?# 应该看到?# [INFO] Calling extraction service: http://172.17.x.x:8000/extract/pdf # [INFO] Extraction completed in 3.2s ``` ### 步骤 6:测?Dify 服务调用 ```bash # 测试 PKB 模块的知识库对话 curl -X POST https://backend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com/api/knowledge-bases/kb-xxx/chat \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "query": "这篇文献的研究方法是什么?", "conversationId": "conv-xxx" }' # 查看后端日志 # 应该看到?# [INFO] Calling Dify API: http://172.17.x.x:80/v1/chat-messages # [INFO] Dify response received in 2.5s ``` ### 步骤 7:测?OSS 文件上传 ```bash # 上传大文件(测试 OSS?curl -X POST https://backend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com/api/asl/upload \ -H "Authorization: Bearer $TOKEN" \ -F "file=@large-paper.pdf" # 查看日志,应该看?OSS 上传成功?# [INFO] File uploaded to OSS: clinical-research-files/asl/xxx.pdf ``` --- ## 11. 监控与维? ### 📊 SAE 自带监控 #### 1. 实时监控 **SAE 控制?* ?**应用详情** ?**监控** **关键指标**? | 指标 | 健康阈?| 告警阈?| 说明 | |------|---------|---------|------| | **CPU 使用?* | < 60% | > 80% | LLM 调用?CPU 密集?| | **内存使用?* | < 70% | > 85% | 监控内存泄漏 | | **请求 QPS** | - | - | 了解负载 | | **平均响应时间** | < 500ms | > 2000ms | AI 对话除外(可?10s+?| | **错误?* | < 0.5% | > 2% | 监控服务稳定?| | **实例数量** | 2+ | - | 确保高可?| #### 2. 日志查看 **SAE 控制?* ?**应用详情** ?**日志** ?**实时日志** **关键日志示例**? ```bash # ?正常启动 [Config] Environment validation passed [Database] ?数据库连接成功! [Fastify] Server listening on http://0.0.0.0:3001 # ?正常请求 [INFO] POST /api/chat 200 2345ms # ⚠️ 警告日志(需关注?[WARN] Database connection pool near limit: 16/18 connections # ?错误日志(需立即处理?[ERROR] Failed to connect to Python service: ECONNREFUSED [ERROR] Prisma timeout: Database connection pool exhausted [ERROR] Dify API error: 502 Bad Gateway ``` #### 3. 数据库连接监? ```bash # ?SAE Webshell 中执行(或使?RDS 控制台) psql $DATABASE_URL -c " SELECT count(*) as total_connections, count(*) FILTER (WHERE state = 'active') as active_connections, count(*) FILTER (WHERE state = 'idle') as idle_connections FROM pg_stat_activity WHERE datname = 'ai_clinical'; " # 输出示例?# total_connections | active_connections | idle_connections # ------------------+--------------------+----------------- # 35 | 5 | 30 # 告警条件:total_connections > (MAX_INSTANCES * connection_limit * 0.8) # 示例?0 实例 * 18 连接/实例 * 0.8 = 288 连接 ``` ### 🔧 日常维护任务 #### 每日检? ```bash # 1. 检查应用健康状?# SAE 控制??应用列表 ?查看运行状态(绿色为正常) # 2. 查看错误日志 # SAE 控制??应用详情 ?日志 ?筛?ERROR 级别 # 3. 检查数据库连接?# RDS 控制??实例监控 ?连接数(< 80% 为健康) ``` #### 每周任务 ```bash # 1. 查看性能指标趋势 # SAE 控制??应用详情 ?监控 ?选择"最?7 ? # 关注?# - CPU/内存是否有持续上涨(内存泄漏?) # - 响应时间是否变慢 # - 错误率是否增? # 2. 清理日志(如果日志存储空间紧张) # SAE 控制??应用详情 ?日志配置 ?清理旧日?``` #### 每月任务 ```bash # 1. 更新依赖(安全补丁) # 在本地开发环境执行: npm outdated npm update npm audit fix # 重新测试后部? # 2. 重建镜像(包含系统安全更新) docker build -t backend-service:v1.0.1 . docker push registry.cn-hangzhou.aliyuncs.com/clinical-research/backend-service:v1.0.1 # 3. ?SAE 中灰度更?# SAE 控制??应用详情 ?部署 # 选择新镜像版本:v1.0.1 # 灰度发布:先更新 1 个实例,观察 10 分钟后全量发? # 4. 数据库备份(RDS 自动备份,仅需验证?# RDS 控制??备份恢复 ?查看最近备份时?``` ### 🚨 告警配置 **云监?* ?**应用监控** ?**创建告警规则** **推荐告警规则**? | 告警?| 阈?| 通知方式 | |-------|------|---------| | CPU 使用?> 80% 持续 5 分钟 | 告警 | 钉钉/邮件 | | 内存使用?> 85% 持续 5 分钟 | 告警 | 钉钉/邮件 | | 错误?> 2% 持续 3 分钟 | 紧?| 短信+钉钉 | | 实例健康检查失?> 3 ?| 紧?| 短信+钉钉 | | RDS 连接?> 80% | 警告 | 钉钉/邮件 | --- ## 12. 故障排查 ### 问题 1:应用启动失? **症状**? ``` SAE 控制台显示:实例启动??健康检查失??实例停止 ``` **排查步骤**? ```bash # 1. 查看启动日志 # SAE 控制??应用详情 ?日志 ?筛选最?5 分钟 # 2. 常见错误原因? # 错误 A:环境变量验证失?# 日志:❌ [Config] Environment validation failed: DATABASE_URL is required # 解决:检?SAE 环境变量配置,补充缺失的变量 # 错误 B:数据库连接失败 # 日志:❌ 数据库连接失? getaddrinfo ENOTFOUND pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com # 解决?# - 检?DATABASE_URL 是否正确 # - 检?RDS 白名单是否允?SAE VPC 访问 # - 检?RDS 内网地址是否可达 # 错误 C:Prisma 迁移未执?# 日志:Error: P1001: Can't reach database server at `pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com` # 解决:先执行数据库迁移(参见?8 节) # 错误 D:端口冲?# 日志:Error: listen EADDRINUSE: address already in use :::3001 # 解决:检?PORT 环境变量是否?3001(与 Dockerfile EXPOSE 一致) ``` ### 问题 2:数据库连接池耗尽 **症状**? ``` [ERROR] Prisma timeout: Database connection pool exhausted [ERROR] P2024: Timed out fetching a new connection from the pool ``` **根本原因**? - SAE 实例?* 每实例连接数 > RDS 最大连接数 - 连接未正确释放(代码 Bug? **排查步骤**? ```bash # 1. 检查当前连接数 psql $DATABASE_URL -c " SELECT count(*) as current_connections FROM pg_stat_activity WHERE datname = 'ai_clinical'; " # 2. 检?SAE 实例?# SAE 控制??应用详情 ?实例列表 # 假设?10 个实? # 3. 计算连接?# 每实例连接数 = 18(connection_limit?# 总连接数 = 10 * 18 = 180 # 如果实际连接数远?180,说明连接未释放 # 4. 查找连接泄漏 psql $DATABASE_URL -c " SELECT pid, state, wait_event, query_start, state_change, query FROM pg_stat_activity WHERE datname = 'ai_clinical' AND state_change < now() - interval '5 minutes' ORDER BY state_change; " # 5. 紧急处理:杀死长时间空闲连接 psql $DATABASE_URL -c " SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'ai_clinical' AND state = 'idle' AND state_change < now() - interval '10 minutes'; " ``` **解决方法**? ```bash # 方法 1:调整连接池配置(临时) # ?DATABASE_URL 中降?connection_limit DATABASE_URL=postgresql://...?connection_limit=10&pool_timeout=10 # 方法 2:限?SAE 实例数(临时?# SAE 控制??应用配置 ?弹性伸?# 将最大实例数?20 降低?10 # 方法 3:升?RDS 规格(长期) # RDS 控制??变更配置 ?升级到更大规格(增加最大连接数? # 方法 4:修复代码(如果是连接泄漏) # 检查代码中是否有未释放的连接: # - 未使?Prisma 的事?API # - 手动创建的数据库连接未关?# - 长时间运行的查询 ``` ### 问题 3:无法连?Python 微服? **症状**? ``` [ERROR] Failed to connect to Python service: ECONNREFUSED ?[ERROR] connect ETIMEDOUT 172.17.x.x:8000 ``` **排查步骤**? ```bash # 1. 确认 Python 服务是否运行 # SAE 控制??应用列表 ?查看 extraction-service 状? # 2. 确认内网地址是否正确 # SAE 控制??extraction-service 应用 ?应用访问配置 # 复制"VPC 内网访问地址",更新后端环境变? # 3. 测试内网连通?# 在后端应用的 Webshell 中执行: curl -v http://172.17.x.x:8000/health # 4. 检查安全组规则 # SAE 控制??extraction-service 应用 ?网络配置 # 确认入站规则允许 VPC 内访?8000 端口 ``` **解决方法**? ```bash # 如果内网地址错误,更新环境变量: # SAE 控制??backend-service 应用 ?应用配置 ?环境变量 # 修改或添加: EXTRACTION_SERVICE_URL=http://<正确的内网IP>:8000 # 重启应用使环境变量生?# SAE 控制??backend-service 应用 ?重启 ``` ### 问题 4:无法连?Dify 服务 **症状**? ``` [ERROR] Dify API error: ECONNREFUSED ?[ERROR] Dify API error: 502 Bad Gateway ``` **排查步骤**? ```bash # 1. 确认 Dify 服务是否运行 # ECS 控制??实例列表 ?找到 Dify 实例 ?远程连接 # ?ECS 中执行: docker ps | grep dify # 应该看到 dify-api, dify-worker, dify-web, redis, weaviate 等容器运行中 # 2. 测试 Dify API curl http://localhost/v1/info # 3. ?SAE 测试连通?# 在后端应用的 Webshell 中执行: curl -v http://172.17.x.x:80/v1/info ``` **解决方法**? ```bash # 如果 Dify 未启动,?ECS 中重启: cd /path/to/dify docker-compose up -d # 如果 API Key 错误,更新环境变量: # SAE 控制??backend-service 应用 ?环境变量 DIFY_API_KEY=app-<正确的Key> # 如果内网地址错误,更新环境变量: DIFY_API_URL=http://:80/v1 ``` ### 问题 5:OSS 上传失败 **症状**? ``` [ERROR] OSS upload failed: AccessDenied ?[ERROR] OSS upload failed: InvalidAccessKeyId ``` **排查步骤**? ```bash # 1. 检查环境变?# SAE 控制??应用详情 ?环境变量 # 确认以下变量正确?OSS_REGION=oss-cn-beijing OSS_BUCKET=clinical-research-files OSS_ACCESS_KEY_ID=LTAI5t... OSS_ACCESS_KEY_SECRET=xxx... # 2. 测试 OSS 访问 # 在后端应用的 Webshell 中执行: node -e " const OSS = require('ali-oss'); const client = new OSS({ region: process.env.OSS_REGION, accessKeyId: process.env.OSS_ACCESS_KEY_ID, accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET, bucket: process.env.OSS_BUCKET }); client.list().then(result => console.log('?OSS connection successful')).catch(err => console.error('?OSS error:', err.message)); " ``` **解决方法**? ```bash # 如果 AccessKey 错误,重新生成: # 阿里云控制台 ?AccessKey 管理 ?创建 AccessKey # 更新 SAE 环境变量 # 如果 Bucket 权限错误?# OSS 控制??Bucket 列表 ?clinical-research-files ?访问控制 # 确认 Bucket ?私有",且 RAM 用户有读写权?``` ### 问题 6:内存泄? **症状**? ``` SAE 监控显示:内存使用率持续上涨,最终导?OOM(Out of Memory?日志:JavaScript heap out of memory ``` **排查步骤**? ```bash # 1. 查看内存趋势 # SAE 控制??应用详情 ?监控 ?选择"最?24 小时" # 如果内存曲线持续上涨,不下降,说明有内存泄漏 # 2. 查看实例内存详情 # ?Webshell 中执行: node -e "console.log(process.memoryUsage())" # 输出示例?# { # rss: 123456789, // 总内存(字节?# heapTotal: 45678910, // V8 堆总大?# heapUsed: 34567890, // V8 堆已使用 # external: 1234567, // 外部内存(Buffer等) # arrayBuffers: 123456 // ArrayBuffer # } # 3. 启用内存快照(本地调试) # 在本地开发环境添?--inspect 参数?node --inspect dist/index.js # 使用 Chrome DevTools 查看内存快照 ``` **常见内存泄漏原因**? 1. **全局变量累积**(如缓存无限增长?2. **事件监听器未移除** 3. **定时器未清理** 4. **闭包持有大对?* 5. **Prisma 查询结果未释?* **解决方法**? ```typescript // 错误示例:全局缓存无限增长 const cache = {}; // ?永不清理 // 正确示例:使?LRU 缓存 import LRUCache from 'lru-cache'; const cache = new LRUCache({ max: 1000, ttl: 1000 * 60 * 10 }); // ?最?1000 项,10 分钟过期 // 错误示例:事件监听器泄漏 emitter.on('event', handler); // ?从不移除 // 正确示例:及时移除监听器 emitter.once('event', handler); // ?自动移除 // ?emitter.on('event', handler); // 使用后: emitter.off('event', handler); // ?手动移除 // 错误示例:定时器泄漏 setInterval(() => { ... }, 1000); // ?永不清理 // 正确示例:清理定时器 const timer = setInterval(() => { ... }, 1000); process.on('SIGTERM', () => clearInterval(timer)); // ?优雅关闭时清?``` ### 问题 7:Prisma Schema 与数据库不一? **症状**? ```typescript // 代码中访问字段: const user = await prisma.user.findUnique({ where: { id: '123' } }); console.log(user.phone); // ?TypeScript 报错:Property 'phone' does not exist // 或运行时报错?PrismaClientKnownRequestError: column "phone" does not exist ``` **原因**? - 开发过程中直接修改了数据库(加?`phone` 字段?- 但没有更?`schema.prisma` - Prisma Client 基于旧的 Schema 生成,不知道新字? **解决方法**? ```bash # 1. 在本地开发环境,连接?RDS export DATABASE_URL="postgresql://username:password@pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432/ai_clinical" # 2. 反向同步 Schema npx prisma db pull # 3. 重新生成 Client npx prisma generate # 4. 提交代码 git add prisma/schema.prisma git commit -m "fix: sync Prisma schema with database" git push # 5. 重新构建镜像并部?cp -r prisma backend/prisma cd backend docker build -t backend-service:v1.0.1 . docker push registry.cn-hangzhou.aliyuncs.com/clinical-research/backend-service:v1.0.1 # 6. ?SAE 中更新镜像版?# SAE 控制??应用详情 ?部署 ?选择新版?v1.0.1 ``` --- ## 13. 注意事项与禁? ### ?最佳实? #### 1. **环境变量管理** ```bash # ?正确做法:使?SAE 环境变量 # SAE 控制??应用配置 ?环境变量 # ?错误做法:在代码?Dockerfile 中硬编码 # Dockerfile ENV DATABASE_URL="postgresql://user:pass@host/db" # ?泄露敏感信息 ``` #### 2. **连接池配?* ```bash # ?正确做法:根?RDS ?SAE 配置动态计?DATABASE_URL=postgresql://...?connection_limit=18&pool_timeout=10 # 计算公式?# connection_limit = (RDS_MAX_CONNECTIONS / MAX_INSTANCES) - 预留 # 示例?400 / 20) - 2 = 18 # ?错误做法:使用默认?DATABASE_URL=postgresql://... # ?默认无限制,导致连接耗尽 ``` #### 3. **文件存储** ```typescript // ?正确做法:使?OSS 存储 import { StorageFactory } from './common/storage'; const storage = StorageFactory.create(); // 根据 STORAGE_TYPE 自动选择 await storage.upload('uploads/file.pdf', buffer); // ?错误做法:存储到容器文件系统 import fs from 'fs'; fs.writeFileSync('/app/uploads/file.pdf', buffer); // ?容器重启后丢?``` #### 4. **日志输出** ```typescript // ?正确做法:使?stdout(SAE 自动收集?console.log('[INFO] User logged in:', userId); logger.info('User logged in', { userId }); // ?错误做法:写入文?import fs from 'fs'; fs.appendFileSync('/var/log/app.log', message); // ?容器重启后丢?``` #### 5. **优雅关闭** ```typescript // ?正确做法:监?SIGTERM 信号 process.on('SIGTERM', async () => { console.log('[Server] Received SIGTERM, shutting down gracefully...'); await fastify.close(); // 关闭 HTTP 服务? await prisma.$disconnect(); // 关闭数据库连? process.exit(0); }); // ?错误做法:直接退?process.on('SIGTERM', () => process.exit(0)); // ?连接未释?``` ### ?绝对禁止 #### 1. **禁止直接修改数据库而不同步 Prisma Schema(致命)** ```bash # ?错误做法?# 1. ?Navicat 中直接添加字?ALTER TABLE "User" ADD COLUMN phone VARCHAR(50); # 2. 然后直接在代码中使用 const user = await prisma.user.create({ data: { email: 'test@example.com', phone: '13800138000' // ?TypeScript 报错:phone 不存? } }); # ?正确做法?# 1. 先修?schema.prisma model User { email String phone String? @db.VarChar(50) // 添加字段定义 } # 2. 生成迁移(仅开发环境) npx prisma migrate dev --name add_user_phone # 3. 生成客户?npx prisma generate # 4. 在代码中使用 const user = await prisma.user.create({ data: { email: 'test@example.com', phone: '13800138000' // ?TypeScript 识别 } }); # 或者,如果数据库已手动修改?# 1. 反向同步 npx prisma db pull # 2. 生成客户?npx prisma generate ``` **原因**?- Prisma Client 基于 Schema 生成 - Schema 与数据库不一致会导致运行时错误或类型不匹?- 这是导致生产环境崩溃的最常见原因之一 #### 2. **禁止在启动命令中无脑执行 `prisma migrate deploy`** ```bash # ?错误做法(如果数据库是通过 pg_dump 导入的)?CMD ["sh", "-c", "npx prisma migrate deploy && node dist/index.js"] # 错误原因?# - 表结构已存在(通过 pg_dump 导入?# - migrate deploy 会尝试重新创建表 # - 导致错误:Table already exists # ?正确做法(针对您的情况)?CMD ["node", "dist/index.js"] # 解释?# - 数据库结构已通过 pg_dump 导入 # - Schema 已通过 prisma db pull 同步 # - 无需在启动时执行迁移 ``` #### 3. **禁止忽略 Prisma 反向同步步骤** ```bash # ?错误流程?pg_dump ?导入 RDS ?直接构建镜像 ?部署 # ?问题:Schema 与数据库不一? # ?正确流程?pg_dump ?导入 RDS ?prisma db pull(同步)?构建镜像 ?部署 # ?关键:prisma db pull 确保一致?``` #### 4. **禁止在代码中硬编码敏感信?* ```typescript // ?错误示例 const dbUrl = 'postgresql://admin:P@ssw0rd@pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432/ai_clinical'; // ?正确做法 const dbUrl = process.env.DATABASE_URL; ``` #### 5. **禁止?backend/ 目录构建前不复制 Prisma 文件** ```bash # ?错误做法?cd backend docker build -t backend-service:v1.0.0 . # ?错误:COPY prisma ./prisma 会失败(找不到文件) # ?正确做法?cd AIclinicalresearch # 项目根目?cp -r prisma backend/prisma # 先复?cd backend docker build -t backend-service:v1.0.0 . # 再构?``` #### 6. **禁止使用内存作为缓存(多实例环境?* ```typescript // ?错误示例:内存缓存不共享 const cache = new Map(); // 实例 1 的缓存,实例 2 看不? // ?正确做法:使?PostgreSQL 缓存(Postgres-Only?import { CacheFactory } from './common/cache'; const cache = CacheFactory.create(); // 根据 CACHE_TYPE 选择 ``` #### 7. **禁止使用本地文件作为队列** ```typescript // ?错误示例:文件队列不共享 import fs from 'fs'; fs.writeFileSync('/tmp/queue.json', JSON.stringify(tasks)); // 实例间不同步 // ?正确做法:使?pg-boss(Postgres-Only?import { jobQueue } from './common/jobs'; await jobQueue.send('pdf-extraction', { fileId: '123' }); ``` #### 8. **禁止使用同步阻塞操作** ```typescript // ?错误示例:阻塞事件循?import fs from 'fs'; const data = fs.readFileSync('/large-file.pdf'); // 阻塞其他请求 // ?正确做法:使用异?API const data = await fs.promises.readFile('/large-file.pdf'); ``` #### 9. **禁止跳过健康检?* ```typescript // ?错误示例:健康检查总是返回 200 app.get('/health', async (req, reply) => { return { status: 'ok' }; // 即使数据库断开也返?ok }); // ?正确做法:真正检查依赖服?app.get('/health', async (req, reply) => { const dbHealthy = await testDatabaseConnection(); if (!dbHealthy) { reply.code(503); return { status: 'unhealthy', database: 'disconnected' }; } return { status: 'healthy' }; }); ``` #### 10. **禁止忽略错误处理** ```typescript // ?错误示例:吞掉错?try { await riskyOperation(); } catch (error) { // 什么都不做 } // ?正确做法:记录错误并返回适当的响?try { await riskyOperation(); } catch (error) { logger.error('Risky operation failed', { error, stack: error.stack }); reply.code(500).send({ error: 'Internal server error' }); } ``` #### 11. **禁止在生产环境使?development 模式** ```bash # ?错误配置 NODE_ENV=development # 会打印大量调试日志,暴露敏感信息 # ?正确配置 NODE_ENV=production LOG_LEVEL=info ``` #### 12. **禁止使用?JWT 密钥** ```bash # ?错误配置 JWT_SECRET=secret # 太弱,容易被破解 # ?正确配置(至?32 位随机字符串?JWT_SECRET=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 # 使用 openssl rand -base64 32 生成 ``` #### 13. **禁止直接暴露 Prisma 错误到前?* ```typescript // ?错误示例:泄露数据库结构 try { await prisma.user.create({ data: { email: 'test@example.com' } }); } catch (error) { reply.code(500).send({ error: error.message }); // 可能暴露表名、字段名 } // ?正确做法:返回通用错误 try { await prisma.user.create({ data: { email: 'test@example.com' } }); } catch (error) { logger.error('Failed to create user', { error }); reply.code(500).send({ error: 'Failed to create user' }); } ``` #### 14. **禁止忽略 Docker 镜像优化** ```dockerfile # ?错误示例:单阶段构建,镜像臃?FROM node:22 COPY . . RUN npm install # 包含 devDependencies CMD ["node", "dist/index.js"] # ?正确做法:多阶段构建,减小镜像体?FROM node:22-alpine AS builder # ... 构建阶段 ... FROM node:22-alpine COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules # 仅生产依?``` --- ## 📚 相关文档 - [03-Dify-ECS部署完全指南.md](./03-Dify-ECS部署完全指南.md) - [04-Python微服?SAE容器部署指南.md](./04-Python微服?SAE容器部署指南.md) - [06-前端Nginx-SAE容器部署指南.md](./06-前端Nginx-SAE容器部署指南.md)(待创建? --- ## 🆘 获取帮助 **遇到问题?* 1. 查看本文档的"故障排查"章节 2. 查看 SAE 控制台的实时日志 3. 查看 RDS 控制台的性能监控 4. 联系团队技术支? --- **部署愉快!🚀**