Nuxt: 部署 Vercel + Docker

最后更新:2026-08-26

MegaShop 开发完成,Charlie 要部署到生产环境。Vercel 部署简单但定制性有限,Docker 可以完全控制但配置复杂。Bob 需要一个既灵活又可靠的部署方案——Vercel 给前端 + API,Docker 给数据库和 Redis。

1. 你将学到


2. 一个架构师的真实故事

(1) 痛点:本地能跑生产挂

Charlie 的 MegaShop 本地运行完美,但部署到服务器后——环境变量缺失、数据库连不上、Redis 未启动、端口冲突。Bob 试了 5 次才部署成功,每次都踩不同的坑。

(2) Docker + Vercel 的解法

Docker 打包整个运行环境,Vercel 零配置部署前端:

BASH
# Vercel: zero-config deploy
vercel --prod

# Docker: build once, run anywhere
docker compose up -d

(3) 收益:一键部署 + 环境一致

Charlie 用 git push 触发 Vercel 部署前端,Docker Compose 管理数据库/Redis/Nuxt 服务,环境完全一致。


3. Vercel 部署

(1) 多平台部署决策树

100%
flowchart TB
    A[Deploy Nuxt 3 App] --> B{Need SSR?}
    B -->|No| C[Static Hosting<br/>Cloudflare/Netlify/S3]
    B -->|Yes| D{Team size?}
    D -->|Small/Medium| E[Vercel<br/>Zero-config deploy]
    D -->|Large/Enterprise| F[Docker + Node.js<br/>Full control]
    E --> G[API + Frontend on Vercel]
    F --> H[Docker Compose<br/>Nuxt + PostgreSQL + Redis]

▶ 示例:Vercel 零配置部署

BASH
# Install Vercel CLI
npm i -g vercel

# Deploy (auto-detects Nuxt 3)
vercel

# Production deploy
vercel --prod

输出:

TEXT 📖 仅展示
# 命令执行成功

▶ 示例:nuxt.config.ts Vercel 配置

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'vercel' // Auto-configured by Vercel
  },
  // Environment variables set in Vercel dashboard
  runtimeConfig: {
    databaseUrl: process.env.DATABASE_URL,
    redisUrl: process.env.REDIS_URL,
    public: {
      apiBase: process.env.API_BASE
    }
  }
})

输出:

TEXT 📖 仅展示
// 执行成功

(2) Vercel 部署优缺点

维度 Vercel Docker
配置复杂度 🟢 零配置 🔴 需写 Dockerfile
部署速度 ⚡ 30s 🟡 3-5min build
自定义 🔴 受限 🟢 完全控制
数据库 🔴 需外部(Vercel Postgres) 🟢 本地部署
成本 ⚠️ 按请求计费 🟢 固定服务器费用
扩展 🟢 自动 🟡 手动/K8s

4. Docker 部署

▶ 示例:多阶段 Dockerfile

DOCKERFILE
# Dockerfile

# Stage 1: Install dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

# Stage 2: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npx prisma generate
RUN npm run build

# Stage 3: Production
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production

# Copy built assets
COPY --from=builder /app/.output ./.output
COPY --from=builder /app/prisma ./prisma

EXPOSE 3000

CMD ["node", ".output/server/index.mjs"]

输出:

TEXT 📖 仅展示
// 执行成功

▶ 示例:Docker Compose 编排

YAML
# docker-compose.yml
version: '3.8'

services:
  # Nuxt application
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://megashop:megashop_pass@db:5432/megashop
      - REDIS_URL=redis://redis:6379
      - JWT_ACCESS_SECRET=${JWT_ACCESS_SECRET}
      - JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET}
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped

  # PostgreSQL database
  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=megashop
      - POSTGRES_PASSWORD=megashop_pass
      - POSTGRES_DB=megashop
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U megashop"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  # Redis cache
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    restart: unless-stopped

  # Nginx reverse proxy
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - web
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

输出:

TEXT 📖 仅展示
CONTAINER ID   IMAGE          STATUS         PORTS
abc123         nginx:latest   Up 2 hours     0.0.0.0:80->80/tcp

▶ 示例:Nginx 反向代理配置

NGINX
# nginx.conf
events {
    worker_connections 1024;
}

http {
    upstream nuxt {
        server web:3000;
    }

    server {
        listen 80;
        server_name megashop.com;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;
        server_name megashop.com;

        ssl_certificate /etc/nginx/ssl/cert.pem;
        ssl_certificate_key /etc/nginx/ssl/key.pem;

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

        # Cache static assets
        location /_nuxt/ {
            proxy_pass http://nuxt;
            expires 1y;
            add_header Cache-Control "public, immutable";
        }
    }
}

输出:

TEXT 📖 仅展示
// 执行成功

5. Node.js Standalone + PM2

▶ 示例:Standalone 预设构建

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'node-server',
    compressPublicAssets: true
  }
})

输出:

TEXT 📖 仅展示
// 执行成功
BASH
# Build standalone server
npm run build

# Output: .output/server/index.mjs
# Run directly
node .output/server/index.mjs

▶ 示例:PM2 进程管理

JAVASCRIPT
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'megashop',
    script: '.output/server/index.mjs',
    instances: 'max',       // Use all CPU cores
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'production',
      PORT: 3000,
      DATABASE_URL: 'postgresql://megashop:pass@localhost:5432/megashop',
      REDIS_URL: 'redis://localhost:6379'
    },
    max_memory_restart: '1G',
    error_file: './logs/error.log',
    out_file: './logs/out.log',
    merge_logs: true,
    autorestart: true
  }]
}

输出:

TEXT 📖 仅展示
// 执行成功
BASH
# Start with PM2
pm2 start ecosystem.config.js

# Monitor
pm2 monit

# Reload (zero-downtime)
pm2 reload megashop

# Logs
pm2 logs megashop

(1) 部署方案对比

方案 复杂度 可控性 扩展性 适用
Vercel 🟢 低 🔴 低 🟢 自动 小/中团队
Docker Compose 🟡 中 🟢 高 🟡 手动 中/大团队
PM2 + Nginx 🟡 中 🟢 高 🟡 手动 VPS 部署
K8s 🔴 高 🟢 最高 🟢 自动 大规模

6. 综合示例:MegaShop 生产部署

BASH
# ============================================
# MegaShop Production Deployment
# Docker Compose + Nginx + PostgreSQL + Redis
# ============================================

# 1. Clone and configure
git clone https://github.com/megashop/megashop.git
cd megashop
cp .env.example .env
# Edit .env with production secrets

# 2. Build and start
docker compose up -d --build

# 3. Run database migrations
docker compose exec web npx prisma migrate deploy

# 4. Seed initial data (first time only)
docker compose exec web npx prisma db seed

# 5. Verify
curl https://megashop.com/api/products?limit=5

# 6. Monitor
docker compose logs -f web

❓ 常见问题

Q Vercel 免费额度够用吗?
A 免费版 100GB 带宽/月 + Serverless 1000 小时。MegaShop 百万页 ISR 命中缓存不计 Function 调用,免费版够中等流量。
Q Docker 多阶段构建有什么好处?
A 最终镜像只包含运行时必要文件,不含源码和 devDependencies。镜像从 1GB+ 缩小到 ~150MB,更安全更快。
Q Nuxt standalone 和 Node preset 有什么区别?
A 没有区别。node-server preset 生成 standalone 输出(.output/server/index.mjs),不需要 node_modules,可直接运行。
Q PM2 cluster 模式支持 SSR 吗?
A 支持。每个 worker 是独立进程,各自渲染 SSR。ISR 缓存用 Redis 共享,所有 worker 共用。
Q Docker 里怎么运行 Prisma 迁移?
A docker compose exec web npx prisma migrate deploy。或在 Dockerfile 里添加迁移步骤。生产用 migrate deploy(不创建新迁移,只应用已有的)。
Q SSL 证书怎么获取?
A 生产环境用 Let's Encrypt + Certbot 自动续期。Docker 部署可用 nginx-proxy + acme-companion 自动获取。Vercel 自带 SSL。

📖 小节


📝 作业

  1. 基础题(难度⭐):将 MegaShop 部署到 Vercel,配置环境变量,验证 SSR 页面正常
  2. 进阶题(难度⭐⭐):编写 Dockerfile + docker-compose.yml,本地启动完整环境(Nuxt + PostgreSQL + Redis)
  3. 挑战题(难度⭐⭐⭐):实现 Docker Compose + Nginx + SSL 的完整生产部署,配置健康检查和自动重启

---|

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏