# 云原生部署架构指南 > **文档版本:** V1.0 > **创建日期:** 2025-11-16 > **适用对象:** 后端开发、架构师、运维 > **维护者:** 架构团队 > **状态:** ✅ 已完成 --- ## 📋 文档说明 本文档提供 **AI临床研究平台** 部署到阿里云 Serverless 架构的完整指南。 > **⭐ 重要更新(2025-11-16)**: > 平台基础设施的详细实施计划和代码实现已迁移到: > **[平台基础设施规划](./04-平台基础设施规划.md)** > > 本文档聚焦于: > - 云原生架构总体设计 > - 阿里云服务选型和配置 > - Docker容器化和部署流程 > - 成本估算和监控告警 **文档定位**: - 本文档(03):**云原生部署架构总览** - 侧重云服务和部署流程 - 04文档:**平台基础设施规划** - 侧重代码实现和开发指南 **阅读时间**:20 分钟 **实施时间**:参见 [平台基础设施规划](./04-平台基础设施规划.md) 的2.5天实施计划 --- ## 🏗️ 架构详解 ### 1. Serverless 应用引擎 (SAE) #### **产品特性** | 特性 | 说明 | 优势 | |------|------|------| | **自动扩缩容** | 根据流量自动调整实例数(0-100) | 高峰期不宕机,低谷期省成本 | | **按需付费** | ¥0.000110592/请求次 + 实例费 | 初期月费约 ¥200-500 | | **容器化部署** | 支持 Docker 镜像 | 环境一致性,快速回滚 | | **内置负载均衡** | 自动分配流量 | 无需单独购买 SLB | | **健康检查** | 自动重启异常实例 | 提高可用性 | #### **实例规格选择** | 阶段 | 规格 | vCPU | 内存 | 适用场景 | |------|------|------|------|---------| | **开发/测试** | 0.5C1G | 0.5核 | 1GB | 日请求 < 1000 | | **初期** | 1C2G | 1核 | 2GB | 日请求 1000-5000 | | **成长期** | 2C4G | 2核 | 4GB | 日请求 5000-20000 | | **成熟期** | 4C8G | 4核 | 8GB | 日请求 > 20000 | **建议配置**: ```yaml # SAE 应用配置 实例规格: 1C2G 最小实例数: 1 # 避免冷启动 最大实例数: 10 CPU 触发扩容阈值: 70% 内存触发扩容阈值: 80% ``` --- ### 2. 云数据库 RDS (PostgreSQL 15) #### **规格选型** | 阶段 | 规格 | vCPU | 内存 | 最大连接数 | 月费 | |------|------|------|------|-----------|------| | **开发/测试** | 基础版 1C1G | 1核 | 1GB | 100 | ¥120 | | **初期** | 通用版 2C4G | 2核 | 4GB | 400 | ¥300 | | **成长期** | 通用版 4C8G | 4核 | 8GB | 800 | ¥600 | | **成熟期** | 独享版 8C16G | 8核 | 16GB | 1600 | ¥1200 | #### **关键配置** ```sql -- 查看当前最大连接数 SHOW max_connections; -- 查看当前活跃连接 SELECT count(*) FROM pg_stat_activity; -- 按数据库分组统计连接 SELECT datname, count(*) FROM pg_stat_activity GROUP BY datname; ``` **连接池计算公式**: ``` 每实例连接数 = RDS最大连接数 / SAE最大实例数 × 0.8(安全系数) 示例: RDS: 400连接 SAE: 最多10实例 每实例: 400 / 10 × 0.8 = 32连接 ``` --- ### 3. 对象存储 OSS #### **Bucket 配置** ```yaml Bucket名称: aiclinical-prod 区域: 华东1(杭州)oss-cn-hangzhou 存储类型: 标准存储 访问权限: 私有(Private) 版本控制: 开启 跨域设置: 允许前端域名 ``` #### **目录结构规划** ``` aiclinical-prod/ ├── asl/ │ ├── pdfs/ # PDF文件 │ ├── excel/ # Excel文件 │ └── exports/ # 导出文件 ├── avatars/ # 用户头像 ├── documents/ # 知识库文档 └── temp/ # 临时文件(1天后自动删除) ``` #### **生命周期管理** ```json { "Rules": [ { "ID": "delete-temp-files", "Prefix": "temp/", "Status": "Enabled", "Expiration": { "Days": 1 } }, { "ID": "archive-old-pdfs", "Prefix": "asl/pdfs/", "Status": "Enabled", "Transitions": [ { "Days": 90, "StorageClass": "IA" // 转为低频访问 } ] } ] } ``` --- ## 💻 存储抽象层设计(核心) ### 接口定义 **文件**:`backend/src/common/storage/StorageAdapter.ts` ```typescript /** * 存储抽象层接口 * * @description * - 支持本地文件系统 + 阿里云 OSS 无缝切换 * - 通过环境变量控制实现类 * * @example * const storage = StorageFactory.create() * const url = await storage.upload('files/doc.pdf', buffer) */ export interface StorageAdapter { /** * 上传文件 * @param key 存储键(路径),如 'asl/pdfs/xxx.pdf' * @param buffer 文件内容 * @returns 访问URL */ upload(key: string, buffer: Buffer): Promise /** * 下载文件 * @param key 存储键 * @returns 文件内容 */ download(key: string): Promise /** * 删除文件 * @param key 存储键 */ delete(key: string): Promise /** * 获取访问URL * @param key 存储键 * @returns 完整访问URL */ getUrl(key: string): string /** * 批量上传 * @param files 文件列表 * @returns URL列表 */ uploadMany(files: Array<{ key: string; buffer: Buffer }>): Promise } ``` --- ### LocalAdapter 实现(本地开发) **文件**:`backend/src/common/storage/LocalAdapter.ts` ```typescript import fs from 'fs/promises' import path from 'path' import { StorageAdapter } from './StorageAdapter.js' /** * 本地文件系统存储适配器 * * @description * - 用于本地开发环境 * - 文件存储在 ./uploads 目录 * - 通过 HTTP 访问:http://localhost:3001/uploads/xxx */ export class LocalAdapter implements StorageAdapter { private uploadDir: string private baseUrl: string constructor() { this.uploadDir = path.resolve(process.cwd(), 'uploads') this.baseUrl = process.env.BASE_URL || 'http://localhost:3001' // 确保目录存在 this.ensureUploadDir() } private async ensureUploadDir() { try { await fs.mkdir(this.uploadDir, { recursive: true }) } catch (error) { console.error('创建上传目录失败:', error) } } async upload(key: string, buffer: Buffer): Promise { const filePath = path.join(this.uploadDir, key) // 确保父目录存在 await fs.mkdir(path.dirname(filePath), { recursive: true }) // 写入文件 await fs.writeFile(filePath, buffer) // 返回访问URL return this.getUrl(key) } async download(key: string): Promise { const filePath = path.join(this.uploadDir, key) return await fs.readFile(filePath) } async delete(key: string): Promise { const filePath = path.join(this.uploadDir, key) try { await fs.unlink(filePath) } catch (error) { // 文件不存在时忽略错误 if ((error as any).code !== 'ENOENT') { throw error } } } getUrl(key: string): string { return `${this.baseUrl}/uploads/${key}` } async uploadMany(files: Array<{ key: string; buffer: Buffer }>): Promise { const urls = await Promise.all( files.map(file => this.upload(file.key, file.buffer)) ) return urls } } ``` --- ### OSSAdapter 实现(生产环境) **文件**:`backend/src/common/storage/OSSAdapter.ts` ```typescript import OSS from 'ali-oss' import { StorageAdapter } from './StorageAdapter.js' /** * 阿里云 OSS 存储适配器 * * @description * - 用于生产环境 * - 文件存储在阿里云 OSS * - 支持内网/外网访问 */ export class OSSAdapter implements StorageAdapter { private client: OSS private bucket: string private region: string constructor() { this.region = process.env.OSS_REGION! this.bucket = process.env.OSS_BUCKET! if (!this.region || !this.bucket) { throw new Error('OSS配置缺失:OSS_REGION 或 OSS_BUCKET 未设置') } this.client = new OSS({ region: this.region, accessKeyId: process.env.OSS_ACCESS_KEY_ID!, accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET!, bucket: this.bucket, // 使用内网endpoint(SAE访问OSS免流量费) internal: process.env.NODE_ENV === 'production', }) } async upload(key: string, buffer: Buffer): Promise { try { const result = await this.client.put(key, buffer) return result.url } catch (error) { console.error('OSS上传失败:', error) throw new Error(`OSS上传失败: ${key}`) } } async download(key: string): Promise { try { const result = await this.client.get(key) return result.content as Buffer } catch (error) { console.error('OSS下载失败:', error) throw new Error(`OSS下载失败: ${key}`) } } async delete(key: string): Promise { try { await this.client.delete(key) } catch (error) { console.error('OSS删除失败:', error) // 文件不存在时不抛出错误 } } getUrl(key: string): string { // 返回外网访问URL return `https://${this.bucket}.${this.region}.aliyuncs.com/${key}` } async uploadMany(files: Array<{ key: string; buffer: Buffer }>): Promise { // 并行上传(最多10个并发) const chunks = [] for (let i = 0; i < files.length; i += 10) { chunks.push(files.slice(i, i + 10)) } const urls: string[] = [] for (const chunk of chunks) { const chunkUrls = await Promise.all( chunk.map(file => this.upload(file.key, file.buffer)) ) urls.push(...chunkUrls) } return urls } /** * 生成签名URL(临时访问) * @param key 存储键 * @param expires 过期时间(秒),默认1小时 */ async getSignedUrl(key: string, expires: number = 3600): Promise { return this.client.signatureUrl(key, { expires }) } } ``` --- ### StorageFactory 工厂类 **文件**:`backend/src/common/storage/StorageFactory.ts` ```typescript import { StorageAdapter } from './StorageAdapter.js' import { LocalAdapter } from './LocalAdapter.js' import { OSSAdapter } from './OSSAdapter.js' /** * 存储工厂类 * * @description * - 根据环境变量自动选择存储实现 * - STORAGE_TYPE=local → LocalAdapter * - STORAGE_TYPE=oss → OSSAdapter */ export class StorageFactory { private static instance: StorageAdapter | null = null /** * 创建存储实例(单例模式) */ static create(): StorageAdapter { if (this.instance) { return this.instance } const storageType = process.env.STORAGE_TYPE || 'local' switch (storageType) { case 'oss': console.log('📦 使用阿里云 OSS 存储') this.instance = new OSSAdapter() break case 'local': console.log('📁 使用本地文件存储') this.instance = new LocalAdapter() break default: throw new Error(`未知的存储类型: ${storageType}`) } return this.instance } /** * 重置实例(用于测试) */ static reset() { this.instance = null } } // 导出单例 export const storage = StorageFactory.create() ``` --- ### 使用示例 ```typescript // backend/src/modules/asl/controllers/literatureController.ts import { storage } from '../../../common/storage/StorageFactory.js' import { prisma } from '../../../config/database.js' /** * 上传PDF文件 */ export async function uploadPdf(req, res) { try { const { literatureId } = req.params const file = await req.file() if (!file) { return res.status(400).send({ error: 'No file uploaded' }) } // 读取文件内容 const buffer = await file.toBuffer() // 生成存储键 const key = `asl/pdfs/${Date.now()}-${file.filename}` // ✅ 上传到存储(自动根据环境选择Local或OSS) const url = await storage.upload(key, buffer) // 保存到数据库 await prisma.aslLiterature.update({ where: { id: literatureId }, data: { pdfUrl: url, pdfOssKey: key, pdfFileSize: buffer.length, } }) res.send({ success: true, url, size: buffer.length }) } catch (error) { console.error('PDF上传失败:', error) res.status(500).send({ error: 'Upload failed' }) } } /** * Excel上传(不需要存储,直接解析) */ export async function importExcel(req, res) { const file = await req.file() const buffer = await file.toBuffer() // ✅ 直接从内存解析,不落盘 const workbook = xlsx.read(buffer, { type: 'buffer' }) const data = xlsx.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]) // 批量入库 await prisma.aslLiterature.createMany({ data }) res.send({ success: true, count: data.length }) } ``` --- ## 🔧 数据库连接池配置 ### Prisma配置 **文件**:`backend/src/config/database.ts` ```typescript import { PrismaClient } from '@prisma/client' // 环境判断 const isProduction = process.env.NODE_ENV === 'production' // 计算连接池大小 // 生产环境:RDS 400连接 / SAE 10实例 × 0.8 = 32连接/实例 // 开发环境:本地 PostgreSQL,5连接足够 const connectionLimit = isProduction ? 32 : 5 /** * Prisma 客户端配置 */ export const prisma = new PrismaClient({ log: isProduction ? ['error', 'warn'] // 生产环境只记录错误和警告 : ['query', 'error', 'warn'], // 开发环境记录所有查询 datasources: { db: { url: process.env.DATABASE_URL } }, // 关键配置:连接池 ...(isProduction && { // 仅在生产环境配置连接池限制 datasources: { db: { url: process.env.DATABASE_URL, // 连接池配置 pool: { timeout: 5, // 获取连接超时(秒) maxsize: connectionLimit, // 最大连接数 min: 2, // 最小保持连接 } } } }) }) // 优雅关闭 process.on('SIGINT', async () => { console.log('📦 正在关闭数据库连接...') await prisma.$disconnect() process.exit(0) }) process.on('SIGTERM', async () => { console.log('📦 收到SIGTERM,正在关闭数据库连接...') await prisma.$disconnect() process.exit(0) }) // 连接数监控(仅生产环境) if (isProduction) { setInterval(async () => { try { const result = await prisma.$queryRaw>` SELECT count(*) as count FROM pg_stat_activity WHERE datname = current_database() ` const connectionCount = Number(result[0].count) console.log(`📊 当前数据库连接数: ${connectionCount}`) // 告警阈值:80% const maxConnections = 400 // 根据RDS规格设置 if (connectionCount > maxConnections * 0.8) { console.error(`⚠️ 数据库连接数过高: ${connectionCount}/${maxConnections}`) } } catch (error) { console.error('连接数监控失败:', error) } }, 60000) // 每60秒检查一次 } ``` --- ## 🌍 环境变量管理 ### 本地开发环境 **文件**:`backend/.env.development` ```bash # 环境 NODE_ENV=development # 存储配置 STORAGE_TYPE=local BASE_URL=http://localhost:3001 # 数据库 DATABASE_URL=postgresql://postgres:postgres@localhost:5432/aiclinical_dev # LLM配置 LLM_API_KEY=sk-xxx LLM_BASE_URL=https://api.deepseek.com # 其他服务(本地无需配置) OSS_REGION= OSS_ACCESS_KEY_ID= OSS_ACCESS_KEY_SECRET= OSS_BUCKET= ``` --- ### 生产环境配置 **在SAE控制台配置,不要写入文件**: ```bash # 环境 NODE_ENV=production # 存储配置 STORAGE_TYPE=oss OSS_REGION=oss-cn-hangzhou OSS_ACCESS_KEY_ID=LTAI5t***(从RAM用户获取) OSS_ACCESS_KEY_SECRET=*** OSS_BUCKET=aiclinical-prod # 数据库 DATABASE_URL=postgresql://aiclinical:***@rm-xxx.mysql.rds.aliyuncs.com:5432/aiclinical_prod # LLM配置 LLM_API_KEY=sk-*** LLM_BASE_URL=https://api.deepseek.com # 日志级别 LOG_LEVEL=info ``` --- ## 🐳 Docker 配置 ### Dockerfile **文件**:`backend/Dockerfile` ```dockerfile # ==================== 构建阶段 ==================== FROM node:20-alpine AS builder WORKDIR /app # 复制依赖文件 COPY package*.json ./ COPY prisma ./prisma/ # 安装依赖(包括dev依赖,用于构建) RUN npm ci # 复制源代码 COPY . . # 生成 Prisma Client RUN npx prisma generate # 构建 TypeScript RUN npm run build # ==================== 生产阶段 ==================== FROM node:20-alpine WORKDIR /app # 复制依赖文件 COPY package*.json ./ COPY prisma ./prisma/ # 仅安装生产依赖 RUN npm ci --only=production # 生成 Prisma Client RUN npx prisma generate # 从构建阶段复制编译后的代码 COPY --from=builder /app/dist ./dist # 复制静态文件(如prompts) COPY prompts ./prompts COPY config ./config # 创建非root用户 RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 # 切换到非root用户 USER nodejs # 暴露端口 EXPOSE 3001 # 健康检查 HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ CMD node -e "require('http').get('http://localhost:3001/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" # 启动应用 CMD ["node", "dist/index.js"] ``` --- ### docker-compose.yml(本地测试) **文件**:`docker-compose.yml` ```yaml version: '3.8' services: # PostgreSQL数据库 postgres: image: postgres:15-alpine container_name: aiclinical-postgres environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: aiclinical_dev ports: - "5432:5432" volumes: - postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 # Redis(可选) redis: image: redis:7-alpine container_name: aiclinical-redis ports: - "6379:6379" volumes: - redis-data:/data # 后端应用 backend: build: context: ./backend dockerfile: Dockerfile container_name: aiclinical-backend ports: - "3001:3001" environment: NODE_ENV: development STORAGE_TYPE: local DATABASE_URL: postgresql://postgres:postgres@postgres:5432/aiclinical_dev depends_on: postgres: condition: service_healthy volumes: - ./backend/uploads:/app/uploads # 本地存储目录 volumes: postgres-data: redis-data: ``` **使用方式**: ```bash # 启动所有服务 docker-compose up -d # 查看日志 docker-compose logs -f backend # 停止服务 docker-compose down ``` --- ## 🚀 部署流程 ### Step 1: 准备阿里云服务 #### 1.1 开通云数据库 RDS ```bash # 1. 登录阿里云控制台 # 2. 搜索"云数据库 RDS" # 3. 创建实例 # - 数据库类型:PostgreSQL 15 # - 规格:2核4GB(通用型) # - 存储空间:20GB(SSD) # - 网络:VPC(与SAE同区域) # 4. 设置白名单(添加 SAE 应用的 VPC网段) # 5. 创建数据库用户 # 6. 创建数据库:aiclinical_prod ``` #### 1.2 开通对象存储 OSS ```bash # 1. 搜索"对象存储 OSS" # 2. 创建 Bucket # - Bucket名称:aiclinical-prod # - 区域:华东1(杭州) # - 存储类型:标准存储 # - 访问权限:私有 # 3. 创建 RAM 用户(用于API访问) # - 权限:AliyunOSSFullAccess # - 获取 AccessKeyId 和 AccessKeySecret ``` #### 1.3 开通容器镜像服务 ```bash # 1. 搜索"容器镜像服务 ACR" # 2. 创建命名空间:aiclinical # 3. 创建镜像仓库:backend # - 类型:私有 # - 地域:华东1(杭州) ``` --- ### Step 2: 构建和推送镜像 ```bash # 1. 登录阿里云容器镜像服务 docker login --username=<你的阿里云账号> registry.cn-hangzhou.aliyuncs.com # 2. 构建镜像 cd backend docker build -t aiclinical-backend:v1.0.0 . # 3. 打标签 docker tag aiclinical-backend:v1.0.0 \ registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:v1.0.0 # 4. 推送到阿里云 docker push registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:v1.0.0 ``` **脚本自动化**: **文件**:`backend/scripts/build-and-push.sh` ```bash #!/bin/bash VERSION=$1 if [ -z "$VERSION" ]; then echo "Usage: ./build-and-push.sh " echo "Example: ./build-and-push.sh v1.0.0" exit 1 fi echo "🔨 构建镜像: $VERSION" docker build -t aiclinical-backend:$VERSION . echo "🏷️ 打标签" docker tag aiclinical-backend:$VERSION \ registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:$VERSION echo "📤 推送到阿里云" docker push registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:$VERSION echo "✅ 完成!" echo "镜像地址: registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:$VERSION" ``` --- ### Step 3: 创建 SAE 应用 #### 3.1 基本配置 ```yaml 应用名称: aiclinical-backend 应用类型: 容器镜像 镜像地址: registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:v1.0.0 端口: 3001 健康检查路径: /health ``` #### 3.2 实例配置 ```yaml 实例规格: 1C2G 最小实例数: 1 最大实例数: 10 CPU触发扩容: 70% 内存触发扩容: 80% ``` #### 3.3 环境变量配置 在 SAE 控制台配置环境变量(**重要!**): ```bash NODE_ENV=production STORAGE_TYPE=oss DATABASE_URL=postgresql://aiclinical:***@rm-xxx.mysql.rds.aliyuncs.com:5432/aiclinical_prod OSS_REGION=oss-cn-hangzhou OSS_ACCESS_KEY_ID=LTAI5t*** OSS_ACCESS_KEY_SECRET=*** OSS_BUCKET=aiclinical-prod LLM_API_KEY=sk-*** LOG_LEVEL=info ``` #### 3.4 VPC网络配置 ```yaml VPC: 选择与RDS相同的VPC 安全组: 允许3001端口入站 公网访问: 开启(分配公网SLB) ``` --- ### Step 4: 部署应用 ```bash # 1. 在 SAE 控制台点击"部署应用" # 2. 选择镜像版本 # 3. 确认配置无误 # 4. 点击"确定"开始部署 # 等待3-5分钟,查看部署日志 ``` **部署成功标志**: - ✅ 实例状态:运行中 - ✅ 健康检查:通过 - ✅ 访问 `http:///health` 返回 200 --- ### Step 5: 验证部署 ```bash # 1. 健康检查 curl http:///health # 预期响应: { "status": "ok", "database": "connected", "timestamp": "2025-11-16T10:30:00.000Z" } # 2. API测试 curl http:///api/v1/health # 3. 查看日志 # 在 SAE 控制台 → 应用详情 → 实时日志 ``` --- ## 📊 成本估算 ### 初期阶段(100用户,日活20) | 服务 | 规格 | 月费 | |------|------|------| | SAE | 1C2G × 1实例 | ¥200 | | RDS | 2C4G 通用版 | ¥300 | | OSS | 100GB标准存储 + 10GB流量 | ¥15 | | ACR | 私有仓库 | ¥0(免费额度) | | **合计** | | **¥515/月** | ### 成长期(1000用户,日活200) | 服务 | 规格 | 月费 | |------|------|------| | SAE | 2C4G × 平均3实例 | ¥600 | | RDS | 4C8G 通用版 | ¥600 | | OSS | 500GB标准存储 + 50GB流量 | ¥70 | | CDN | 100GB流量(可选) | ¥20 | | **合计** | | **¥1290/月** | ### 成熟期(10000用户,日活2000) | 服务 | 规格 | 月费 | |------|------|------| | SAE | 4C8G × 平均10实例 | ¥2000 | | RDS | 8C16G 独享版 + 读写分离 | ¥2000 | | OSS | 2TB标准存储 + 500GB流量 | ¥300 | | CDN | 1TB流量 | ¥200 | | Redis | 2GB标准版 | ¥200 | | **合计** | | **¥4700/月** | --- ## 🔍 监控与告警 ### 应用性能监控(ARMS) ```yaml # 在 SAE 控制台开通 ARMS 监控指标: - RT(响应时间) - QPS(每秒请求数) - 错误率 - JVM内存 告警规则: - RT > 3秒 - 错误率 > 5% - 可用率 < 99% ``` ### 数据库监控 ```yaml # RDS 控制台 → 监控与报警 监控指标: - CPU利用率 - 内存利用率 - 连接数 - IOPS 告警规则: - CPU > 80% - 连接数 > 320(80%) - 磁盘使用率 > 80% ``` ### 成本告警 ```bash # 费用中心 → 费用预警 设置预算: - 每月预算: ¥1000 - 80%预警: ¥800 - 通知方式: 邮件 + 短信 ``` --- ## 📚 故障排查 ### 常见问题 #### 问题1:数据库连接失败 **症状**:应用启动失败,日志显示 `Connection refused` **解决方案**: ```bash # 1. 检查 RDS 白名单 # 确保添加了 SAE 应用的 VPC网段 # 2. 检查 DATABASE_URL 格式 # 正确格式: postgresql://user:pass@host:port/db # 3. 检查 RDS 实例状态 # 确保实例运行中 ``` --- #### 问题2:OSS 上传失败 **症状**:文件上传返回 403 Forbidden **解决方案**: ```bash # 1. 检查 RAM 用户权限 # 确保有 AliyunOSSFullAccess # 2. 检查 Bucket 权限 # 确保应用有写入权限 # 3. 检查环境变量 # OSS_ACCESS_KEY_ID 和 OSS_ACCESS_KEY_SECRET 是否正确 ``` --- #### 问题3:连接数耗尽 **症状**:`Connection pool exhausted` **解决方案**: ```typescript // 1. 检查当前连接数 const result = await prisma.$queryRaw` SELECT count(*) FROM pg_stat_activity ` // 2. 调整连接池配置 // 减少每实例连接数,或增加 RDS 规格 // 3. 检查是否有连接泄漏 // 确保所有查询都正确关闭 ``` --- ## 📝 更新日志 | 日期 | 版本 | 变更内容 | 维护者 | |------|------|---------|--------| | 2025-11-16 | V1.0 | 创建文档,定义云原生部署架构 | 架构团队 | --- ## 📚 相关文档 - [前后端模块化架构设计-V2](../00-系统总体设计/前后端模块化架构设计-V2.md) - 架构总纲 - [云原生开发规范](../04-开发规范/08-云原生开发规范.md) - DO/DON'T 检查清单 - [Schema隔离架构设计](./01-Schema隔离架构设计(10个).md) - [数据库连接配置](./02-数据库连接配置.md) --- **文档维护者:** 架构团队 **最后更新:** 2025-11-16 **文档状态:** ✅ 已完成