PM2 部署 Node.js 项目
一、核心要点速览
💡 核心考点
- 快速部署: pm2 start app.js -i max
- 集群模式: 利用多核 CPU,提升性能
- 配置文件: ecosystem.config.js 标准化管理
- Nginx 反向代理: 负载均衡和 SSL 终止
- 开机自启: pm2 startup + pm2 save
二、快速部署
1. 简单启动
bash
# ========== 基本命令 ==========
cd /path/to/your/node-app
# 单进程启动(开发环境)
pm2 start app.js
# 集群模式(推荐生产环境)
pm2 start app.js -i max
# 指定名称和参数
pm2 start app.js --name "my-api" -i 4 --max-memory-restart 500M
# ========== 保存配置 ==========
# 保存进程列表
pm2 save
# 生成开机自启配置
pm2 startup
# 复制输出的命令并执行(需要 sudo)
# 验证
pm2 list
pm2 monit2. 集群模式优势
┌──────────────────────────────────────────────────────────┐
│ 集群模式性能优化 │
└──────────────────────────────────────────────────────────┘
CPU 核心利用:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
单进程模式:
┌─────────────────────────────┐
│ Single Process │
│ (只用 1 个 CPU 核心) │
└─────────────────────────────┘
CPU 利用率:~25% (4 核 CPU)
集群模式 (-i max):
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│ CPU │ │ CPU │ │ CPU │ │ CPU │
│ 0 │ │ 1 │ │ 2 │ │ 3 │
├─────┤ ├─────┤ ├─────┤ ├─────┤
│App 1│ │App 2│ │App 3│ │App 4│
└─────┘ └─────┘ └─────┘ └─────┘
CPU 利用率:~100% ✓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
最佳实践:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 根据 CPU 核心数设置实例
pm2 start app.js -i max
# 或手动指定
pm2 start app.js -i 4
# 动态缩放
pm2 scale api +2 # 增加 2 个实例
pm2 scale api -1 # 减少 1 个实例
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━三、完整部署流程
1. 步骤详解
bash
# ========== 步骤 1: 准备项目 ==========
cd /var/www/my-node-app
# 安装依赖
npm ci --only=production
# 创建日志目录
mkdir -p logs
# ========== 步骤 2: 创建配置文件 ==========
cat > ecosystem.config.js << 'EOF'
module.exports = {
apps: [{
name: 'my-node-app',
script: './dist/server.js',
instances: 4,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
},
log_date_format: 'YYYY-MM-DD HH:mm:ss',
error_file: './logs/error.log',
out_file: './logs/out.log',
max_memory_restart: '1G',
autorestart: true,
max_restarts: 10,
min_uptime: '10s'
}]
}
EOF
# ========== 步骤 3: 启动应用 ==========
pm2 start ecosystem.config.js
# 验证
pm2 status
pm2 logs my-node-app
# ========== 步骤 4: 配置 Nginx ==========
cat > /etc/nginx/sites-available/my-app << 'EOF'
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
EOF
ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
nginx -t
systemctl restart nginx
# ========== 步骤 5: 配置防火墙 ==========
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable2. 项目结构示例
/var/www/my-node-app/
├── package.json
├── ecosystem.config.js # PM2 配置
├── .env # 环境变量
├── logs/ # 日志目录
│ ├── error.log
│ └── out.log
│
├── src/ # 源代码
│ ├── index.js
│ ├── app.js
│ └── routes/
│
├── dist/ # 构建输出
│ └── server.js
│
└── node_modules/ # 依赖四、不同框架的部署
1. Express 项目
javascript
// app.js
const express = require('express')
const app = express()
const PORT = process.env.PORT || 3000
app.get('/', (req, res) => {
res.send(`Hello from worker ${process.pid}`)
})
app.get('/health', (req, res) => {
res.json({
status: 'ok',
pid: process.pid,
uptime: process.uptime()
})
})
// PM2 集群模式下不需要手动处理 cluster
// PM2 会自动管理
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
})javascript
// ecosystem.config.js
module.exports = {
apps: [{
name: 'express-app',
script: './app.js',
instances: 4,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
},
max_memory_restart: '500M',
autorestart: true
}]
}2. Koa 项目
javascript
// src/app.js
const Koa = require('koa')
const app = new Koa()
app.use(async ctx => {
ctx.body = `Hello from Koa (PID: ${process.pid})`
})
const port = process.env.PORT || 3000
app.listen(port, () => {
console.log(`Koa app listening on ${port}`)
})javascript
// ecosystem.config.js
module.exports = {
apps: [{
name: 'koa-app',
script: './src/app.js',
instances: 4,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
}
}]
}3. NestJS 项目
typescript
// main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
const port = process.env.PORT || 3000
await app.listen(port)
console.log(`NestJS application listening on ${port}`)
}
bootstrap()javascript
// ecosystem.config.js
module.exports = {
apps: [{
name: 'nestjs-app',
script: './dist/main.js',
instances: 4,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production'
}
}]
}五、性能优化
1. 内存管理
javascript
// ecosystem.config.js
module.exports = {
apps: [{
name: 'memory-optimized-app',
script: './app.js',
instances: 4,
exec_mode: 'cluster',
// 内存限制
max_memory_restart: '500M', // 超过 500M 自动重启
// Node 堆内存配置
interpreter_args: '--max-old-space-size=4096',
// 垃圾回收优化
node_args: [
'--max-old-space-size=4096',
'--max-new-space-size=2048',
'--gc-interval=100'
],
// 重启策略
autorestart: true,
max_restarts: 10,
min_uptime: '10s',
restart_delay: 4000
}]
}2. 日志优化
javascript
// ecosystem.config.js
module.exports = {
apps: [{
name: 'logging-optimized-app',
script: './app.js',
// 日志格式
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
// 日志文件
error_file: './logs/error-logs/error.log',
out_file: './logs/out-logs/out.log',
// 合并日志(集群模式)
merge_logs: true,
// 日志级别过滤
log_level: 'info', // debug, info, warn, error
}]
}
// 安装日志轮转插件
pm2 install pm2-logrotate
// 配置日志轮转
pm2 set pm2-logrotate:max_size 10M # 最大 10MB
pm2 set pm2-logrotate:retain 7 # 保留 7 个文件
pm2 set pm2-logrotate:compress true # 压缩旧日志六、监控与告警
1. 实时监控
bash
# 实时监控面板
pm2 monit
# 显示内容:
# ┌──────────────────────────────────────┐
# │ PM2 Monitoring │
# ├──────────────────────────────────────┤
# │ my-app │
# │ CPU: 15% Memory: 256MB │
# │ Status: online Uptime: 2d 5h │
# └──────────────────────────────────────┘2. 健康检查
javascript
// monitor.js
const axios = require('axios')
async function checkHealth() {
try {
const response = await axios.get('http://localhost:3000/health')
if (response.data.status !== 'ok') {
console.error('Health check failed!')
process.exit(1)
}
console.log('Health check passed')
} catch (error) {
console.error('Health check error:', error.message)
process.exit(1)
}
}
// 每分钟检查一次
setInterval(checkHealth, 60000)
checkHealth()
// ecosystem.config.js
module.exports = {
apps: [{
name: 'monitor',
script: './monitor.js',
instances: 1,
cron_restart: '0 * * * *' // 每小时重启
}]
}七、常见问题解决
1. 启动失败
bash
# 问题 1: 权限错误
sudo chown -R $USER:$USER /path/to/app
chmod +x app.js
# 问题 2: 端口被占用
lsof -i :3000
kill -9 <PID>
# 或修改配置
env: {
PORT: 3001 # 改用其他端口
}
# 问题 3: Node 版本不兼容
node --version
nvm install 18
nvm use 182. 内存泄漏
bash
# 诊断
pm2 monit # 观察内存增长
# 临时解决
pm2 restart all
# 永久解决:配置自动重启
max_memory_restart: '500M'
# 深度分析
pm2 trigger @pm2/io-agent heap:snapshot3. 集群会话丢失
javascript
// 解决方案:使用外部 session 存储
const session = require('express-session')
const RedisStore = require('connect-redis')(session)
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret'
}))八、面试标准回答
使用 PM2 部署 Node.js 项目非常简单高效。
基本步骤是:
- 安装依赖后,使用
pm2 start app.js -i max集群模式启动- 或使用
ecosystem.config.js配置文件标准化管理- 配合 Nginx 反向代理实现负载均衡和 SSL 终止
- 执行
pm2 save和pm2 startup配置开机自启集群模式的优势是可以充分利用多核 CPU,将单进程应用的 CPU 利用率从 25% 提升到 100%,并且支持零停机重启。
性能优化方面,我会:
- 配置内存限制防止内存泄漏(max_memory_restart)
- 设置日志轮转避免磁盘占满
- 使用 Node 参数优化垃圾回收
- 配置健康检查自动监控
实际项目中,我经常部署 Express、Koa、NestJS 等框架的应用,PM2 都能很好地管理,提供进程守护、日志收集、监控告警等企业级功能。
九、记忆口诀
部署 Node 歌诀:
PM2 部署很简单,
-i max 来启动。
集群模式性能好,
多核 CPU 全用上!
配置文件写好,
ecosystem 不能少。
Nginx 反代配,
开机自启要记牢!
内存限制要设好,
日志轮转定期搞。
监控面板实时看,
生产稳定没烦恼!十、推荐资源
十一、总结一句话
- 快速部署: pm2 start -i max = 一行命令搞定 🚀
- 集群模式: 多核利用 + 零停机 = 性能提升关键 ⚡
- 配置文件: ecosystem.config.js = 标准化必备 ✓
- 监控优化: 内存限制 + 日志轮转 = 生产稳定保障 🎯