📚 现代前端开发 目录
阶段5:全栈与部署
第 33 / 35 课

部署:Vercel与自托管

把应用上线

📖 核心概念

💻 代码实现

Vercel部署与Docker ✅ bash
# ===== Vercel部署 =====
# 安装Vercel CLI
pnpm i -g vercel

# 登录
vercel login

# 预览部署(自动分配URL)
vercel

# 生产部署
vercel --prod

# 环境变量
vercel env add DATABASE_URL
vercel env add NEXTAUTH_SECRET

# 域名配置
vercel domains add myapp.com
# 然后在域名注册商添加CNAME记录指向cname.vercel-dns.com

# ===== Docker部署 =====
# Dockerfile
# --- 构建阶段 ---
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable pnpm && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

# --- 运行阶段 ---
FROM node:20-alpine 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 /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000
ENV PORT=3000 HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]
Nginx与CI/CD ✅ bash
# ===== Nginx配置 =====
# /etc/nginx/sites-available/myapp
# server {
#     listen 80;
#     server_name myapp.com www.myapp.com;
#     return 301 https://$host$request_uri;
# }
#
# server {
#     listen 443 ssl http2;
#     server_name myapp.com www.myapp.com;
#
#     ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem;
#     ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;
#
#     # 安全头
#     add_header X-Frame-Options DENY;
#     add_header X-Content-Type-Options nosniff;
#     add_header X-XSS-Protection "1; mode=block";
#     add_header Strict-Transport-Security "max-age=63072000" always;
#
#     # Next.js应用
#     location / {
#         proxy_pass http://127.0.0.1:3000;
#         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;
#     }
#
#     # 静态资源缓存
#     location /_next/static {
#         proxy_pass http://127.0.0.1:3000;
#         expires 365d;
#         add_header Cache-Control "public, immutable";
#     }
# }

# ===== GitHub Actions CI/CD =====
# .github/workflows/deploy.yml
# name: Deploy
# on:
#   push:
#     branches: [main]
# jobs:
#   deploy:
#     runs-on: ubuntu-latest
#     steps:
#       - uses: actions/checkout@v4
#       - uses: pnpm/action-setup@v2
#       - uses: actions/setup-node@v4
#         with: { node-version: 20, cache: pnpm }
#       - run: pnpm install --frozen-lockfile
#       - run: pnpm test
#       - run: pnpm build
#       - name: Deploy to server
#         uses: appleboy/ssh-action@v1
#         with:
#           host: ${{ secrets.SERVER_HOST }}
#           username: deploy
#           key: ${{ secrets.SSH_KEY }}
#           script: |
#             cd /opt/myapp
#             git pull
#             pnpm install --frozen-lockfile
#             pnpm build
#             pm2 restart myapp

# SSL证书 (Let's Encrypt)
# apt install certbot python3-certbot-nginx
# certbot --nginx -d myapp.com -d www.myapp.com
# certbot renew --dry-run  # 测试自动续期

🔍 部署进阶与运维

生产环境优化清单

部署前的最后检查,确保应用在生产环境中表现最佳:

监控与告警

健康检查与监控 ✅ typescript
// app/api/health/route.ts
export async function GET() {
  const checks = {
    database: await checkDatabase(),
    redis: await checkRedis(),
    storage: await checkStorage(),
  };

  const healthy = Object.values(checks).every(c => c.status === 'ok');

  return Response.json({
    status: healthy ? 'healthy' : 'degraded',
    timestamp: new Date().toISOString(),
    checks,
  }, { status: healthy ? 200 : 503 });
}

// PM2生态配置
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'myapp',
    script: 'node',
    args: 'server.js',
    instances: 'max',
    exec_mode: 'cluster',
    max_memory_restart: '1G',
    env: {
      NODE_ENV: 'production',
      PORT: 3000,
    },
  }],
};

// 健康检查命令
// pm2 start ecosystem.config.js
// pm2 monit
// pm2 logs myapp --lines 100

灾备与回滚

🎯 练习任务

🏆 成就解锁
部署工程师 — 掌握应用部署全流程,让代码安全上线