Next.js项目MindAI教程 - 第九章:系统部署

1. 环境准备

1.1 生产环境配置

首先创建生产环境配置文件:

// src/config/production.ts
export const productionConfig = {
  apiUrl: process.env.NEXT_PUBLIC_API_URL,
  databaseUrl: process.env.DATABASE_URL,
  openaiApiKey: process.env.OPENAI_API_KEY,
  nextAuthSecret: process.env.NEXTAUTH_SECRET,
  nextAuthUrl: process.env.NEXTAUTH_URL,
}

1.2 环境变量设置

创建 .env.production 文件:

# .env.production
NEXT_PUBLIC_API_URL=https://your-domain.com/api
DATABASE_URL="mysql://username:password@host:3306/mindai"
OPENAI_API_KEY=your-openai-api-key
NEXTAUTH_SECRET=your-nextauth-secret
NEXTAUTH_URL=https://your-domain.com

2. 构建优化

2.1 Next.js 构建配置

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
  images: {
    domains: ['localhost', 'your-domain.com'],
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
  experimental: {
    serverActions: true,
    serverComponentsExternalPackages: ['@prisma/client'],
  },
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback = {
        fs: false,
        net: false,
        tls: false,
      }
    }
    return config
  },
}

module.exports = nextConfig

2.2 性能优化

// src/middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // 添加安全相关的响应头
  const response = NextResponse.next()
  
  response.headers.set('X-DNS-Prefetch-Control', 'on')
  response.headers.set(
    'Strict-Transport-Security',
    'max-age=63072000; includeSubDomains; preload'
  )
  response.headers.set('X-Frame-Options', 'SAMEORIGIN')
  response.headers.set('X-Content-Type-Options', 'nosniff')
  response.headers.set('Referrer-Policy', 'origin-when-cross-origin')
  
  // 设置缓存策略
  if (request.nextUrl.pathname.startsWith('/api/')) {
    response.headers.set('Cache-Control', 'no-store')
  } else if (
    request.nextUrl.pathname.match(/\.(js|css|svg|png|jpg|jpeg|gif|ico)$/)
  ) {
    response.headers.set(
      'Cache-Control',
      'public, max-age=31536000, immutable'
    )
  }
  
  return response
}

export const config = {
  matcher: [
    '/((?!_next/static|_next/image|favicon.ico).*)',
  ],
}

3. Docker部署

3.1 Dockerfile

# Dockerfile
FROM node:18-alpine AS base

# 依赖阶段
FROM base AS deps
RUN apk add --no-cache libc6-compat
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 . .
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"]

3.2 Docker Compose

# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
      - NEXTAUTH_URL=${NEXTAUTH_URL}
    depends_on:
      - db
    restart: always

  db:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=mindai
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - app

volumes:
  mysql_data:

3.3 Nginx配置

# nginx.conf
events {
    worker_connections 1024;
}

http {
    upstream nextjs_upstream {
        server app:3000;
    }

    server {
        listen 80;
        server_name your-domain.com;
        return 301 https://$server_name$request_uri;
    }

    server {
        listen 443 ssl http2;
        server_name your-domain.com;

        ssl_certificate /etc/nginx/ssl/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/privkey.pem;
        ssl_session_timeout 1d;
        ssl_session_cache shared:SSL:50m;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
        ssl_prefer_server_ciphers off;

        client_max_body_size 20M;

        location / {
            proxy_pass http://nextjs_upstream;
            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;
        }

        location /_next/static {
            proxy_cache STATIC;
            proxy_pass http://nextjs_upstream;
            add_header Cache-Control "public, max-age=31536000, immutable";
        }

        location /static {
            proxy_cache STATIC;
            proxy_ignore_headers Cache-Control;
            proxy_cache_valid 60m;
            proxy_pass http://nextjs_upstream;
            add_header Cache-Control "public, max-age=31536000, immutable";
        }
    }
}

4. 自动化部署

4.1 GitHub Actions配置

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Login to DockerHub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v2
        with:
          push: true
          tags: your-username/mindai:latest
          cache-from: type=registry,ref=your-username/mindai:buildcache
          cache-to: type=registry,ref=your-username/mindai:buildcache,mode=max

      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USERNAME }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            cd /path/to/project
            docker-compose pull
            docker-compose up -d

5. 监控与日志

5.1 日志配置

// src/lib/logger.ts
import pino from 'pino'

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  transport: {
    target: 'pino-pretty',
    options: {
      colorize: true,
    },
  },
})

export default logger

5.2 性能监控

// src/lib/monitoring.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function monitoringMiddleware(request: NextRequest) {
  const start = Date.now()
  const response = NextResponse.next()

  response.headers.set('Server-Timing', `total;dur=${Date.now() - start}`)

  // 记录请求信息
  logger.info({
    method: request.method,
    url: request.url,
    duration: Date.now() - start,
    status: response.status,
  })

  return response
}

6. 备份策略

6.1 数据库备份脚本

#!/bin/bash
# backup.sh

# 设置变量
BACKUP_DIR="/path/to/backups"
MYSQL_USER="root"
MYSQL_PASSWORD="your-password"
DATABASE="mindai"
DATE=$(date +%Y%m%d_%H%M%S)

# 创建备份目录
mkdir -p "$BACKUP_DIR"

# 执行备份
mysqldump -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$DATABASE" > "$BACKUP_DIR/$DATABASE_$DATE.sql"

# 压缩备份文件
gzip "$BACKUP_DIR/$DATABASE_$DATE.sql"

# 删除30天前的备份
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete

6.2 自动备份配置

# 添加到crontab
0 2 * * * /path/to/backup.sh

7. 部署检查清单

1. 环境变量检查

  • 确认所有必要的环境变量已设置
  • 验证数据库连接字符串
  • 检查API密钥配置
  • 安全检查
  • SSL证书是否有效
  • 敏感信息是否加密
  • 防火墙规则是否正确
  • 性能检查
  • 静态资源是否正确缓存
  • 数据库索引是否优化
  • CDN是否正常工作
  • 监控检查
  • 日志系统是否正常运行
  • 监控告警是否配置
  • 备份系统是否正常
  • 回滚计划
  • 准备回滚脚本
  • 确保有数据备份
  • 测试回滚流程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值