三种部署形态

形态 适用
托管平台(Vercel / Netlify / Cloudflare Pages) 80% 项目首选
自建 VPS 已有服务器、要省钱、要数据可控
K8s / 容器编排 大团队、多服务、企业级

react-quick/16 讲了 Vercel;react-quick/17 讲了 VPS。这篇补生产级清单——不论部署在哪都该做的事。

1. 环境变量

# .env.local 不进 git
DATABASE_URL=postgres://...
JWT_SECRET=...

# .env.example 进 git(占位)
DATABASE_URL=
JWT_SECRET=

NEXT_PUBLIC_* 前缀的会编译进客户端 bundle——只放可公开的值(如 GA ID、地图 key)。

.env.production 在 CI / 部署平台配,本地不存。

2. 构建 + 启动

npm ci             # 严格按 lock 装,不更新
npm run build      # 出 .next/
NODE_ENV=production npm start   # 或 next start

Docker 化(K8s / 自建都用):

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

FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]

next.config.js

module.exports = { output: 'standalone' };   // 启用 standalone bundle

镜像只带运行所需,体积小一半。

3. 进程管理

VPS 上推荐两种:

systemd(用 PM2 也行):

# /etc/systemd/system/myapp.service
[Unit]
Description=My Next.js
After=network.target

[Service]
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/node server.js
Restart=on-failure
Environment="NODE_ENV=production"
Environment="PORT=3000"
User=www
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
systemctl enable --now myapp
journalctl -u myapp -f         # 看日志

4. 反向代理(Nginx)

upstream nextapp { server 127.0.0.1:3000; keepalive 64; }

server {
  listen 443 ssl http2;
  server_name example.com;

  ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  # gzip / br
  gzip on;
  gzip_types text/css application/javascript application/json image/svg+xml;

  # 静态资产缓存 1 年
  location /_next/static/ {
    proxy_pass http://nextapp;
    add_header Cache-Control "public, max-age=31536000, immutable";
  }

  location / {
    proxy_pass http://nextapp;
    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;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}

5. HTTPS

Let's Encrypt + certbot 全套:

sudo dnf install epel-release    # CentOS / Rocky
sudo dnf install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo systemctl enable --now certbot-renew.timer

证书 90 天自动续。别忘了重定向 HTTP→HTTPS、加 HSTS:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

6. 监控

最低三件套:

1. 运行状态:UptimeRobot / Better Stack / Pingdom——免费层够个人用。

GET https://example.com/api/health → 200 才算活

实现一个健康检查:

// app/api/health/route.ts
export async function GET() {
  // 真正测一下能不能查库 / 关键依赖
  const dbOk = await db.$queryRaw`SELECT 1`.then(() => true).catch(() => false);
  return Response.json({ ok: dbOk }, { status: dbOk ? 200 : 503 });
}

2. 错误追踪Sentry(自托管也行)

npx @sentry/wizard@latest -i nextjs

自动捕获前端 + 后端错误、source map 上传、user feedback。

3. 性能 / Web Vitals

// app/web-vitals.ts
'use client';
import { useReportWebVitals } from 'next/web-vitals';
export function WebVitals() {
  useReportWebVitals((m) => {
    fetch('/api/vitals', { method: 'POST', body: JSON.stringify(m) });
  });
}

也可以接 Vercel Analytics / Plausible / 自建。

7. 日志

结构化日志:JSON 输出,方便采集

import pino from 'pino';
const log = pino({ level: 'info' });

log.info({ userId, action: 'login' }, 'user logged in');

journalctl / Loki / Datadog 都能解析 JSON。

8. 备份

  • 数据库:每日全量 + WAL 增量;备份到异地(不同 region 的对象存储)
  • 用户上传:S3 / OSS / R2 自带跨区域复制
  • 演练:每季度真的恢复一次——光备份没演练等于没备份

9. CI/CD

# .github/workflows/deploy.yml
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: npm }
      - run: npm ci
      - run: npm run lint
      - run: npm run test -- --run
      - run: npm run build
      - name: Deploy
        run: |
          rsync -avz --delete .next/standalone/ user@server:/var/www/myapp/
          ssh user@server 'sudo systemctl restart myapp'

或者直接用 Vercel / Netlify 的内置部署(push 即上线)。

10. 安全清单

  • 所有 secret 在环境变量,不进代码
  • npm audit 在 CI 跑,高危依赖阻断
  • 请求 rate limit(Vercel KV / Upstash 都有现成)
  • CSP 头:default-src 'self'
  • CSRF:Server Actions 自带,自己写的 API 别忘
  • Auth 用 Auth.js / Clerk / Lucia,不要自己手写
  • 数据库连接最小权限(不要用 admin 跑应用)

11. 回滚预案

永远要能 30 秒内回滚

  • Vercel:上一个 deployment 一键 promote
  • 自建:保留上一版本镜像 / 文件,systemd unit 切换 + 重启
  • 数据库 schema 变更必须前向兼容(先改 schema 再改代码,不要反过来)

上线前自检清单

  • HTTPS 证书有效 + 自动续签
  • 健康检查 endpoint 配在监控
  • 错误追踪接通(故意触发一次确认收到)
  • 日志能在主机外看到
  • 数据库定时备份 + 至少手动恢复过一次
  • CI 跑通 lint + test + build
  • 域名 DNS 正确(A / AAAA / CNAME)
  • 文件上传大小、并发请求限流配好
  • 隐私政策 / Cookie 同意(如需)
  • sitemap.xml + robots.txt

→ 最后一篇 继续往哪走