# 前端 Nginx - SAE 容器部署完全指南 **文档版本**: v1.2 (修正:Node 版本、Vite 版本、日志输出、时? **创建时间**: 2025-12-13 **最后修?*: 2025-12-13 **适用范围**: AIclinicalresearch 平台 - 前端静态资源服务(frontend-v2? **目标读?*: 运维工程师、前端开发工程师 **部署目标**: 阿里?SAE(Serverless 应用引擎)Nginx 容器部署 **v1.2 更新日志**?- 🔴 修正:Node 版本?18 改为 22(与开发环?v22.18.0 一致) - ?确认:Vite 版本 7.2(package.json 真实版本?- ⚠️ 修正:日志输出到 stdout/stderr(SAE 最佳实践) - ⚠️ 新增:容器时区设置(Asia/Shanghai?- ⚠️ 强化:后端地址必须显式配置(不给危险默认值) **v1.1 更新日志**?- 🛑 修正:基?**frontend-v2** 目录(不?frontend?- ?更新:React 19 + Vite 6 + Ant Design 6.0 - ?新增:Ant Design X 2.1 说明(AI 对话组件?- ?新增:模块化架构说明(framework + modules?- ?完善:构建路径和目录结构 --- ## 📋 目录 1. [为什么选择 SAE Nginx 容器部署](#1-为什么选择-sae-nginx-容器部署) 2. [部署前准备](#2-部署前准? 3. [前端服务分析](#3-前端服务分析) 4. [创建 Nginx 配置](#4-创建-nginx-配置) 5. [构建 Docker 镜像](#5-构建-docker-镜像) 6. [本地测试验证](#6-本地测试验证) 7. [推送到 ACR](#7-推送到-acr) 8. [SAE 应用配置](#8-sae-应用配置) 9. [端到端测试](#9-端到端测? 10. [监控与维护](#10-监控与维? 11. [故障排查](#11-故障排查) 12. [注意事项与禁忌](#12-注意事项与禁? --- ## 1. 为什么选择 SAE Nginx 容器部署 ### ?核心优势 | 优势 | 说明 | 对您的价?| |------|------|----------| | **极高性能** | Nginx 处理静态文件效率远高于 Node.js(serve ?next start?| 首屏加载快,用户体验?| | **SPA 路由支持** | React Router 刷新页面不会 404 | `try_files` 原生支持 | | **反向代理** | Nginx ?`/api` 转发到后端,前端无感?| ?CORS 配置,简化开?| | **缓存控制** | 精细控制静态资源缓存策?| 减少带宽消耗,加速加?| | **私有化就?* | Docker 镜像可直接交付给医院 | 满足医疗数据合规要求 | | **统一架构** | 与后端、Python 微服务统一使用 Docker | 一套部署流程,降低运维成本 | ### 🎯 为什么不?OSS + CDN? 根据之前的讨论,我们明确了医疗科研项目的特殊性: | 对比?| OSS + CDN | SAE Nginx 容器 | 评分 | |-------|-----------|----------------|------| | **私有化部?* | ?无法在医院内网部?OSS | ?Docker 镜像可直接交?| **容器?* | | **架构统一?* | ?前端 OSS,后?SAE,割?| ?全部 SAE 容器,统一管理 | **容器?* | | **CORS 处理** | ⚠️ 前端需要处理跨?| ?Nginx 反向代理,前端无?| **容器?* | | **成本(小规模?* | ?存储费用极低 | ⚠️ 需要运行实例(~100?月) | **OSS ?* | | **全球加?* | ?CDN 节点覆盖全球 | ?单地域部?| **OSS ?* | | **医疗场景适配** | ?不适合内网环境 | ?完美适配私有?| **容器?* | **结论**:对于需要私有化部署的医疗科研产品,SAE Nginx 容器是唯一选择? --- ## 2. 部署前准? ### ?前置条件检查清? #### 本地开发环? - [ ] Docker Desktop 已安装并运行(版?20.10+?- [ ] Node.js 已安装(版本 18+?- [ ] 前端代码已拉取到本地 - [ ] 前端在本地能正常启动(`npm run dev`?- [ ] 前端能成功构建(`npm run build`? 验证命令? ```bash # 检?Docker docker --version # 输出示例:Docker version 24.0.6 # 检?Node.js node --version # 输出示例:v18.17.0 # 检?npm npm --version # 输出示例?.6.7 # 测试构建 cd frontend npm run build # 应该成功生成 dist/ 目录 ``` #### 阿里云资? - [ ] **后端服务(SAE?* 已部署并运行 - 后端 VPC 内网地址已获取(?`http://172.17.x.x:3001`? - 后端健康检查可访问 - [ ] **阿里云容器镜像服?ACR** 已开? - 命名空间已创建(?`clinical-research`? - 登录密码已设? - [ ] **SAE 应用** 准备创建 - VPC 和交换机已选择(与后端在同一 VPC? #### 配置信息准备 ```bash # 后端服务内网地址(关键) BACKEND_SERVICE_URL=http://172.17.x.x:3001 # 如果需要配置环境变量(可选) # VITE_API_BASE_URL 在构建时注入(很少使用) ``` --- ## 3. 前端服务分析 ### 📦 技术栈 | 技?| 版本 | 用?| |------|------|------| | **React** | 19.2+ | UI 框架(最新版本) | | **Vite** | 7.2+ | 构建工具(极快,最新版本)| | **TypeScript** | 5.9+ | 类型安全 | | **Ant Design** | 6.0+ | UI 组件库(最新版本) | | **Ant Design X** | 2.1+ | AI 对话组件(新增) | | **React Router** | 7.9+ | 路由管理(SPA?| | **React Query** | 5.90+ | 数据获取和缓?| | **Axios** | 1.13+ | HTTP 客户?| | **AG Grid** | 34.3+ | 数据表格(DC 模块?| | **TailwindCSS** | 3.4+ | CSS 框架 | | **Zustand** | 5.0+ | 状态管?| **⚠️ 版本说明**?- **Node.js**: v22.18.0(开发环境真实版本) - **Vite**: 7.2.2(package.json 真实版本,最新稳定版?- 所有版本均来自 `frontend-v2/package.json` 真实代码 ### 📊 架构设计(模块化? ``` frontend-v2/ ├── src/ ? ├── framework/ # 框架?? ? ├── layout/ # 主布局 + 顶部导航 ? ? ├── router/ # 路由守卫 ? ? ├── permission/ # 权限管理 ? ? └── modules/ # 模块注册?? ?? ├── modules/ # 业务模块?? ? ├── aia/ # AI 智能问答 ? ? ├── pkb/ # 个人知识?? ? ├── asl/ # AI 智能文献 ? ? ├── dc/ # 数据清洗整理 ? ? ├── ssa/ # 智能统计分析 ? ? ├── st/ # 统计分析工具 ? ? └── rvw/ # 稿件审查系统 ? ?? └── shared/ # 共享能力?? ├── components/ # 共享组件(Chat 组件等) ? ├── api/ # 共享 API 工具 ? ├── hooks/ # 共享 Hooks ? └── utils/ # 工具函数 ``` ### 📊 API 调用方式(模块化设计? **每个模块有独立的 API 文件**? ```typescript // modules/asl/api/index.ts const API_BASE_URL = '/api/v1/asl'; async function request(url: string, options?: RequestInit): Promise { const response = await fetch(`${API_BASE_URL}${url}`, { ...options, headers: { 'Content-Type': 'application/json', ...options?.headers, }, }); return response.json(); } // modules/dc/api/toolC.ts const BASE_URL = '/api/v1/dc/tool-c'; // 使用 axios axios.post(`${BASE_URL}/upload`, formData); ``` **关键设计**?- ?每个模块独立管理自己?API(模块化?- ?使用相对路径 `/api/v1/...`(不硬编码后端地址?- ?混合使用 fetch ?axios(根据模块需求) - ?生产环境?Nginx 反向代理到后?- ?无需处理 CORS(同源请求) **示例请求流程**?``` ASL 模块:GET /api/v1/asl/projects ?Nginx 反向代理 ?后端服务:http://172.17.x.x:3001/api/v1/asl/projects ``` ### 📝 构建流程 ```bash # ⚠️ 注意:使?frontend-v2 目录(不?frontend?cd frontend-v2 # 1. 安装依赖 npm install # 2. 构建生产版本 npm run build # 实际执行:tsc -b && vite build # 3. 产物目录结构 dist/ ├── index.html # 入口 HTML(约 1KB? ├── assets/ ? ├── index-xxxxx.js # ?JS 文件(约 800KB,已压缩? ? ├── index-xxxxx.css # ?CSS 文件(约 100KB,含 Ant Design + TailwindCSS? ? ├── vendor-xxxxx.js # 第三方库(React、Ant Design 等) ? └── *.svg/png/... # 其他资源 └── vite.svg # Favicon # 预期构建时间?0-60 秒(取决于机器性能?# 产物总大小:?2-3MB(压缩后?``` **构建产物分析**?| 文件类型 | 大小(压缩前?| 大小(压缩后?| 说明 | |---------|--------------|--------------|------| | index.html | 1KB | 1KB | 入口 HTML | | index-xxxxx.js | ~2.5MB | ~800KB | 业务代码 | | vendor-xxxxx.js | ~3MB | ~1MB | 第三方库 | | index-xxxxx.css | ~300KB | ~100KB | 样式文件 | | 其他资源 | ~500KB | ~200KB | 图标、字体等 | | **总计** | **~6.3MB** | **~2.1MB** | Gzip 压缩?| ### 🔍 当前 API 配置分析 **frontend-v2 ?API 配置方式**? ```typescript // modules/asl/api/index.ts const API_BASE_URL = '/api/v1/asl'; // ?相对路径(推荐) // modules/dc/api/toolC.ts const BASE_URL = '/api/v1/dc/tool-c'; // ?相对路径(推荐) // modules/dc/api/toolB.ts const BASE_URL = '/api/v1/dc/tool-b'; // ?相对路径(推荐) ``` **开发环境代理配?*? ```typescript // vite.config.ts export default defineConfig({ server: { host: '0.0.0.0', port: 3000, proxy: { '/api': { target: 'http://localhost:3001', changeOrigin: true, }, }, }, }); ``` **生产环境配置策略**?- ?**不需?*设置任何环境变量(如 `VITE_API_BASE_URL`?- ?所有模块都使用相对路径 `/api/v1/...` - ?使用 Nginx 反向代理统一处理 - ?开发环境通过 Vite proxy 代理到后?- ?生产环境通过 Nginx 代理到后? **完美设计**?- ?无需在构建时注入环境变量 - ?同一套代码适用于开发和生产 - ?支持私有化部署(不依赖外部配置) --- ## 4. 创建 Nginx 配置 ### 📝 创建 nginx.conf **⚠️ 重要:在 `frontend-v2/` 目录下创?`nginx.conf`(不?`frontend/`?* ```nginx # Nginx 配置文件 - AI临床研究平台前端服务 # 用途:托管 React SPA + 反向代理后端 API user nginx; worker_processes auto; # 自动根据 CPU 核心数调? # ⚠️ 日志输出?stderr(SAE 会自动收集) error_log /dev/stderr warn; pid /var/run/nginx.pid; events { worker_connections 1024; # 每个 worker 进程的最大连接数 use epoll; # Linux 下使?epoll(高性能?} http { include /etc/nginx/mime.types; default_type application/octet-stream; # 日志格式 log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; # ⚠️ 日志输出?stdout(SAE 会自动收集,避免磁盘写满? access_log /dev/stdout main; # 性能优化 sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; # Gzip 压缩(减少传输大小) gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml; gzip_disable "msie6"; # 上游后端服务(Backend Service? upstream backend { # ⚠️ 重要:这里的地址在部署时需要替换为真实的后端内网地址 # SAE 部署时,通过环境变量注入,详?Dockerfile server ${BACKEND_SERVICE_HOST}:${BACKEND_SERVICE_PORT} fail_timeout=30s max_fails=3; # 如果有多个后端实例(负载均衡? # server 172.17.x.x:3001 weight=1; # server 172.16.0.31:3001 weight=1; keepalive 32; # 保持连接? } server { listen 80; server_name _; # 接受所有域? # 根目录(React 构建产物? root /usr/share/nginx/html; index index.html; # 字符? charset utf-8; # ==================== 静态资源处?==================== # 主页面(index.html? 不缓? location = / { try_files /index.html =404; add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; add_header Expires "0"; } location = /index.html { add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; add_header Expires "0"; } # JS/CSS 文件 - 强缓存(文件名带 hash? location ~* \.(js|css)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # 图片/字体文件 - 强缓? location ~* \.(png|jpg|jpeg|gif|ico|svg|webp|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # ==================== API 反向代理 ==================== # 后端 API 代理(关键配置) location /api/ { # 代理到后端服? proxy_pass http://backend; # 保留原始请求信息 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 超时配置(AI 对话、文件处理可能耗时较长? proxy_connect_timeout 300s; proxy_send_timeout 300s; proxy_read_timeout 300s; # 缓冲配置 proxy_buffering off; # 关闭缓冲(实时流式响应) proxy_request_buffering off; # 支持大文件上? # WebSocket 支持(如果后续需要) proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # 错误处理 proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; proxy_next_upstream_tries 2; } # ==================== SPA 路由支持 ==================== # React Router 路由回退 # 所有非文件请求都返?index.html(SPA 的核心) location / { try_files $uri $uri/ /index.html; # 禁用缓存(用户刷新时总是获取最新页面) add_header Cache-Control "no-cache, no-store, must-revalidate"; } # ==================== 安全加固 ==================== # 隐藏 Nginx 版本? server_tokens off; # 禁止访问隐藏文件 location ~ /\. { deny all; access_log off; log_not_found off; } # 禁止访问特定文件 location ~* \.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)$ { deny all; } # ==================== 健康检?==================== # SAE 健康检查端? location /health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } # Nginx 状态页(用于监控) location /nginx_status { stub_status on; access_log off; # 仅允许内网访? allow 10.0.0.0/8; allow 172.17.0.0/16; allow 192.168.0.0/16; deny all; } # ==================== 错误页面 ==================== error_page 404 /index.html; # SPA 路由回退 error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } } ``` ### 🔍 配置关键点解? #### 1. 后端地址动态注? ```nginx upstream backend { server ${BACKEND_SERVICE_HOST}:${BACKEND_SERVICE_PORT}; } ``` **问题**:Nginx 配置文件不支持环境变量? **解决方案**:在 Docker 启动时使?`envsubst` 替换占位符(详见?5 ?Dockerfile)? #### 2. SPA 路由支持 ```nginx location / { try_files $uri $uri/ /index.html; } ``` **原理**?- 用户访问 `/dashboard` ?Nginx 找不?`dashboard` 文件 ?返回 `index.html` - React Router 接管路由 ?渲染 Dashboard 组件 **如果没有这个配置**?- 用户刷新 `/dashboard` ?Nginx ?404 错误 ? #### 3. 反向代理配置 ```nginx location /api/ { proxy_pass http://backend; } ``` **请求流程**?``` 浏览器:GET /api/v1/projects ?Nginx:接收请? ?Nginx:proxy_pass http://backend ?后端服务:http://172.17.x.x:3001/api/v1/projects ?后端返回数据 ?Nginx 转发给浏览器 ``` **优势**?- ?前端和后端同源(都是 `https://your-domain.com`?- ?无需处理 CORS - ?前端代码无需知道后端真实地址 #### 4. 缓存策略 | 资源类型 | 缓存策略 | 原因 | |---------|---------|------| | `index.html` | `no-cache` | 确保用户总能获取最新版?| | `.js` / `.css` | `1y` + `immutable` | 文件名带 hash,内容不?| | 图片/字体 | `1y` + `immutable` | 静态资源,不会改变 | **为什?index.html 不缓存?** ``` 用户首次访问? ?下载 index.html(引?index-abc123.js? ?部署新版本:index.html 更新(引?index-xyz789.js? ?用户再次访问? - 如果 index.html 被缓??仍加?index-abc123.js(旧版本?? - 如果 index.html 不缓??加载 index-xyz789.js(新版本??``` --- ## 5. 构建 Docker 镜像 ### 📝 创建 Dockerfile **⚠️ 重要:在 `frontend-v2/` 目录下创?`Dockerfile`(不?`frontend/`?* ```dockerfile # ==================== 阶段 1: 构建阶段 ==================== # ⚠️ 使用 Node 22(与开发环境一致,避免 package-lock.json 版本冲突?FROM node:22-alpine AS builder # 设置工作目录 WORKDIR /app # 1. 复制依赖文件 COPY package*.json ./ # 2. 安装依赖 # 使用国内镜像加速(可选) # RUN npm config set registry https://registry.npmmirror.com RUN npm ci --only=production=false # 3. 复制源代?COPY . . # 4. 构建生产版本 # ⚠️ 注意:如果需要在构建时注入环境变量,在这里设?ARG # ARG VITE_API_BASE_URL # ENV VITE_API_BASE_URL=$VITE_API_BASE_URL RUN npm run build # 验证构建产物 RUN ls -la /app/dist/ # ==================== 阶段 2: 运行阶段 ==================== FROM nginx:1.25-alpine # 安装必要工具(包括时区数据) RUN apk add --no-cache \ bash \ gettext \ curl \ tzdata # 设置容器时区为上海(否则日志时间会比北京时间?8 小时?RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ echo "Asia/Shanghai" > /etc/timezone # 创建 Nginx 配置目录 RUN mkdir -p /etc/nginx/templates # 1. 复制 Nginx 配置模板(支持环境变量替换) COPY nginx.conf /etc/nginx/templates/nginx.conf.template # 2. 复制构建产物?Nginx 默认目录 COPY --from=builder /app/dist /usr/share/nginx/html # 3. 创建启动脚本(处理环境变量替换) RUN cat > /docker-entrypoint.sh <<'EOF' #!/bin/bash set -e # ⚠️ 关键:不给默认值,强制?SAE 控制台配?# 如果未配置,报错退出(避免使用错误的后端地址?if [ -z "$BACKEND_SERVICE_HOST" ]; then echo "?ERROR: BACKEND_SERVICE_HOST environment variable is required!" echo "Please configure it in SAE console with backend internal IP (e.g., 172.16.0.30)" exit 1 fi if [ -z "$BACKEND_SERVICE_PORT" ]; then echo "⚠️ WARNING: BACKEND_SERVICE_PORT not set, using default: 3001" export BACKEND_SERVICE_PORT=3001 fi echo "============================================" echo "Starting Frontend Nginx Service" echo "Backend Service: ${BACKEND_SERVICE_HOST}:${BACKEND_SERVICE_PORT}" echo "Container Timezone: $(cat /etc/timezone)" echo "Current Time: $(date)" echo "============================================" # 使用 envsubst 替换 Nginx 配置中的环境变量 envsubst '${BACKEND_SERVICE_HOST} ${BACKEND_SERVICE_PORT}' \ < /etc/nginx/templates/nginx.conf.template \ > /etc/nginx/nginx.conf # 验证 Nginx 配置 nginx -t # 启动 Nginx exec nginx -g 'daemon off;' EOF RUN chmod +x /docker-entrypoint.sh # 健康检?HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD curl -f http://localhost/health || exit 1 # 暴露端口 EXPOSE 80 # 启动命令 ENTRYPOINT ["/docker-entrypoint.sh"] ``` ### 📝 创建 .dockerignore **⚠️ 重要:在 `frontend-v2/` 目录下创?`.dockerignore`(不?`frontend/`?* ``` # Node.js node_modules npm-debug.log yarn-error.log .npm .yarn # 开发文?.env .env.* *.local # 构建产物(Dockerfile 中会重新生成?dist # 测试文件 test tests *.test.ts *.test.tsx *.spec.ts *.spec.tsx coverage .nyc_output # 文档和临时文?docs *.md !README.md .vscode .idea .DS_Store Thumbs.db # Git .git .gitignore .gitattributes # CI/CD .github .gitlab-ci.yml .travis.yml # 日志 *.log logs # 临时文件 temp tmp *.swp *.swo *~ # 编辑器配?.editorconfig .prettierrc .eslintrc* # TypeScript 配置(保?tsconfig.json,其他忽略) tsconfig.tsbuildinfo # Vite .vite vite.config.*.timestamp-* ``` ### 📝 Dockerfile 关键设计解析 #### 1. 多阶段构建(减小镜像体积? ```dockerfile # 阶段 1:构建(Node.js 环境?FROM node:22-alpine AS builder # ⚠️ 使用 Node 22(与开发环境一致) # 体积:~200MB(临时) RUN npm run build # 阶段 2:运行(Nginx 环境?FROM nginx:1.25-alpine # 体积:~25MB(最终) COPY --from=builder /app/dist /usr/share/nginx/html ``` **效果对比**?- ?单阶段构建:~700MB(包?Node.js + npm + 所有依赖) - ?多阶段构建:~40-60MB(仅 Nginx + 静态文件) **为什么使?Node 22**?- 开发环境使?Node 22.18.0+ - `package-lock.json` ?Node 22 生成(Lockfile version 3?- Node 18 可能导致依赖安装警告或失?- 保证构建环境与开发环境完全一? #### 2. 环境变量替换机制 ```bash # 启动脚本 envsubst '${BACKEND_SERVICE_HOST} ${BACKEND_SERVICE_PORT}' \ < /etc/nginx/templates/nginx.conf.template \ > /etc/nginx/nginx.conf ``` **流程**?``` nginx.conf.template(模板): server ${BACKEND_SERVICE_HOST}:${BACKEND_SERVICE_PORT}; ?envsubst 替换 nginx.conf(最终配置): server 172.17.x.x:3001; ``` #### 3. 健康检? ```dockerfile HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD curl -f http://localhost/health || exit 1 ``` **SAE 会使用这个健康检?*?- ?容器启动?30 秒开始检?- ??30 秒检查一?- ?连续失败 3 ??标记为不健康 ?SAE 重启容器 --- ## 6. 本地测试验证 ### 步骤 1:构建镜? ```bash # ⚠️ 重要:使?frontend-v2 目录(不?frontend?cd frontend-v2 # 确认当前目录正确 pwd # 应该输出?../AIclinicalresearch/frontend-v2 # 确认 package.json 存在 cat package.json | grep '"name"' # 应该输出?name": "frontend-v2" # 构建镜像(需?3-5 分钟?docker build -t frontend-service:v1.0.0 . # 查看镜像大小 docker images frontend-service:v1.0.0 # 预期大小:~40-60MB(Alpine 基础镜像 + 构建产物? # 查看镜像详情 docker inspect frontend-service:v1.0.0 | grep -A 5 "Size" ``` **如果构建失败**? ```bash # 常见问题 1:npm install 超时 # 解决:使用国内镜像源 # ?Dockerfile ?npm ci 之前添加?RUN npm config set registry https://registry.npmmirror.com # 常见问题 2:构建产物为?# 解决:检?package.json 中的 build 脚本 cat package.json | grep '"build"' # 应该输出? "build": "tsc -b && vite build", # 常见问题 3:TypeScript 编译错误 # 解决:检查是否有类型错误 npm run build # ?Docker 外先测试构建 # 常见问题 4:nginx.conf 文件找不?# 解决:确?nginx.conf ?frontend-v2/ 目录?ls nginx.conf ``` ### 步骤 2:本地运行测? ```bash # 运行容器(需要指定后端地址?docker run -d \ --name frontend-test \ -p 8080:80 \ -e BACKEND_SERVICE_HOST=host.docker.internal \ -e BACKEND_SERVICE_PORT=3001 \ frontend-service:v1.0.0 # 查看启动日志 docker logs frontend-test # 应该看到?# ============================================ # Starting Frontend Nginx Service # Backend Service: host.docker.internal:3001 # ============================================ # nginx: the configuration file /etc/nginx/nginx.conf syntax is ok # nginx: configuration file /etc/nginx/nginx.conf test is successful ``` **注意**:`host.docker.internal` ?Docker Desktop 特有的域名,指向宿主机? ### 步骤 3:测试静态资源访? ```bash # 测试主页 curl http://localhost:8080/ # 预期返回?# # # ... # # 测试健康检?curl http://localhost:8080/health # 预期返回?# healthy ``` ### 步骤 4:测?SPA 路由 ```bash # 访问不存在的路由(应该返?index.html,而不?404?curl http://localhost:8080/dashboard # 预期返回?# # # ...(与主页相同?# # 而不是: # # 404 Not Found # ...(Nginx 默认 404 页面?``` ### 步骤 5:测试反向代理(关键测试? **前提**:确保本地后端服务在 `http://localhost:3001` 运行? ```bash # 通过前端 Nginx 访问后端 API curl http://localhost:8080/api/v1/health # 预期返回(后端的响应): { "status": "healthy", "timestamp": "2025-12-13T10:30:00.000Z", "database": { "status": "connected" } } # 查看 Nginx 日志,确认代理成?docker logs frontend-test 2>&1 | grep "/api/v1/health" # 应该看到?# 172.17.0.1 - - [13/Dec/2025:10:30:00 +0000] "GET /api/v1/health HTTP/1.1" 200 123 ``` ### 步骤 6:浏览器测试 打开浏览器,访问 `http://localhost:8080` **测试清单**?- [ ] 页面能正常加载(不是空白页) - [ ] 样式正确显示(CSS 加载成功?- [ ] JavaScript 功能正常(交互正常) - [ ] 刷新页面不会 404(SPA 路由正常?- [ ] 浏览器开发者工?Network 标签? - [ ] API 请求路径?`/api/v1/...`(相对路径) - [ ] API 请求状态是 200(代理成功) - [ ] 没有 CORS 错误 ### 步骤 7:清理测试容? ```bash # 停止并删除测试容?docker stop frontend-test docker rm frontend-test ``` --- ## 7. 推送到 ACR ### 步骤 1:登?ACR ```bash # 登录(使?ACR 密码,不是阿里云账号密码?docker login --username=your-aliyun-account registry.cn-beijing.aliyuncs.com # 输入密码后看到: # Login Succeeded ``` ### 步骤 2:标记镜? ```bash # 格式:registry地址/命名空间/仓库?版本?docker tag frontend-service:v1.0.0 \ registry.cn-beijing.aliyuncs.com/clinical-research/frontend-service:v1.0.0 # 同时打一?latest 标签 docker tag frontend-service:v1.0.0 \ registry.cn-beijing.aliyuncs.com/clinical-research/frontend-service:latest ``` ### 步骤 3:推送镜? ```bash # 推送指定版?docker push registry.cn-beijing.aliyuncs.com/clinical-research/frontend-service:v1.0.0 # 推?latest docker push registry.cn-beijing.aliyuncs.com/clinical-research/frontend-service:latest # 推送过程需?1-3 分钟(镜像很小) ``` ### 步骤 4:验证推送成? 登录阿里云控制台 ?容器镜像服务 ?镜像仓库 ?`frontend-service` - 应该看到版本:`v1.0.0` ?`latest` - 镜像大小:~30-50MB - 推送时间:刚才的时? --- ## 8. SAE 应用配置 ### 步骤 1:创建应? **阿里云控制台** ?**SAE** ?**应用列表** ?**创建应用** | 配置?| ?| 说明 | |-------|-----|------| | **应用名称** | `frontend-service` | 前端服务 | | **命名空间** | 选择已创建的命名空间 | 与后端服务同一命名空间 | | **VPC** | 选择后端所?VPC | 必须与后端在同一 VPC | | **交换?* | 选择可用?| 建议多可用区 | | **应用实例规格** | 0.5?G | 前端Nginx占用资源很少 | | **实例数量** | 2 | 最?2 个实例(高可用) | **为什么只需?0.5?G?* | 服务类型 | 推荐规格 | 原因 | |---------|---------|------| | Node.js 后端 | 2?G | 需要处?AI 对话、数据库查询 | | Python 微服?| 2?G | PDF 提取?CPU 密集?| | **Nginx 前端** | **0.5?G** | **仅提供静态文件,消耗极?* | ### 步骤 2:配置镜? | 配置?| ?| |-------|-----| | **镜像类型** | 容器镜像服务企业版实?| | **镜像仓库** | `registry.cn-beijing.aliyuncs.com/clinical-research/frontend-service` | | **镜像版本** | `v1.0.0` | | **镜像拉取策略** | 总是拉取镜像 | ### 步骤 3:配置端? | 配置?| ?| |-------|-----| | **容器端口** | `80` | | **协议** | TCP | ### 步骤 4:配置环境变量(关键步骤? **⚠️ 极其重要:必须配置后端服务的内网地址,否则容器启动失?* #### 获取后端内网地址 1. **登录 SAE 控制?* ?**应用列表** ?**backend-service** ?**应用详情** 2. **查看"应用访问配置"** ?**VPC 内网访问地址** 复制地址,格式通常是: ``` 172.16.0.30:3001 ``` #### 配置环境变量 ```bash # ⚠️ 必须配置(否则容器启动失败) BACKEND_SERVICE_HOST=172.17.x.x # 可选配置(默认 3001?BACKEND_SERVICE_PORT=3001 ``` **⚠️ 重要说明**?- `BACKEND_SERVICE_HOST` **必须配置**,否则容器启动时会报错退?- 不要使用主机名(?`backend-service`),SAE 可能无法解析 - 必须使用后端服务?*内网 IP 地址**(从 SAE 控制台获取) **为什么要拆分?Host ?Port?* ```nginx # nginx.conf 中的配置?upstream backend { server ${BACKEND_SERVICE_HOST}:${BACKEND_SERVICE_PORT}; } # 如果不拆分,写成?server ${BACKEND_SERVICE_URL}; # ?无法解析 http://172.16.0.30:3001 # 拆分后: server 172.17.x.x:3001; # ?正确 ``` ### 步骤 5:配置健康检? **SAE 控制?* ?**应用配置** ?**健康检?* | 配置?| ?| 说明 | |-------|-----|------| | **检查方?* | HTTP 请求 | | | **检查路?* | `/health` | Nginx 健康检查端?| | **检查端?* | 80 | 与容器端口一?| | **检查协?* | HTTP | | | **初始延迟** | 30 ?| Nginx 启动很快 | | **检查间?* | 10 ?| | | **超时时间** | 3 ?| | | **不健康阈?* | 3 ?| 连续失败 3 次标记为不健?| | **健康阈?* | 2 ?| 连续成功 2 次标记为健康 | ### 步骤 6:配置弹性伸? **SAE 控制?* ?**应用配置** ?**弹性伸?* | 配置?| ?| 说明 | |-------|-----|------| | **最小实例数** | 2 | 高可用保?| | **最大实例数** | 5 | 前端流量波动通常不大 | | **扩容条件** | CPU > 60% 持续 3 分钟 | Nginx 很少达到这个阈?| | **缩容条件** | CPU < 20% 持续 5 分钟 | | **注意**:前?Nginx 很少需要扩容,因为静态文件服务极其高效? ### 步骤 7:配置公网访问(可选) **SAE 控制?* ?**应用配置** ?**应用访问配置** #### 选项 A:使?SAE 提供的公网地址(临时测试) - **优势**:免费,立即可用 - **劣势**:域名难记,HTTPS 证书?SAE ? #### 选项 B:绑定自定义域名(生产推荐) 1. **添加域名解析**? ``` your-domain.com ? CNAME ? frontend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com ``` 2. **配置 HTTPS 证书**? - 上传 SSL 证书(或使用免费证书? 3. **强制 HTTPS**? ```nginx # ?nginx.conf 中添加重定向 server { listen 80; server_name your-domain.com; return 301 https://$server_name$request_uri; } ``` ### 步骤 8:部署应? 点击"部署"按钮,SAE 将: 1. ?ACR 拉取镜像(~30 秒) 2. 启动容器实例(~30 秒) 3. 执行健康检查(~30 秒) 4. 流量切换(~10 秒) **总耗时**:约 2 分钟 --- ## 9. 端到端测? ### 步骤 1:获取应用访问地址 **SAE 控制?* ?**应用详情** ?**应用访问配置** 复制公网访问地址?``` https://frontend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com ``` ### 步骤 2:测试主页加? ```bash # 使用 curl 测试 curl -I https://frontend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com/ # 预期返回?# HTTP/2 200 # content-type: text/html # cache-control: no-cache, no-store, must-revalidate # ... # 完整响应 curl https://frontend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com/ | head -n 10 # 应该看到 HTML 内容 ``` ### 步骤 3:浏览器完整测试 打开浏览器,访问前端地址? #### 测试清单 A:页面基础功能 - [ ] **主页能正常加?* - 看到登录页面或首? - 样式正确显示(不是纯文本? - 图片和图标正常显? - [ ] **路由跳转正常** - 点击导航菜单,URL 变化 - 页面内容更新 - 浏览器后退/前进按钮正常工作 - [ ] **刷新页面不报?* - 访问 `/dashboard` - ?F5 刷新 - 页面正常显示(不?404? #### 测试清单 B:API 调用测试 - [ ] **登录功能** - 输入用户名和密码 - 点击登录 - 查看浏览器开发者工??Network 标签 - 应该看到:`POST /api/v1/auth/login` ?状?200 - [ ] **数据加载** - 访问项目列表? - 查看 Network 标签 - 应该看到:`GET /api/v1/projects` ?状?200 - 列表数据正常显示 - [ ] **文件上传** - 尝试上传一?PDF 文档 - 查看 Network 标签 - 应该看到:`POST /api/v1/knowledge-bases/.../documents` ?状?200 - 上传进度正常显示 #### 测试清单 C:性能测试 - [ ] **首屏加载时间** - 清除浏览器缓? - 刷新页面 - 查看 Network 标签 ?底部统计 - 总时间应 < 3 秒(国内访问? - [ ] **静态资源缓?* - 第一次访问:所有资源从服务器加? - 第二次访问:JS/CSS 文件显示"(from disk cache)" - index.html 显示状?200(每次都重新获取? - [ ] **Gzip 压缩** - 查看 Response Headers - 应该看到:`Content-Encoding: gzip` - JS 文件大小减少 60-70% ### 步骤 4:测试反向代理(验证 CORS 解决? ```bash # 打开浏览器开发者工??Console # 执行以下代码? fetch('/api/v1/health') .then(res => res.json()) .then(data => console.log(data)); # 预期输出?# {status: "healthy", timestamp: "...", ...} # 如果看到 CORS 错误?# ?Access to fetch at '/api/v1/health' from origin 'https://...' has been blocked by CORS policy # ?说明 Nginx 反向代理配置有问? # 正确的情况: # ?请求成功,无任何 CORS 错误 ``` ### 步骤 5:移动端测试(可选) 使用手机浏览器访问前端地址? - [ ] 页面能自适应移动端屏?- [ ] 触摸交互正常 - [ ] 页面加载速度可接? --- ## 10. 监控与维? ### 📊 SAE 自带监控 #### 1. 实时监控 **SAE 控制?* ?**应用详情** ?**监控** **关键指标**? | 指标 | 健康阈?| 告警阈?| 说明 | |------|---------|---------|------| | **CPU 使用?* | < 30% | > 60% | Nginx 极少超过 30% | | **内存使用?* | < 50% | > 80% | Nginx 内存占用很低 | | **请求 QPS** | - | - | 了解访问?| | **平均响应时间** | < 50ms | > 200ms | 静态文件响应极?| | **错误?* | < 0.1% | > 1% | 监控 404/50x 错误 | | **实例数量** | 2+ | - | 确保高可?| **性能基准(参考)**?``` 静?HTML:响应时?10-20ms JS/CSS 文件:响应时?10-30ms API 代理:响应时?50-500ms(取决于后端?首屏加载(全部资源)?-3?``` #### 2. 日志查看 **SAE 控制?* ?**应用详情** ?**日志** ?**实时日志** **关键日志示例**? ```bash # ?正常启动 ============================================ Starting Frontend Nginx Service Backend Service: 172.17.x.x:3001 ============================================ nginx: configuration file /etc/nginx/nginx.conf test is successful # ?正常请求(静态资源) 172.31.0.10 - - [13/Dec/2025:10:30:00 +0000] "GET / HTTP/1.1" 200 1234 172.31.0.10 - - [13/Dec/2025:10:30:01 +0000] "GET /assets/index-xxxxx.js HTTP/1.1" 200 567890 # ?正常请求(API 代理?172.31.0.10 - - [13/Dec/2025:10:30:02 +0000] "GET /api/v1/projects HTTP/1.1" 200 8765 # ⚠️ 警告日志?04 Not Found?172.31.0.10 - - [13/Dec/2025:10:30:03 +0000] "GET /favicon.ico HTTP/1.1" 404 153 # ?错误日志(后端连接失败) 2025/12/13 10:30:04 [error] 7#7: *1 connect() failed (111: Connection refused) while connecting to upstream client: 172.31.0.10, server: _, request: "GET /api/v1/projects HTTP/1.1" upstream: "http://172.17.x.x:3001/api/v1/projects" ``` #### 3. Nginx 状态监? ```bash # ?SAE Webshell 中执行(或通过内网访问?curl http://localhost/nginx_status # 输出示例?# Active connections: 15 # server accepts handled requests # 123456 123456 567890 # Reading: 2 Writing: 5 Waiting: 8 ``` **指标含义**?- `Active connections`: 当前活跃连接?- `accepts`: 已接受的连接总数 - `handled`: 已处理的连接总数 - `requests`: 已处理的请求总数 - `Reading`: 正在读取请求头的连接?- `Writing`: 正在写入响应的连接数 - `Waiting`: 空闲?Keep-Alive 连接? ### 🔧 日常维护任务 #### 每日检? ```bash # 1. 检查应用健康状?# SAE 控制??应用列表 ?查看运行状态(绿色为正常) # 2. 查看错误日志 # SAE 控制??应用详情 ?日志 ?筛?error 级别 # 3. 检查访问量 # SAE 控制??应用详情 ?监控 ?查看 QPS ``` #### 每周任务 ```bash # 1. 查看性能指标趋势 # SAE 控制??应用详情 ?监控 ?选择"最?7 ? # 关注?# - 响应时间是否变慢 # - 错误率是否增?# - 流量是否有异常波? # 2. 查看 404 错误 # 在日志中搜索 "404",分析原因: # - 资源确实不存在? # - SPA 路由配置问题?# - 外部爬虫访问不存在的路径?``` #### 每月任务 ```bash # 1. 更新前端代码(如有新版本?# 在本地重新构建镜像: cd frontend npm run build docker build -t frontend-service:v1.0.1 . docker push registry.cn-beijing.aliyuncs.com/clinical-research/frontend-service:v1.0.1 # 2. ?SAE 中更新镜?# SAE 控制??应用详情 ?部署 # 选择新镜像版本:v1.0.1 # 灰度发布:先更新 1 个实例,观察 5 分钟后全量发? # 3. 清理旧镜像(节省 ACR 存储空间?# ACR 控制??镜像仓库 ?frontend-service # 删除 30 天前的旧版本(保留最?3 个版本) # 4. 检?SSL 证书有效期(如果使用自定义域名) # SAE 控制??应用详情 ?应用访问配置 ?HTTPS 设置 # 证书到期?30 天更?``` ### 🚨 告警配置 **云监?* ?**应用监控** ?**创建告警规则** **推荐告警规则**? | 告警?| 阈?| 通知方式 | |-------|------|---------| | CPU 使用?> 60% 持续 5 分钟 | 告警 | 钉钉/邮件 | | 内存使用?> 80% 持续 5 分钟 | 告警 | 钉钉/邮件 | | 错误?> 1% 持续 3 分钟 | 紧?| 短信+钉钉 | | 实例健康检查失?> 3 ?| 紧?| 短信+钉钉 | | 平均响应时间 > 500ms 持续 5 分钟 | 告警 | 钉钉/邮件 | --- ## 11. 故障排查 ### 问题 1:页面加载空白(最常见? **症状**? 浏览器访问前端地址,看到空白页面? **排查步骤**? ```bash # 1. 检?HTML 是否正确返回 curl -I https://frontend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com/ # 如果返回 404 ?50x,说?Nginx 配置有问? # 2. 检查构建产物是否存?# 登录 SAE Webshell?ls -la /usr/share/nginx/html/ # 应该看到?# index.html # assets/index-xxxxx.js # assets/index-xxxxx.css # 如果 dist/ 目录为空,说明构建阶段失? # 3. 查看浏览器开发者工?Console # 如果看到?# Uncaught SyntaxError: Unexpected token '<' # 说明 JS 文件路径错误,返回了 HTML 而不?JS # 4. 检?Vite 构建配置 # vite.config.ts 中的 base 路径 # 应该是:base: '/'(默认) # 不要写成:base: '/app/'(会导致路径错误?``` **解决方法**? ```bash # 方法 1:重新构建镜像(本地验证?cd frontend npm run build ls -la dist/ # 确认构建产物存在 docker build -t frontend-service:v1.0.1 . # 方法 2:检?Dockerfile 是否正确复制?dist/ # 确保有这一行: COPY --from=builder /app/dist /usr/share/nginx/html ``` ### 问题 2:刷新页面报 404(SPA 路由问题? **症状**? - 访问 `/` 正常 - 点击链接跳转?`/dashboard` 正常 - 刷新页面(F5)→ 404 Not Found **原因**? Nginx 找不?`/dashboard` 文件,应该返?`index.html` ?React Router 接管? **排查步骤**? ```bash # 1. 检?Nginx 配置中的 try_files # 登录 SAE Webshell?cat /etc/nginx/nginx.conf | grep "try_files" # 应该看到?# try_files $uri $uri/ /index.html; # 如果没有这一行,说明 SPA 路由配置缺失 ``` **解决方法**? ```nginx # ?nginx.conf ?location / 块中添加?location / { try_files $uri $uri/ /index.html; } # 重新构建镜像并部?``` ### 问题 3:API 请求报错(反向代理问题) **症状**? - 前端页面正常显示 - 调用 API 时报错: - `Network Error` - `504 Gateway Timeout` - `502 Bad Gateway` **排查步骤**? ```bash # 1. 检查后端服务是否运?# SAE 控制??应用列表 ?backend-service ?查看状? # 2. 测试后端内网地址是否可达 # 登录前端应用?Webshell?curl http://172.17.x.x:3001/api/v1/health # 如果返回错误,说明: # - 后端服务未启?# - 内网地址配置错误 # - VPC 网络不? # 3. 检?Nginx 配置中的 upstream cat /etc/nginx/nginx.conf | grep -A 5 "upstream backend" # 应该看到正确的后端地址?# server 172.17.x.x:3001 fail_timeout=30s max_fails=3; # 4. 查看 Nginx 错误日志 tail -f /var/log/nginx/error.log | grep "upstream" ``` **解决方法**? ```bash # 方法 1:更新环境变?# SAE 控制??frontend-service ?应用配置 ?环境变量 # 确认?BACKEND_SERVICE_HOST=172.17.x.x # 正确的内?IP BACKEND_SERVICE_PORT=3001 # 重启应用使环境变量生? # 方法 2:测试内网连通?# 在前?Webshell 中: telnet 172.17.x.x 3001 # 如果连接失败,检查: # - 后端和前端是否在同一 VPC # - 安全组规则是否允许访?``` ### 问题 4:CORS 错误(反向代理未生效? **症状**? 浏览?Console 显示?``` Access to fetch at 'http://backend-service:3001/api/v1/projects' from origin 'https://frontend-service.com' has been blocked by CORS policy ``` **原因**? 前端代码中硬编码了后端地址,而不是使用相对路径? **排查步骤**? ```bash # 1. 检查前端代码中?API 调用 # src/api/request.ts cat src/api/request.ts | grep "baseURL" # ?错误示例?# baseURL: 'http://backend-service:3001/api/v1' # ?正确示例?# baseURL: '/api/v1' # 2. 检查浏览器 Network 标签 # 正确的请求路径应该是?# https://frontend-service.com/api/v1/projects # 而不是: # http://backend-service:3001/api/v1/projects ``` **解决方法**? ```typescript // src/api/request.ts const request = axios.create({ baseURL: '/api/v1', // ?使用相对路径 timeout: 30000, }); // 重新构建并部?``` ### 问题 5:静态资?404(路径问题) **症状**? - HTML 能加?- JS/CSS 文件 404 Not Found - 浏览?Console 显示? ``` GET https://frontend-service.com/assets/index-xxxxx.js 404 (Not Found) ``` **原因**? Vite 构建配置中的 `base` 路径不正确? **排查步骤**? ```bash # 1. 查看 index.html 中的资源路径 curl https://frontend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com/ | grep "assets" # 应该看到?# # 如果看到?# # ⚠️ 相对路径 # ?# # ?错误?base # 2. 检?vite.config.ts cat vite.config.ts | grep "base" ``` **解决方法**? ```typescript // vite.config.ts export default defineConfig({ base: '/', // ?默认值,绝对路径 // base: '/app/', // ?不要设置子路径(除非确实需要) plugins: [react()], }); // 重新构建并部?``` ### 问题 6:页面样式错乱(CSS 未加载) **症状**? 页面内容显示,但样式混乱(纯文本排版)? **排查步骤**? ```bash # 1. 检查浏览器 Network 标签 # 查看 CSS 文件是否成功加载 # 如果显示 404,参考问?5 # 2. 检?CSS 文件?MIME 类型 curl -I https://frontend-service-xxxxx.cn-hangzhou.sae.aliyuncs.com/assets/index-xxxxx.css # 应该看到?# Content-Type: text/css # 如果看到?# Content-Type: application/octet-stream # ?错误?MIME 类型 # 3. 检?Nginx 配置 cat /etc/nginx/nginx.conf | grep "include.*mime.types" ``` **解决方法**? ```nginx # 确保 nginx.conf 中包含: http { include /etc/nginx/mime.types; # ?必需 default_type application/octet-stream; ... } ``` ### 问题 7:环境变量未替换 **症状**? Nginx 启动失败,错误日志显示: ``` nginx: [emerg] host not found in upstream "${BACKEND_SERVICE_HOST}" in /etc/nginx/nginx.conf:45 ``` **原因**? `envsubst` 未正确替换环境变量? **排查步骤**? ```bash # 1. 检查启动脚?cat /docker-entrypoint.sh | grep "envsubst" # 2. 检查最终生成的 nginx.conf cat /etc/nginx/nginx.conf | grep "server.*backend" # 应该看到?# server 172.17.x.x:3001; # 如果看到?# server ${BACKEND_SERVICE_HOST}:${BACKEND_SERVICE_PORT}; # ?未替?``` **解决方法**? ```dockerfile # 确保 Dockerfile 中的启动脚本正确?envsubst '${BACKEND_SERVICE_HOST} ${BACKEND_SERVICE_PORT}' \ < /etc/nginx/templates/nginx.conf.template \ > /etc/nginx/nginx.conf # 注意?# - 单引号包裹变量名 # - 使用 .template 文件作为?# - 输出?/etc/nginx/nginx.conf ``` --- ## 12. 注意事项与禁? ### ?最佳实? #### 1. **API 调用使用相对路径** ```typescript // ?正确做法:相对路?const request = axios.create({ baseURL: '/api/v1', }); // 前端:https://your-domain.com // API:https://your-domain.com/api/v1/... // 同源请求,无 CORS 问题 // ?错误做法:硬编码后端地址 const request = axios.create({ baseURL: 'http://backend-service:3001/api/v1', }); // 跨域请求,会?CORS 问题 ``` #### 2. **Vite base 路径配置** ```typescript // ?正确做法:默认根路径 export default defineConfig({ base: '/', // 绝对路径,适合大多数场?}); // ⚠️ 仅在以下情况使用子路径: // - 部署?CDN 的子目录 // - 多个前端共享一个域?export default defineConfig({ base: '/app/', // 资源路径?app/assets/index.js }); ``` #### 3. **环境变量命名规范** ```bash # ?正确做法:拆?Host ?Port BACKEND_SERVICE_HOST=172.17.x.x BACKEND_SERVICE_PORT=3001 # ?错误做法:完?URL BACKEND_SERVICE_URL=http://172.17.x.x:3001 # Nginx 无法解析协议前缀 ``` #### 4. **缓存策略** ```nginx # ?正确做法:差异化缓存 location = /index.html { add_header Cache-Control "no-cache"; # 每次都获取最?} location ~* \.(js|css)$ { expires 1y; # 文件名带 hash,可以长期缓?} # ?错误做法:全部缓存或全部不缓?add_header Cache-Control "public, max-age=31536000"; # index.html 也缓?1 ?add_header Cache-Control "no-cache"; # 所有文件都不缓存(浪费带宽?``` #### 5. **健康检查端?* ```nginx # ?正确做法:独立的健康检查端?location /health { access_log off; return 200 "healthy\n"; } # ?错误做法:使用主页作为健康检?# 如果前端代码?Bug,健康检查也会失?``` ### ?绝对禁止 #### 1. **禁止在前端代码中硬编码后端地址** ```typescript // ?错误示例 const API_URL = 'http://172.16.0.30:3001/api/v1'; // 后果?// - 本地开发时需要修改代?// - 部署到生产环境时需要再次修?// - 跨域问题 // - 无法使用 Nginx 反向代理 // ?正确做法 const API_URL = '/api/v1'; // 相对路径,由 Nginx 代理 ``` #### 2. **禁止忽略 SPA 路由配置** ```nginx # ?错误配置(缺?try_files?location / { root /usr/share/nginx/html; } # 后果:刷新页面报 404 # ?正确配置 location / { try_files $uri $uri/ /index.html; } ``` #### 3. **禁止?index.html 设置为长期缓?* ```nginx # ?错误示例 location / { expires 1y; add_header Cache-Control "public, immutable"; } # 后果:部署新版本后,用户仍看到旧版本 # ?正确做法 location = /index.html { add_header Cache-Control "no-cache"; } ``` #### 4. **禁止?Nginx 配置中使用未经环境变量替换的占位?* ```nginx # ?错误示例(直接写占位符) upstream backend { server ${BACKEND_SERVICE_HOST}:${BACKEND_SERVICE_PORT}; } # 如果没有 envsubst 替换,Nginx 会启动失? # ?正确做法(使?envsubst?envsubst '${BACKEND_SERVICE_HOST} ${BACKEND_SERVICE_PORT}' \ < nginx.conf.template > /etc/nginx/nginx.conf ``` #### 5. **禁止忽略 Gzip 压缩** ```nginx # ?错误做法(不压缩?# 传输大小?00KB JS 文件 ?500KB # ?正确做法(启?Gzip?gzip on; gzip_types text/plain text/css application/javascript; # 传输大小?00KB JS 文件 ?150KB(节?70%?``` #### 6. **禁止在生产环境暴露敏感信?* ```nginx # ?错误做法 server_tokens on; # 暴露 Nginx 版本?autoindex on; # 允许目录浏览 # ?正确做法 server_tokens off; autoindex off; ``` #### 7. **禁止使用过长的超时时?* ```nginx # ?错误示例?0 分钟超时?proxy_read_timeout 1800s; # 后果:占用连接资源,影响并发能力 # ?正确做法(根据实际需求) proxy_read_timeout 300s; # 5 分钟(AI 对话足够?``` #### 8. **禁止忽略错误处理** ```nginx # ?错误做法(后端挂了,Nginx 直接返回 502?proxy_pass http://backend; # ?正确做法(重试其他实例) proxy_next_upstream error timeout http_502 http_503; proxy_next_upstream_tries 2; ``` #### 9. **禁止?Dockerfile 中使?root 用户运行 Nginx** ```dockerfile # ?错误做法 USER root # 安全风险 # ?正确做法 USER nginx # Nginx 官方镜像默认已创?nginx 用户 # 或者不指定,Alpine 镜像默认使用 nginx 用户 ``` #### 10. **禁止使用错误?Node 版本** ```dockerfile # ?错误示例:使?Node 18(与开发环境不一致) FROM node:18-alpine AS builder # 风险:package-lock.json 版本冲突 # ?正确做法:使?Node 22(与开发环境一致) FROM node:22-alpine AS builder # 保证依赖安装和构建环境一?``` #### 11. **禁止将日志写入文?* ```nginx # ?错误做法(日志写入文件) access_log /var/log/nginx/access.log main; # 风险:可能写满容器磁? # ?正确做法(日志输出到标准流) access_log /dev/stdout main; error_log /dev/stderr warn; # SAE 会自动收集到日志中心 ``` #### 12. **禁止忽略容器时区设置** ```dockerfile # ?错误做法(不设置时区,默?UTC?# 问题:日志时间比北京时间?8 小时 # ?正确做法(设置为 Asia/Shanghai?RUN apk add --no-cache tzdata && \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ echo "Asia/Shanghai" > /etc/timezone ``` #### 13. **禁止给后端地址危险的默认?* ```bash # ?错误做法(给默认值) export BACKEND_SERVICE_HOST=${BACKEND_SERVICE_HOST:-backend-service} # 风险:可能连接到错误的后? # ?正确做法(强制要求配置) if [ -z "$BACKEND_SERVICE_HOST" ]; then echo "ERROR: BACKEND_SERVICE_HOST is required!" exit 1 fi ``` #### 14. **禁止忽略 Docker 镜像优化** ```dockerfile # ?错误示例:单阶段构建 FROM node:22 RUN npm install RUN npm run build CMD ["nginx"] # 镜像大小:~600MB # ?正确做法:多阶段构建 FROM node:22-alpine AS builder RUN npm run build FROM nginx:1.25-alpine COPY --from=builder /app/dist /usr/share/nginx/html # 镜像大小:~40-60MB(节?90%?``` --- ## 📚 相关文档 - [05-Node.js后端-SAE容器部署指南.md](./05-Node.js后端-SAE容器部署指南.md) - [04-Python微服?SAE容器部署指南.md](./04-Python微服?SAE容器部署指南.md) - [03-Dify-ECS部署完全指南.md](./03-Dify-ECS部署完全指南.md) --- ## 🆘 获取帮助 **遇到问题?* 1. 查看本文档的"故障排查"章节 2. 查看 SAE 控制台的实时日志 3. 查看 Nginx 错误日志:`/var/log/nginx/error.log` 4. 使用浏览器开发者工?Network 标签分析请求 5. 联系团队技术支? --- **部署愉快!🚀**