生产环境部署清单:Vercel、Docker、PM2
五个必选项
部署到生产环境时,不管什么平台,都要回答这五个问题:
- 迁移什么时候跑? (不是在每次函数启动时!)
- DATABASE_URL 和 DIRECT_URL 怎么配?
- 生产 PrismaClient 要不要关掉 query 日志?
- 连接池大小和数据库的连接上限对得上吗?
- 应用崩溃/重启时 PrismaClient 怎么优雅关闭?
方案一:Vercel(Serverless)
架构
Vercel Build Step(跑 migrate deploy + prisma generate)
→ Vercel Functions(每个函数实例是一个 Serverless Runtime)
→ 数据库(PostgreSQL/MySQL/SQLite)
配置步骤
1. 环境变量(在 Vercel Project Settings → Environment Variables):
DATABASE_URL=prisma://accelerate.prisma-data.net/?api_key=xxx # 或直连
DIRECT_URL=postgresql://user:pass@real-host:5432/proddb # migrate 用
2. Build Command / Pre-build Command:
# Vercel Pre-build Command(Build Phase 前执行一次)
npx prisma migrate deploy
# Build Command
npx prisma generate && next build # 或你的构建命令
Pre-build Command 比每个函数里自动跑更高效——它只跑一次,而不是每个冷启动的函数实例都跑。
3. 生产 PrismaClient:
export const prisma = new PrismaClient({
log: ['error'], // 只记 error,关掉 query 日志
})
坑点
- 连接数爆炸:并发的 Vercel Functions 可能同时建立大量数据库连接。如果你的数据库连接数有限制,必须用 Accelerate 或 PgBouncer
- 冷启动 + 连接握手超时:Serverless 环境冷启动加数据库连接可能超过 10 秒。考虑用 Neon(Serverless Postgres)或 Accelerate
- migrate deploy 会阻塞构建:迁移时间长的话 Build 会超时。大迁移考虑单独跑在 CI 里
方案二:Docker + 云服务器(长驻进程)
Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY prisma ./prisma
RUN npx prisma generate
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/package.json ./
# 容器启动时:先跑迁移,再启动应用
CMD ["sh", "-c", "npx prisma migrate deploy && npm start"]
docker-compose 简化版
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: mydb
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
app:
build: .
environment:
DATABASE_URL: postgresql://postgres:secret@db:5432/mydb?schema=public
DIRECT_URL: postgresql://postgres:secret@db:5432/mydb?schema=public
depends_on:
- db
volumes:
pgdata:
Docker Compose 启动顺序不可靠(app 启动时 db 可能还没 ready),migrate deploy 有数据库连不上时的重试逻辑,刚好能 cover。
方案三:PM2 / systemd(裸机或 VM)
# 部署脚本
cd /var/www/myapp
git pull origin main
npm ci
npx prisma migrate deploy
npm run build
pm2 restart myapp
PM2 或 systemd 管理的长驻进程不需要额外关心 PrismaClient 的生命周期——它跟进程同生共死。进程崩溃重启时 PrismaClient 也会跟着重新创建。
PM2 优雅关闭
// ecosystem.config.js
module.exports = {
apps: [{
name: 'myapp',
script: 'dist/server.js',
listen_timeout: 10000,
kill_timeout: 10000, // 给足够时间让 Prisma $disconnect() 完成
}],
}
在应用代码里监听 SIGINT/SIGTERM:
process.on('SIGINT', async () => {
console.log('正在关闭...')
await prisma.$disconnect()
process.exit(0)
})
生产 PrismaClient 配置
// src/lib/prisma.ts(生产版)
import { PrismaClient } from '@prisma/client'
export const prisma = new PrismaClient({
log: ['error'], // 关掉 query 日志,只记 error
// production 不做热重载,不需要 globalThis 单例
})
生产环境与开发环境的 PrismaClient 区别:
| 配置 | 开发 | 生产 |
|---|---|---|
log | ['query', 'error', 'warn'] | ['error'] |
| 单例方式 | globalThis 跨 HMR | 直接 new 一次 |
| 端口/主机 | localhost | 按部署环境 |
环境变量管理
生产环境的 .env 绝对不要提交到 Git。推荐做法:
- Vercel / Netlify / Railway:在平台 UI 上配置 Environment Variables
- Docker:用
--env-file参数或 Docker secrets - 裸机:用 systemd 的 EnvironmentFile 或 .env 文件(确保权限 600)
- CI/CD 密码:GitHub Secrets / GitLab CI Variables
部署前自检清单
- [ ]
prisma migrate status→ 生产库迁移历史与本地一致 - [ ]
npx prisma validate→ Schema 无语法错误 - [ ]
npx prisma generate→ Client 版本与 Schema 同步 - [ ] 生产 DATABASE_URL 和 DIRECT_URL 都能连通
- [ ] 生产 PrismaClient 关掉了 query 日志
- [ ] 进程有优雅关闭处理(长驻进程)