Nuxt 生态 精选推荐

Nuxt 生产环境部署与监控:从开发到上线的完整实践

HTMLPAGE 团队
25 分钟阅读

系统讲解 Nuxt 3 应用的生产部署流程,包括多种部署方案对比、环境配置、性能优化、监控告警和故障排查,确保应用稳定高效运行。

#Nuxt 部署 #生产环境 #Docker #监控 #DevOps

Nuxt 生产环境部署与监控:从开发到上线的完整实践

开发完成只是第一步——让应用在生产环境稳定运行才是真正的挑战。本文将带你走完 Nuxt 3 应用从构建到监控的全流程,避开那些"线上才出现"的坑。

部署方案对比

Nuxt 3 支持多种部署模式,选择取决于你的场景:

SSR 服务端渲染

# 构建 SSR 应用
npm run build

# 输出目录
.output/
├── server/      # Node.js 服务
├── public/      # 静态资源
└── nitro.json   # 运行时配置

适用场景

  • 需要 SEO 的内容站
  • 首屏性能要求高
  • 有动态数据渲染需求

部署要求

  • Node.js 运行时
  • 持久化进程管理(PM2/Docker)

SSG 静态生成

# 预渲染所有页面
npm run generate

# 输出纯静态文件
.output/public/
├── index.html
├── about.html
├── _nuxt/        # JS/CSS 资源
└── ...

适用场景

  • 博客、文档站
  • 内容变化不频繁
  • 追求极致性能和成本

部署要求

  • 任何静态托管(Nginx/CDN/Vercel)
  • 无需 Node.js

混合模式

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },           // 首页静态生成
    '/blog/**': { swr: 3600 },          // 博客缓存 1 小时
    '/dashboard/**': { ssr: false },    // 后台纯客户端
    '/api/**': { cors: true }           // API 开启 CORS
  }
})

生产构建配置

环境变量管理

# .env.production
NUXT_PUBLIC_API_BASE=https://api.example.com
NUXT_API_SECRET=your-secret-key
DATABASE_URL=postgresql://...
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // 仅服务端可访问
    apiSecret: '',
    databaseUrl: '',
    
    // 客户端也可访问
    public: {
      apiBase: ''
    }
  }
})

使用:

// 组件/页面中
const config = useRuntimeConfig()
const apiBase = config.public.apiBase  // 客户端安全

// server/api 中
const secret = config.apiSecret  // 仅服务端

构建优化

// nuxt.config.ts
export default defineNuxtConfig({
  // 压缩配置
  vite: {
    build: {
      minify: 'terser',
      terserOptions: {
        compress: {
          drop_console: true,  // 移除 console
          drop_debugger: true
        }
      }
    }
  },
  
  // Nitro 服务端优化
  nitro: {
    compressPublicAssets: true,  // 压缩静态资源
    minify: true
  },
  
  // 实验性功能
  experimental: {
    payloadExtraction: true,  // 提取页面 payload
    viewTransition: true
  }
})

部署到各平台

Vercel(推荐 Serverless)

# 安装 Vercel CLI
npm i -g vercel

# 部署
vercel

或连接 Git 仓库自动部署:

// vercel.json
{
  "buildCommand": "npm run build",
  "outputDirectory": ".output",
  "framework": "nuxt"
}

Docker 部署

# Dockerfile
FROM node:20-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS runner

WORKDIR /app
COPY --from=builder /app/.output .output
COPY --from=builder /app/package.json .

ENV NODE_ENV=production
ENV NUXT_HOST=0.0.0.0
ENV NUXT_PORT=3000

EXPOSE 3000

CMD ["node", ".output/server/index.mjs"]
# docker-compose.yml
version: '3.8'
services:
  nuxt-app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NUXT_PUBLIC_API_BASE=https://api.example.com
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3

PM2 部署(传统服务器)

// ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'nuxt-app',
      script: '.output/server/index.mjs',
      instances: 'max',  // 多进程
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'production',
        NUXT_HOST: '0.0.0.0',
        NUXT_PORT: 3000
      },
      max_memory_restart: '500M',
      error_file: './logs/err.log',
      out_file: './logs/out.log',
      time: true
    }
  ]
}
# 启动
pm2 start ecosystem.config.js

# 查看状态
pm2 status

# 监控
pm2 monit

Nginx 反向代理

# /etc/nginx/sites-available/nuxt-app
upstream nuxt_backend {
    server 127.0.0.1:3000;
    keepalive 64;
}

server {
    listen 80;
    listen 443 ssl http2;
    server_name example.com;
    
    # SSL 配置
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    
    # 静态资源缓存
    location /_nuxt/ {
        proxy_pass http://nuxt_backend;
        proxy_cache_valid 200 365d;
        add_header Cache-Control "public, immutable";
    }
    
    # 其他请求
    location / {
        proxy_pass http://nuxt_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
        proxy_cache_bypass $http_upgrade;
    }
    
    # Gzip
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
    gzip_min_length 1000;
}

健康检查与监控

健康检查端点

// server/api/health.get.ts
export default defineEventHandler(() => {
  return {
    status: 'ok',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: process.memoryUsage()
  }
})

性能监控集成

// plugins/monitoring.client.ts
export default defineNuxtPlugin(() => {
  // Web Vitals 上报
  if (typeof window !== 'undefined') {
    import('web-vitals').then(({ onCLS, onFID, onLCP, onFCP, onTTFB }) => {
      const reportVital = (metric) => {
        // 上报到分析服务
        fetch('/api/vitals', {
          method: 'POST',
          body: JSON.stringify(metric)
        })
      }
      
      onCLS(reportVital)
      onFID(reportVital)
      onLCP(reportVital)
      onFCP(reportVital)
      onTTFB(reportVital)
    })
  }
})

错误追踪(Sentry)

npm install @sentry/vue
// plugins/sentry.client.ts
import * as Sentry from '@sentry/vue'

export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig()
  
  Sentry.init({
    app: nuxtApp.vueApp,
    dsn: config.public.sentryDsn,
    environment: config.public.env,
    tracesSampleRate: 0.1,
    replaysSessionSampleRate: 0.1,
    integrations: [
      Sentry.browserTracingIntegration(),
      Sentry.replayIntegration()
    ]
  })
})
// server/plugins/sentry.ts
import * as Sentry from '@sentry/node'

export default defineNitroPlugin((nitroApp) => {
  Sentry.init({
    dsn: process.env.SENTRY_DSN,
    tracesSampleRate: 0.1
  })
  
  nitroApp.hooks.hook('error', (error) => {
    Sentry.captureException(error)
  })
})

日志管理

// server/utils/logger.ts
import pino from 'pino'

export const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  transport: process.env.NODE_ENV !== 'production' 
    ? { target: 'pino-pretty' }
    : undefined,
  base: {
    env: process.env.NODE_ENV
  }
})

// 使用
logger.info({ userId: 123 }, 'User logged in')
logger.error({ err }, 'Request failed')

性能优化实践

缓存策略

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // CDN 缓存
    '/_nuxt/**': { 
      headers: { 'Cache-Control': 'public, max-age=31536000, immutable' }
    },
    
    // API 响应缓存
    '/api/products': { 
      cache: { maxAge: 60 * 5 }  // 5 分钟
    },
    
    // SWR 模式
    '/blog/**': { 
      swr: 3600,  // 后台重新验证
      cache: { maxAge: 3600 }
    }
  }
})

CDN 配置

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    cdnURL: 'https://cdn.example.com'  // 静态资源走 CDN
  }
})

数据库连接池

// server/utils/db.ts
import { Pool } from 'pg'

let pool: Pool | null = null

export function getPool() {
  if (!pool) {
    pool = new Pool({
      connectionString: process.env.DATABASE_URL,
      max: 20,        // 最大连接数
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000
    })
  }
  return pool
}

// 优雅关闭
process.on('SIGTERM', async () => {
  if (pool) await pool.end()
})

CI/CD 流水线

GitHub Actions

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

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
        env:
          NUXT_PUBLIC_API_BASE: ${{ secrets.API_BASE }}
      
      - name: Run tests
        run: npm test
      
      - name: Build Docker image
        run: docker build -t nuxt-app:${{ github.sha }} .
      
      - name: Push to registry
        run: |
          docker tag nuxt-app:${{ github.sha }} registry.example.com/nuxt-app:latest
          docker push registry.example.com/nuxt-app:latest
      
      - name: Deploy to server
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            docker pull registry.example.com/nuxt-app:latest
            docker-compose up -d

零停机部署

#!/bin/bash
# deploy.sh

# 拉取新镜像
docker pull registry.example.com/nuxt-app:latest

# 启动新容器(不同端口)
docker run -d --name nuxt-app-new -p 3001:3000 nuxt-app:latest

# 等待健康检查通过
until curl -f http://localhost:3001/api/health; do
  sleep 2
done

# 切换 Nginx 上游
sed -i 's/127.0.0.1:3000/127.0.0.1:3001/' /etc/nginx/sites-available/nuxt-app
nginx -s reload

# 停止旧容器
docker stop nuxt-app-old
docker rm nuxt-app-old

# 重命名容器
docker rename nuxt-app-new nuxt-app-old

故障排查

常见问题

1. 内存泄漏

# 监控内存
pm2 monit

# 设置内存限制自动重启
pm2 start app.js --max-memory-restart 500M

2. 冷启动慢

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    // 预热关键路由
    prerender: {
      routes: ['/', '/about', '/products']
    }
  }
})

3. SSR 水合失败

<template>
  <!-- 客户端专属内容 -->
  <ClientOnly>
    <BrowserOnlyComponent />
  </ClientOnly>
</template>

调试工具

# 开启详细日志
DEBUG=nitro:* node .output/server/index.mjs

# 分析构建产物
npx nuxi analyze

# 性能分析
node --inspect .output/server/index.mjs

监控仪表盘

关键指标

## 服务健康
- 可用性 (Uptime): 99.9%
- 响应时间 P99: < 500ms
- 错误率: < 0.1%

## 资源使用
- CPU: < 70%
- 内存: < 80%
- 磁盘 I/O: 正常

## 业务指标
- QPS: 当前请求数
- 活跃用户: 实时在线
- 转化漏斗: 关键路径

告警规则

# prometheus/alerts.yml
groups:
  - name: nuxt-app
    rules:
      - alert: HighErrorRate
        expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.01
        for: 5m
        annotations:
          summary: "错误率超过 1%"
          
      - alert: HighResponseTime
        expr: histogram_quantile(0.99, http_request_duration_seconds) > 0.5
        for: 5m
        annotations:
          summary: "P99 响应时间超过 500ms"
          
      - alert: HighMemoryUsage
        expr: process_resident_memory_bytes > 500000000
        for: 10m
        annotations:
          summary: "内存使用超过 500MB"

总结

Nuxt 生产部署检查清单:

阶段检查项
构建环境变量配置、构建优化、移除 console
部署Docker/PM2 配置、Nginx 反代、SSL
监控健康检查、错误追踪、性能指标
维护日志管理、告警规则、备份策略
CI/CD自动化流水线、零停机部署

生产环境没有"差不多"——每个细节都可能在凌晨 3 点把你叫醒。提前规划、持续监控、快速响应,才是运维的正道。

延伸阅读