570 lines
12 KiB
Markdown
570 lines
12 KiB
Markdown
# Mobile Upgrade Admin - 部署指南
|
||
|
||
## 部署概述
|
||
|
||
本文档描述 Mobile Upgrade Admin 前端应用的部署方案,包括本地构建、Docker 容器化和生产环境部署。
|
||
|
||
## 部署架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Nginx / Caddy │
|
||
│ (Reverse Proxy) │
|
||
└─────────────────┬───────────────────────┬───────────────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌─────────────────┐ ┌─────────────────┐
|
||
│ Mobile Upgrade │ │ Admin Service │
|
||
│ Frontend (3001)│ │ Backend (3000) │
|
||
└─────────────────┘ └─────────────────┘
|
||
```
|
||
|
||
## 环境要求
|
||
|
||
| 环境 | 要求 |
|
||
|------|------|
|
||
| Node.js | >= 18.x |
|
||
| npm | >= 9.x |
|
||
| Docker | >= 20.x (可选) |
|
||
| 内存 | >= 512MB |
|
||
| 磁盘 | >= 500MB |
|
||
|
||
## 环境变量配置
|
||
|
||
### 开发环境
|
||
|
||
```bash
|
||
# .env.local
|
||
NEXT_PUBLIC_API_URL=http://localhost:3000
|
||
```
|
||
|
||
### 生产环境
|
||
|
||
```bash
|
||
# .env.production
|
||
NEXT_PUBLIC_API_URL=https://api.example.com
|
||
```
|
||
|
||
| 变量 | 说明 | 默认值 |
|
||
|------|------|--------|
|
||
| NEXT_PUBLIC_API_URL | Admin Service API 地址 | http://localhost:3000 |
|
||
| PORT | 应用监听端口 | 3000 |
|
||
|
||
## 部署方式
|
||
|
||
### 方式一:本地构建部署
|
||
|
||
#### 1. 构建生产版本
|
||
|
||
```bash
|
||
cd frontend/mobile-upgrade
|
||
|
||
# 安装依赖
|
||
npm ci
|
||
|
||
# 构建
|
||
npm run build
|
||
```
|
||
|
||
构建产物在 `.next/` 目录。
|
||
|
||
#### 2. 启动生产服务
|
||
|
||
```bash
|
||
# 使用 Node.js 直接运行
|
||
npm run start
|
||
|
||
# 或指定端口
|
||
PORT=3001 npm run start
|
||
```
|
||
|
||
#### 3. 使用 PM2 管理
|
||
|
||
```bash
|
||
# 安装 PM2
|
||
npm install -g pm2
|
||
|
||
# 启动应用
|
||
pm2 start npm --name "mobile-upgrade" -- start
|
||
|
||
# 查看状态
|
||
pm2 status
|
||
|
||
# 查看日志
|
||
pm2 logs mobile-upgrade
|
||
|
||
# 重启
|
||
pm2 restart mobile-upgrade
|
||
|
||
# 停止
|
||
pm2 stop mobile-upgrade
|
||
```
|
||
|
||
PM2 配置文件:
|
||
|
||
```javascript
|
||
// ecosystem.config.js
|
||
module.exports = {
|
||
apps: [{
|
||
name: 'mobile-upgrade',
|
||
script: 'npm',
|
||
args: 'start',
|
||
cwd: '/path/to/mobile-upgrade',
|
||
env: {
|
||
NODE_ENV: 'production',
|
||
PORT: 3001,
|
||
},
|
||
instances: 1,
|
||
autorestart: true,
|
||
watch: false,
|
||
max_memory_restart: '500M',
|
||
}],
|
||
}
|
||
```
|
||
|
||
### 方式二:Docker 容器部署
|
||
|
||
#### 1. Dockerfile
|
||
|
||
```dockerfile
|
||
# Dockerfile
|
||
FROM node:18-alpine AS base
|
||
|
||
# 安装依赖阶段
|
||
FROM base AS deps
|
||
WORKDIR /app
|
||
COPY package.json package-lock.json ./
|
||
RUN npm ci
|
||
|
||
# 构建阶段
|
||
FROM base AS builder
|
||
WORKDIR /app
|
||
COPY --from=deps /app/node_modules ./node_modules
|
||
COPY . .
|
||
|
||
# 设置环境变量
|
||
ARG NEXT_PUBLIC_API_URL
|
||
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
||
|
||
RUN npm run build
|
||
|
||
# 运行阶段
|
||
FROM base AS runner
|
||
WORKDIR /app
|
||
|
||
ENV NODE_ENV production
|
||
|
||
RUN addgroup --system --gid 1001 nodejs
|
||
RUN adduser --system --uid 1001 nextjs
|
||
|
||
COPY --from=builder /app/public ./public
|
||
|
||
# 复制构建产物
|
||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||
|
||
USER nextjs
|
||
|
||
EXPOSE 3000
|
||
|
||
ENV PORT 3000
|
||
ENV HOSTNAME "0.0.0.0"
|
||
|
||
CMD ["node", "server.js"]
|
||
```
|
||
|
||
#### 2. 更新 next.config.js
|
||
|
||
```javascript
|
||
// next.config.js
|
||
/** @type {import('next').NextConfig} */
|
||
const nextConfig = {
|
||
output: 'standalone', // 启用独立输出模式
|
||
// ... 其他配置
|
||
}
|
||
|
||
module.exports = nextConfig
|
||
```
|
||
|
||
#### 3. 构建 Docker 镜像
|
||
|
||
```bash
|
||
# 构建镜像
|
||
docker build \
|
||
--build-arg NEXT_PUBLIC_API_URL=https://api.example.com \
|
||
-t mobile-upgrade:latest \
|
||
.
|
||
|
||
# 运行容器
|
||
docker run -d \
|
||
--name mobile-upgrade \
|
||
-p 3001:3000 \
|
||
mobile-upgrade:latest
|
||
```
|
||
|
||
#### 4. Docker Compose
|
||
|
||
```yaml
|
||
# docker-compose.yml
|
||
version: '3.8'
|
||
|
||
services:
|
||
mobile-upgrade:
|
||
build:
|
||
context: .
|
||
args:
|
||
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://admin-service:3000}
|
||
image: mobile-upgrade:latest
|
||
container_name: mobile-upgrade
|
||
ports:
|
||
- "3001:3000"
|
||
environment:
|
||
- NODE_ENV=production
|
||
restart: unless-stopped
|
||
networks:
|
||
- rwa-network
|
||
depends_on:
|
||
- admin-service
|
||
|
||
networks:
|
||
rwa-network:
|
||
external: true
|
||
```
|
||
|
||
启动命令:
|
||
|
||
```bash
|
||
# 启动
|
||
docker-compose up -d
|
||
|
||
# 查看日志
|
||
docker-compose logs -f mobile-upgrade
|
||
|
||
# 停止
|
||
docker-compose down
|
||
```
|
||
|
||
### 方式三:静态导出部署
|
||
|
||
适用于不需要 SSR 的场景。
|
||
|
||
#### 1. 配置静态导出
|
||
|
||
```javascript
|
||
// next.config.js
|
||
/** @type {import('next').NextConfig} */
|
||
const nextConfig = {
|
||
output: 'export',
|
||
trailingSlash: true,
|
||
images: {
|
||
unoptimized: true,
|
||
},
|
||
}
|
||
|
||
module.exports = nextConfig
|
||
```
|
||
|
||
#### 2. 构建静态文件
|
||
|
||
```bash
|
||
npm run build
|
||
```
|
||
|
||
静态文件输出到 `out/` 目录。
|
||
|
||
#### 3. 使用 Nginx 部署
|
||
|
||
```nginx
|
||
# nginx.conf
|
||
server {
|
||
listen 80;
|
||
server_name upgrade.example.com;
|
||
root /var/www/mobile-upgrade;
|
||
|
||
location / {
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
|
||
location /api {
|
||
proxy_pass http://admin-service:3000;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
}
|
||
}
|
||
```
|
||
|
||
## 反向代理配置
|
||
|
||
### Nginx 配置
|
||
|
||
```nginx
|
||
# /etc/nginx/sites-available/mobile-upgrade
|
||
upstream mobile_upgrade {
|
||
server 127.0.0.1:3001;
|
||
}
|
||
|
||
upstream admin_service {
|
||
server 127.0.0.1:3000;
|
||
}
|
||
|
||
server {
|
||
listen 80;
|
||
server_name upgrade.example.com;
|
||
|
||
# 前端应用
|
||
location / {
|
||
proxy_pass http://mobile_upgrade;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection 'upgrade';
|
||
proxy_set_header Host $host;
|
||
proxy_cache_bypass $http_upgrade;
|
||
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;
|
||
}
|
||
|
||
# API 代理
|
||
location /api {
|
||
proxy_pass http://admin_service;
|
||
proxy_http_version 1.1;
|
||
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;
|
||
|
||
# 大文件上传支持
|
||
client_max_body_size 200M;
|
||
proxy_read_timeout 300s;
|
||
proxy_connect_timeout 75s;
|
||
}
|
||
}
|
||
```
|
||
|
||
### Caddy 配置
|
||
|
||
```caddyfile
|
||
# Caddyfile
|
||
upgrade.example.com {
|
||
reverse_proxy /api/* admin-service:3000
|
||
reverse_proxy /* mobile-upgrade:3001
|
||
}
|
||
```
|
||
|
||
## 健康检查
|
||
|
||
### 应用健康检查
|
||
|
||
```typescript
|
||
// src/app/api/health/route.ts
|
||
export async function GET() {
|
||
return Response.json({
|
||
status: 'healthy',
|
||
timestamp: new Date().toISOString(),
|
||
})
|
||
}
|
||
```
|
||
|
||
### Docker 健康检查
|
||
|
||
```dockerfile
|
||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
|
||
```
|
||
|
||
### Kubernetes 探针
|
||
|
||
```yaml
|
||
livenessProbe:
|
||
httpGet:
|
||
path: /api/health
|
||
port: 3000
|
||
initialDelaySeconds: 10
|
||
periodSeconds: 30
|
||
|
||
readinessProbe:
|
||
httpGet:
|
||
path: /api/health
|
||
port: 3000
|
||
initialDelaySeconds: 5
|
||
periodSeconds: 10
|
||
```
|
||
|
||
## CI/CD 集成
|
||
|
||
### GitHub Actions
|
||
|
||
```yaml
|
||
# .github/workflows/deploy.yml
|
||
name: Deploy
|
||
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
paths:
|
||
- 'frontend/mobile-upgrade/**'
|
||
|
||
jobs:
|
||
build-and-deploy:
|
||
runs-on: ubuntu-latest
|
||
|
||
steps:
|
||
- uses: actions/checkout@v3
|
||
|
||
- name: Setup Node.js
|
||
uses: actions/setup-node@v3
|
||
with:
|
||
node-version: 18
|
||
cache: 'npm'
|
||
cache-dependency-path: frontend/mobile-upgrade/package-lock.json
|
||
|
||
- name: Install dependencies
|
||
working-directory: frontend/mobile-upgrade
|
||
run: npm ci
|
||
|
||
- name: Build
|
||
working-directory: frontend/mobile-upgrade
|
||
run: npm run build
|
||
env:
|
||
NEXT_PUBLIC_API_URL: ${{ secrets.API_URL }}
|
||
|
||
- name: Build Docker image
|
||
working-directory: frontend/mobile-upgrade
|
||
run: |
|
||
docker build \
|
||
--build-arg NEXT_PUBLIC_API_URL=${{ secrets.API_URL }} \
|
||
-t mobile-upgrade:${{ github.sha }} \
|
||
.
|
||
|
||
- name: Push to registry
|
||
run: |
|
||
docker tag mobile-upgrade:${{ github.sha }} registry.example.com/mobile-upgrade:${{ github.sha }}
|
||
docker push registry.example.com/mobile-upgrade:${{ github.sha }}
|
||
|
||
- name: Deploy to server
|
||
uses: appleboy/ssh-action@master
|
||
with:
|
||
host: ${{ secrets.SERVER_HOST }}
|
||
username: ${{ secrets.SERVER_USER }}
|
||
key: ${{ secrets.SERVER_SSH_KEY }}
|
||
script: |
|
||
docker pull registry.example.com/mobile-upgrade:${{ github.sha }}
|
||
docker stop mobile-upgrade || true
|
||
docker rm mobile-upgrade || true
|
||
docker run -d \
|
||
--name mobile-upgrade \
|
||
-p 3001:3000 \
|
||
--restart unless-stopped \
|
||
registry.example.com/mobile-upgrade:${{ github.sha }}
|
||
```
|
||
|
||
## 监控和日志
|
||
|
||
### 日志收集
|
||
|
||
```bash
|
||
# Docker 日志
|
||
docker logs -f mobile-upgrade
|
||
|
||
# PM2 日志
|
||
pm2 logs mobile-upgrade
|
||
|
||
# 日志文件位置
|
||
/var/log/mobile-upgrade/access.log
|
||
/var/log/mobile-upgrade/error.log
|
||
```
|
||
|
||
### 性能监控
|
||
|
||
建议集成:
|
||
- **Prometheus + Grafana**: 指标监控
|
||
- **Sentry**: 错误追踪
|
||
- **Datadog / New Relic**: APM
|
||
|
||
### 集成 Sentry
|
||
|
||
```bash
|
||
npm install @sentry/nextjs
|
||
```
|
||
|
||
```typescript
|
||
// sentry.client.config.ts
|
||
import * as Sentry from '@sentry/nextjs'
|
||
|
||
Sentry.init({
|
||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||
tracesSampleRate: 0.1,
|
||
})
|
||
```
|
||
|
||
## 故障排查
|
||
|
||
### 常见问题
|
||
|
||
| 问题 | 可能原因 | 解决方案 |
|
||
|------|----------|----------|
|
||
| 页面空白 | 构建失败 | 检查构建日志 |
|
||
| API 请求失败 | CORS / 网络 | 检查代理配置 |
|
||
| 静态资源 404 | 路径配置 | 检查 basePath 配置 |
|
||
| 内存不足 | 并发过高 | 增加实例或内存 |
|
||
|
||
### 调试命令
|
||
|
||
```bash
|
||
# 检查进程
|
||
ps aux | grep node
|
||
|
||
# 检查端口
|
||
netstat -tlnp | grep 3001
|
||
|
||
# 检查容器
|
||
docker ps -a
|
||
docker inspect mobile-upgrade
|
||
|
||
# 检查日志
|
||
docker logs --tail 100 mobile-upgrade
|
||
```
|
||
|
||
## 回滚策略
|
||
|
||
### Docker 回滚
|
||
|
||
```bash
|
||
# 查看历史版本
|
||
docker images mobile-upgrade
|
||
|
||
# 回滚到指定版本
|
||
docker stop mobile-upgrade
|
||
docker rm mobile-upgrade
|
||
docker run -d \
|
||
--name mobile-upgrade \
|
||
-p 3001:3000 \
|
||
mobile-upgrade:previous-tag
|
||
```
|
||
|
||
### PM2 回滚
|
||
|
||
```bash
|
||
# 保存当前配置
|
||
pm2 save
|
||
|
||
# 回滚
|
||
pm2 reload ecosystem.config.js --update-env
|
||
```
|
||
|
||
## 安全建议
|
||
|
||
1. **环境变量**:不要在代码中硬编码敏感信息
|
||
2. **HTTPS**:生产环境强制使用 HTTPS
|
||
3. **CSP**:配置内容安全策略
|
||
4. **更新依赖**:定期更新依赖修复安全漏洞
|
||
5. **访问控制**:配置合适的访问权限
|
||
|
||
## 相关文档
|
||
|
||
- [架构文档](./ARCHITECTURE.md)
|
||
- [API 文档](./API.md)
|
||
- [开发指南](./DEVELOPMENT.md)
|
||
- [测试指南](./TESTING.md)
|