PM2 详细配置指南与示例
概述
什么是 PM2?
PM2(Process Manager 2)是一个功能强大的 Node.js 进程管理器,提供生产级别的进程管理、负载均衡、日志管理、监控和部署功能。
核心特性
| 特性 | 描述 |
|---|---|
| 进程守护 | 应用崩溃后自动重启,保证服务高可用 |
| 负载均衡 | 内置 Cluster 模式,充分利用多核 CPU |
| 日志管理 | 统一的日志收集、轮转和查询 |
| 监控面板 | 实时监控 CPU、内存等资源使用 |
| 零停机部署 | 支持平滑重载,服务不中断 |
| 多环境管理 | 开发、测试、生产环境配置隔离 |
适用场景
- 生产环境部署:保证 Node.js 应用稳定运行
- 微服务架构:管理多个独立服务进程
- 开发环境:文件监听自动重启,提升开发效率
- CI/CD 流程:集成到自动化部署流水线
系统架构
架构图
code
┌─────────────────────────────────────────────────────────────────┐
│ PM2 架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ PM2 CLI │ │ PM2 API │ │ PM2 Plus │ │
│ │ (命令行) │ │ (编程接口) │ │ (监控面板) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └──────────────────┼──────────────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ PM2 Daemon │ │
│ │ (守护进程) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌─────────────────┼─────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ God │ │ Satan │ │ God │ │
│ │ (App #1) │ │ (App #2) │ │ (App #N) │ │
│ │ Cluster │ │ Fork │ │ Cluster │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Worker 1 │ │ Worker │ │ Worker 1 │ │
│ │ Worker 2 │ │ (单进程) │ │ Worker 2 │ │
│ │ ... │ │ │ │ ... │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘执行模式对比
code
Fork 模式 Cluster 模式
┌─────────┐ ┌─────────┐
│ Master │ │ Master │
│ Process │ │ Process │
└────┬────┘ └────┬────┘
│ │
▼ ┌────┼────┐
┌─────────┐ ▼ ▼ ▼
│ Worker │ ┌────┐┌────┐┌────┐
│ (单进程) │ │ W1 ││ W2 ││ W3 │
└─────────┘ └────┘└────┘└────┘
适用场景: 适用场景:
- 定时任务 - HTTP 服务
- 消息队列消费 - WebSocket 服务
- 单线程应用 - CPU 密集型任务工作流程
code
启动流程:
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ CLI │───▶│ Daemon │───▶│ Spawn │───▶│ Running│
│ 命令 │ │ 初始化 │ │ 进程 │ │ 运行 │
└────────┘ └────────┘ └────────┘ └────────┘
重启流程:
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ Reload │───▶│ Graceful│───▶│ New │───▶│ Old │
│ 命令 │ │ Spawn │ │ Worker │ │ Worker │
└────────┘ └────────┘ └────────┘ │ Exit │
└────────┘安装与初始化
安装 PM2
bash
# 全局安装(推荐)
npm install pm2 -g
# 使用 yarn
yarn global add pm2
# 验证安装
pm2 --version初始化配置文件
bash
# 生成示例配置文件
pm2 ecosystem
# 或手动创建
touch ecosystem.config.js核心功能模块
1. 进程守护
PM2 的核心功能是进程守护,确保应用在崩溃后自动重启。
code
进程状态流转:
┌─────────┐ start ┌──────────┐
│ stopped │────────────▶│ launching│
└─────────┘ └────┬─────┘
▲ │
│ ▼
│ ┌──────────┐
│ stop │ online │◀─────┐
└─────────────────│ │ │
└────┬─────┘ │
│ │
crash │ restart│
▼ │
┌──────────┐ │
│ errored │──────┘
└──────────┘守护机制:
- 监听进程退出事件
- 根据重启策略决定是否重启
- 记录重启次数和原因
- 支持最大重启次数限制
2. 负载均衡(Cluster 模式)
利用 Node.js 的 Cluster 模块实现多进程负载均衡:
javascript
// Cluster 模式配置
{
instances: 'max', // 启动与 CPU 核心数相同的进程
exec_mode: 'cluster' // 启用集群模式
}负载均衡原理:
code
Master Process
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐
│Worker1│ │Worker2│ │Worker3│
│ :3000 │ │ :3000 │ │ :3000 │
└───────┘ └───────┘ └───────┘
│ │ │
└───────────────┴───────────────┘
│
▼
共享同一个端口
(通过 IPC 通信)3. 日志管理
PM2 提供完善的日志管理功能:
bash
# 日志文件类型
├── combined.log # 所有日志(标准输出 + 错误)
├── out.log # 标准输出日志
└── error.log # 错误日志日志配置选项:
| 参数 | 类型 | 说明 |
|---|---|---|
log_file | String | 合并日志文件路径 |
out_file | String | 标准输出日志路径 |
error_file | String | 错误日志路径 |
log_date_format | String | 时间戳格式 |
merge_logs | Boolean | 合并集群模式日志 |
log_type | String | 日志格式(json/raw) |
4. 监控功能
bash
# 实时监控面板
pm2 monit监控指标:
| 指标 | 说明 |
|---|---|
| CPU | CPU 使用率 (%) |
| Memory | 内存占用 (MB) |
| Uptime | 运行时长 |
| Restarts | 重启次数 |
| Status | 进程状态 |
PM2 Plus 监控面板:
code
┌────────────────────────────────────────┐
│ PM2 Plus Dashboard │
├────────────────────────────────────────┤
│ App Name CPU Memory Status │
│ ─────────────────────────────────────│
│ api-server 12% 256MB online │
│ web-app 8% 128MB online │
│ worker 5% 64MB online │
├────────────────────────────────────────┤
│ Total: 3 apps | 25% CPU | 448MB RAM │
└────────────────────────────────────────┘5. 环境变量管理
支持多环境配置隔离:
javascript
// 环境配置优先级
env_production > env_staging > env > 默认值
// 环境切换
pm2 start app.js --env production
pm2 start app.js --env staging6. 零停机重载
code
Graceful Reload 流程:
时间线 ─────────────────────────────────────▶
旧进程: ████████████████░░░░░░░░░░░░░░░░░░停止
│ │
新进程: ░░░░░░░░░░░░████████████████████运行
│ │
│ └── 旧进程处理完请求后退出
└── 新进程启动,开始接收请求
过渡期间请求不丢失配置参数详解
配置参数速查表
基础配置
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name | String | - | 应用名称(必填) |
script | String | - | 入口文件路径(必填) |
cwd | String | - | 工作目录 |
args | String/Array | - | 命令行参数 |
interpreter | String | 'node' | 解释器路径 |
interpreter_args | String/Array | - | 解释器参数 |
node_args | String/Array | - | Node.js 参数(别名) |
实例配置
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
instances | Number/'max' | 1 | 实例数量 |
exec_mode | String | 'fork' | 执行模式:fork/cluster |
pid_file | String | - | PID 文件路径 |
cron_restart | String | - | 定时重启(cron 格式) |
日志配置
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
log_file | String | - | 合并日志文件 |
out_file | String | - | 标准输出日志 |
error_file | String | - | 错误日志 |
log_date_format | String | - | 时间戳格式 |
time | Boolean | false | 启用时间戳 |
merge_logs | Boolean | false | 合并集群日志 |
disable_logs | Boolean | false | 禁用日志 |
重启策略
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
autorestart | Boolean | true | 崩溃后自动重启 |
watch | Boolean/String | false | 监听文件变化 |
ignore_watch | Array | [] | 忽略监听的文件 |
max_restarts | Number | 16 | 最大重启次数 |
min_uptime | String | 1000ms | 最小运行时间 |
restart_delay | Number | 0 | 重启延迟(毫秒) |
max_memory_restart | String | - | 内存限制重启 |
高级配置
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
source_map_support | Boolean | true | 支持 Source Map |
instance_var | String | 'NODE_APP_INSTANCE' | 实例 ID 变量名 |
listen_timeout | Number | 3000 | 启动超时(毫秒) |
kill_timeout | Number | 1600 | 停止超时(毫秒) |
wait_ready | Boolean | false | 等待 ready 信号 |
shutdown_with_message | Boolean | false | 关闭时发送消息 |
配置文件示例
1. 最简单的配置文件 (ecosystem.config.js)
javascript
module.exports = {
apps: [{
name: 'my-app', // 应用名称
script: './app.js', // 入口文件
instances: 1, // 实例数量
exec_mode: 'fork', // 执行模式: fork 或 cluster
watch: false, // 是否监听文件变化
env: {
NODE_ENV: 'development', // 开发环境变量
PORT: 3000
},
env_production: {
NODE_ENV: 'production', // 生产环境变量
PORT: 80
}
}]
};2. 完整的配置文件示例
javascript
module.exports = {
apps: [
{
name: 'api-server',
script: './dist/server.js',
// 实例配置
instances: 'max', // 使用最大CPU核心数
exec_mode: 'cluster', // 集群模式
// 日志配置
log_file: './logs/combined.log',
out_file: './logs/out.log',
error_file: './logs/error.log',
log_date_format: 'YYYY-MM-DD HH:mm Z',
time: true,
// 性能监控
max_memory_restart: '1G', // 内存超过1G时重启
node_args: '--max-old-space-size=1024',
// 自动重启
watch: false,
ignore_watch: [
'node_modules',
'logs',
'uploads'
],
// 重启策略
min_uptime: '10s',
max_restarts: 10,
restart_delay: 4000,
autorestart: true,
// 环境变量
env: {
NODE_ENV: 'development',
PORT: 3000,
DEBUG: 'app:*'
},
env_staging: {
NODE_ENV: 'staging',
PORT: 4000
},
env_production: {
NODE_ENV: 'production',
PORT: 80,
INSTANCE_VAR: 'production_value'
},
// 高级配置
source_map_support: true,
instance_var: 'INSTANCE_ID'
}
],
// 部署配置
deploy: {
production: {
user: 'ubuntu',
host: ['server1.example.com', 'server2.example.com'],
ref: 'origin/main',
repo: 'git@github.com:user/repo.git',
path: '/var/www/app',
'pre-deploy': 'git fetch --all',
'post-deploy': 'npm install && pm2 reload ecosystem.config.js --env production',
'pre-setup': 'echo "开始服务器设置"'
},
staging: {
user: 'ubuntu',
host: 'staging.example.com',
ref: 'origin/develop',
repo: 'git@github.com:user/repo.git',
path: '/var/www/staging',
'post-deploy': 'npm install && pm2 start ecosystem.config.js --env staging'
}
}
};常用配置项详解
实例管理配置
javascript
{
instances: 4, // 固定4个实例
// instances: 'max', // 根据CPU核心数自动设置
exec_mode: 'cluster', // 集群模式
pid_file: '/var/run/app.pid' // PID文件路径
}日志配置详解
javascript
{
// 日志文件配置
log_file: './logs/combined.log', // 所有日志
out_file: './logs/app-out.log', // 标准输出日志
error_file: './logs/app-err.log', // 错误日志
log_date_format: 'YYYY-MM-DD HH:mm:ss.SSS',
time: true, // 显示时间戳
// 日志轮转
log_type: 'json', // JSON格式日志
merge_logs: true, // 合并集群日志
disable_logs: false // 禁用日志
}监控和自动重启配置
javascript
{
// 资源监控
max_memory_restart: '500M', // 内存限制
max_restarts: 10, // 最大重启次数
min_uptime: '10s', // 最小运行时间
// 重启策略
restart_delay: 4000, // 重启延迟
autorestart: true, // 自动重启
cron_restart: '0 3 * * *', // 定时重启(每天3点)
// 健康检查
listen_timeout: 3000, // 启动超时时间
kill_timeout: 5000 // 停止超时时间
}文件监听配置
javascript
{
watch: true, // 启用文件监听
watch_delay: 1000, // 监听延迟
ignore_watch: [ // 忽略的文件/目录
'node_modules',
'logs',
'*.log',
'test',
'.git'
],
watch_options: { // 监听选项
followSymlinks: false,
usePolling: true,
interval: 1000
}
}多应用配置示例
javascript
module.exports = {
apps: [
// API 服务器
{
name: 'api-service',
script: './services/api/index.js',
instances: 2,
exec_mode: 'cluster',
env: {
PORT: 3001,
SERVICE_NAME: 'api'
}
},
// WebSocket 服务
{
name: 'websocket-service',
script: './services/websocket/server.js',
instances: 1,
env: {
PORT: 3002,
SERVICE_NAME: 'websocket'
}
},
// 定时任务服务
{
name: 'cron-service',
script: './services/cron/index.js',
instances: 1,
exec_mode: 'fork',
autorestart: false, // 定时任务不需要自动重启
env: {
SERVICE_NAME: 'cron'
}
},
// 静态文件服务
{
name: 'static-service',
script: './services/static/server.js',
instances: 1,
env: {
PORT: 3003,
SERVICE_NAME: 'static'
}
}
]
};环境特定配置
javascript
module.exports = {
apps: [{
name: 'my-app',
script: './app.js',
// 开发环境
env_development: {
NODE_ENV: 'development',
PORT: 3000,
DATABASE_URL: 'mongodb://localhost:27017/dev',
LOG_LEVEL: 'debug',
CORS_ORIGIN: 'http://localhost:3000'
},
// 测试环境
env_test: {
NODE_ENV: 'test',
PORT: 3001,
DATABASE_URL: 'mongodb://localhost:27017/test',
LOG_LEVEL: 'info'
},
// 生产环境
env_production: {
NODE_ENV: 'production',
PORT: 80,
DATABASE_URL: 'mongodb://prod-db:27017/app',
LOG_LEVEL: 'warn',
CORS_ORIGIN: 'https://myapp.com'
}
}]
};部署配置详解
javascript
module.exports = {
apps: [...], // 应用配置
deploy: {
// 生产环境部署
production: {
key: '/path/to/ssh/key.pem', // SSH密钥
user: 'deploy', // 服务器用户
host: [ // 服务器列表
{ host: 'server1.example.com', port: 22 },
{ host: 'server2.example.com', port: 2222 }
],
ssh_options: 'StrictHostKeyChecking=no',
ref: 'origin/main', // Git分支
repo: 'git@github.com:user/repo.git', // 仓库地址
path: '/home/deploy/app/production', // 部署路径
// 部署钩子
'pre-deploy': 'echo "开始部署"',
'pre-deploy-local': 'npm run test', // 本地预部署检查
'post-deploy': `
npm install --production
npm run build
pm2 reload ecosystem.config.js --env production
echo "部署完成"
`,
'pre-setup': 'sudo apt-get update', // 服务器初始化
'post-setup': 'echo "服务器设置完成"'
},
// 蓝绿部署配置
production_blue_green: {
user: 'deploy',
host: 'server.example.com',
ref: 'origin/main',
repo: 'git@github.com:user/repo.git',
path: '/home/deploy/app',
// 蓝绿部署特定配置
'post-deploy': `
if [ -d "current" ]; then
cp current/.env ./source/.env
mv current previous
fi
ln -nfs ./source current
cd current
npm install
pm2 startOrReload ecosystem.config.js --env production
# 健康检查
sleep 10
if curl -f http://localhost:3000/health; then
echo "部署成功"
rm -rf previous
else
echo "部署失败,回滚"
pm2 reload previous/ecosystem.config.js --env production
exit 1
fi
`
}
}
};使用示例
1. 启动应用
bash
# 使用配置文件启动
pm2 start ecosystem.config.js
# 指定环境启动
pm2 start ecosystem.config.js --env production
# 启动单个应用(多应用配置时)
pm2 start ecosystem.config.js --only api-service2. 部署命令
bash
# 初始化部署(首次)
pm2 deploy ecosystem.config.js production setup
# 部署更新
pm2 deploy ecosystem.config.js production
# 回滚到上一个版本
pm2 deploy ecosystem.config.js production revert 13. 进程管理
bash
# 查看进程列表
pm2 list
# 监控资源使用
pm2 monit
# 查看日志
pm2 logs
pm2 logs api-service --lines 100
# 重新启动
pm2 reload all
pm2 reload api-service
# 停止应用
pm2 stop all
pm2 delete api-service命令行使用
命令速查表
进程管理
| 命令 | 说明 |
|---|---|
pm2 start <file|config|json> | 启动应用 |
pm2 stop <id|name|all> | 停止应用 |
pm2 restart <id|name|all> | 重启应用 |
pm2 reload <id|name|all> | 平滑重载(零停机) |
pm2 delete <id|name|all> | 删除应用 |
pm2 kill | 停止所有应用并关闭守护进程 |
信息查看
| 命令 | 说明 |
|---|---|
pm2 list | 查看所有应用列表 |
pm2 status | 同 list |
pm2 show <id|name> | 查看应用详情 |
pm2 describe <id|name> | 同 show |
pm2 monit | 打开监控面板 |
pm2 prettylist | 美化输出应用列表 |
日志管理
| 命令 | 说明 |
|---|---|
pm2 logs | 查看所有日志 |
pm2 logs <id|name> | 查看指定应用日志 |
pm2 logs --lines <n> | 显示最后 n 行 |
pm2 flush | 清空所有日志 |
pm2 reloadLogs | 重载日志文件 |
其他命令
| 命令 | 说明 |
|---|---|
pm2 startup | 生成开机自启动脚本 |
pm2 unstartup | 取消开机自启动 |
pm2 save | 保存当前进程列表 |
pm2 resurrect | 恢复保存的进程列表 |
pm2 reset <id|name|all> | 重置重启计数器 |
pm2 update | 更新 PM2 内存中的进程 |
pm2 ping | 检查 PM2 守护进程状态 |
常用命令示例
bash
# 启动应用
pm2 start app.js # 基本启动
pm2 start app.js --name my-app # 指定应用名称
pm2 start app.js -i max # 启动最大 CPU 核心数个实例
pm2 start app.js -i 4 --cluster # 启动 4 个集群实例
pm2 start app.js --watch # 启用文件监听
pm2 start app.js --env production # 指定环境
# 停止应用
pm2 stop all # 停止所有应用
pm2 stop 0 # 停止 ID 为 0 的应用
pm2 stop my-app # 停止指定名称的应用
# 重启/重载
pm2 restart all # 重启所有应用
pm2 reload all # 平滑重载所有应用
pm2 gracefulReload all # 优雅重载
# 删除应用
pm2 delete all # 删除所有应用
pm2 delete my-app # 删除指定应用
# 查看信息
pm2 list # 查看列表
pm2 show my-app # 查看详情
pm2 monit # 监控面板
# 日志操作
pm2 logs # 查看所有日志
pm2 logs my-app --lines 200 # 查看最后 200 行
pm2 flush # 清空日志
# 开机自启动
pm2 startup # 生成启动脚本
pm2 save # 保存当前状态
pm2 unstartup # 取消自启动
# 部署相关
pm2 deploy ecosystem.config.js production setup
pm2 deploy ecosystem.config.js production
pm2 deploy ecosystem.config.js production revert 1命令选项详解
bash
pm2 start <file> [options]
# 实例选项
--name <name> # 应用名称
-i, --instances <n> # 实例数量(max 表示 CPU 核心数)
--no-vizion # 禁用版本控制
--no-autorestart # 禁用自动重启
# 执行选项
--exec-mode <mode> # 执行模式(fork/cluster)
--interpreter <path> # 解释器路径
--node-args "<args>" # Node.js 参数
-- <args> # 应用参数
# 日志选项
--log <path> # 日志文件路径
--output <path> # 标准输出日志
--error <path> # 错误日志
--time # 添加时间戳
--log-date-format <fmt> # 时间戳格式
--no-daemon # 前台运行
# 监听选项
--watch # 启用文件监听
--ignore-watch <paths> # 忽略监听的文件
--watch-delay <ms> # 监听延迟
# 重启选项
--max-memory-restart <mem> # 内存限制重启
--max-restarts <n> # 最大重启次数
--min-uptime <ms> # 最小运行时间
--restart-delay <ms> # 重启延迟
--cron <pattern> # 定时重启
# 环境选项
--env <name> # 指定环境变量配置编程 API 接口
基本用法
PM2 提供了完整的编程 API,可以在 Node.js 代码中管理进程。
javascript
const pm2 = require('pm2');
// 连接到 PM2 守护进程
pm2.connect((err) => {
if (err) {
console.error(err);
process.exit(2);
}
// 连接成功后的操作...
});核心 API 方法
进程启动
javascript
// 启动应用
pm2.start({
name: 'my-app',
script: './app.js',
instances: 2,
exec_mode: 'cluster'
}, (err, apps) => {
if (err) throw err;
console.log('Started:', apps);
pm2.disconnect(); // 断开连接
});
// 启动配置文件中的应用
pm2.start('ecosystem.config.js', (err, proc) => {
if (err) throw err;
console.log('Started from config');
});进程停止与重启
javascript
// 停止进程
pm2.stop('my-app', (err, proc) => {
if (err) throw err;
console.log('Stopped');
});
// 重启进程
pm2.restart('my-app', (err, proc) => {
if (err) throw err;
console.log('Restarted');
});
// 平滑重载
pm2.reload('my-app', (err, proc) => {
if (err) throw err;
console.log('Reloaded');
});
// 删除进程
pm2.delete('my-app', (err, proc) => {
if (err) throw err;
console.log('Deleted');
});进程查询
javascript
// 获取所有进程列表
pm2.list((err, list) => {
if (err) throw err;
console.log(list);
// list 是进程描述对象数组
});
// 查找特定进程
pm2.describe('my-app', (err, desc) => {
if (err) throw err;
console.log(desc);
});
// 查询单个进程详情
pm2.describe(0, (err, desc) => {
console.log(desc[0].monit); // CPU 和内存信息
});日志操作
nginx
// 刷新日志
pm2.flush((err) => {
if (err) throw err;
console.log('Logs flushed');
});
// 重载日志
pm2.reloadLogs((err) => {
if (err) throw err;
console.log('Logs reloaded');
});Promise 封装
javascript
const pm2 = require('pm2');
const { promisify } = require('util');
// Promisify PM2 方法
const connect = promisify(pm2.connect.bind(pm2));
const start = promisify(pm2.start.bind(pm2));
const stop = promisify(pm2.stop.bind(pm2));
const restart = promisify(pm2.restart.bind(pm2));
const list = promisify(pm2.list.bind(pm2));
const disconnect = promisify(pm2.disconnect.bind(pm2));
// 使用 async/await
async function manageApp() {
try {
await connect();
// 启动应用
await start({
name: 'my-app',
script: './app.js'
});
// 获取列表
const procs = await list();
console.log('Running processes:', procs.length);
// 重启
await restart('my-app');
} catch (err) {
console.error('Error:', err);
} finally {
await disconnect();
}
}
manageApp();事件监听
javascript
// 监听进程事件
pm2.launchBus((err, bus) => {
if (err) throw err;
// 监听日志
bus.on('log:out', (data) => {
console.log('App:', data.process.name);
console.log('Output:', data.data);
});
// 监听错误
bus.on('log:err', (data) => {
console.error('Error from:', data.process.name);
console.error('Message:', data.data);
});
// 监听进程事件
bus.on('process:event', (data) => {
console.log('Event:', data.event); // online, exit, restart...
console.log('Process:', data.process.name);
});
});完整示例
javascript
const pm2 = require('pm2');
class PM2Manager {
constructor() {
this.connected = false;
}
async connect() {
return new Promise((resolve, reject) => {
pm2.connect((err) => {
if (err) reject(err);
else {
this.connected = true;
resolve();
}
});
});
}
async startApp(config) {
return new Promise((resolve, reject) => {
pm2.start(config, (err, proc) => {
if (err) reject(err);
else resolve(proc);
});
});
}
async stopApp(name) {
return new Promise((resolve, reject) => {
pm2.stop(name, (err, proc) => {
if (err) reject(err);
else resolve(proc);
});
});
}
async listApps() {
return new Promise((resolve, reject) => {
pm2.list((err, list) => {
if (err) reject(err);
else resolve(list);
});
});
}
async getAppStatus(name) {
const list = await this.listApps();
return list.find(p => p.name === name);
}
disconnect() {
pm2.disconnect();
this.connected = false;
}
}
// 使用示例
async function main() {
const manager = new PM2Manager();
try {
await manager.connect();
// 启动应用
await manager.startApp({
name: 'api-server',
script: './server.js',
instances: 2,
exec_mode: 'cluster',
env: { PORT: 3000 }
});
// 查看状态
const apps = await manager.listApps();
console.log('Running apps:', apps.map(a => ({
name: a.name,
status: a.pm2_env.status,
cpu: a.monit.cpu,
memory: a.monit.memory
})));
} finally {
manager.disconnect();
}
}
main().catch(console.error);性能优化建议
1. 实例数量选择
javascript
// CPU 密集型任务:等于 CPU 核心数
instances: 'max'
// I/O 密集型任务:可以超过 CPU 核心数
instances: 'max', // 或具体数值如 4、8
// 内存受限场景:根据内存预算计算
// 可用内存 / 单进程内存 = 实例数
instances: Math.floor(totalMemory / perProcessMemory)2. 内存管理
javascript
{
// 设置内存限制
max_memory_restart: '800M',
// Node.js 堆内存限制
node_args: '--max-old-space-size=1024',
// 其他优化参数
env_production: {
UV_THREADPOOL_SIZE: 64, // 增加线程池大小
NODE_OPTIONS: '--max-http-header-size=16384'
}
}3. 日志优化
javascript
{
// 避免日志文件过大
log_file: '/var/log/app/combined.log',
out_file: '/dev/null', // 禁用标准输出日志
error_file: '/var/log/app/error.log',
// 使用 JSON 格式便于分析
log_type: 'json',
log_date_format: 'YYYY-MM-DD HH:mm:ss.SSS'
}4. 启动优化
javascript
{
// 预热启动
wait_ready: true,
listen_timeout: 10000, // 给应用足够的启动时间
// 应用代码中发送 ready 信号
// process.send('ready');
}5. Graceful Shutdown
javascript
// 应用代码中实现优雅关闭
process.on('SIGINT', gracefulShutdown);
process.on('SIGTERM', gracefulShutdown);
async function gracefulShutdown() {
console.log('Received shutdown signal...');
// 1. 停止接收新请求
server.close();
// 2. 处理完现有请求
await closeDatabaseConnections();
await flushQueues();
// 3. 退出进程
process.exit(0);
}6. 性能监控集成
javascript
// 集成 PM2 Plus
{
name: 'my-app',
script: 'app.js',
// PM2 Plus 配置
instance_var: 'NODE_APP_INSTANCE',
// 自定义指标
// 在应用代码中:
// const io = require('@pm2/io');
// io.init({
// metrics: {
// network: true,
// http: true,
// v8: true
// }
// });
}故障排查指南
常见问题诊断流程
code
应用异常
│
├── 检查状态: pm2 list
│
├── 查看日志: pm2 logs
│
├── 检查详情: pm2 show <name>
│
└── 监控面板: pm2 monit问题排查清单
1. 应用无法启动
bash
# 检查错误日志
pm2 logs my-app --err
# 查看完整信息
pm2 show my-app
# 常见原因:
# - 脚本路径错误
# - 依赖未安装
# - 端口被占用
# - 权限不足2. 应用频繁重启
bash
# 查看重启次数
pm2 list
# 检查是否触发作内存限制
pm2 show my-app | grep "restart_time\|memory_limit"
# 解决方案:
# - 检查内存泄漏
# - 调整 max_memory_restart
# - 增加 min_uptime3. 高 CPU 使用
bash
# 使用监控面板
pm2 monit
# 生成 CPU Profile
pm2 profile:cpu 10s cpu-profile.cpuprofile
# 解决方案:
# - 检查代码中的死循环
# - 优化同步操作
# - 增加实例数4. 内存泄漏
bash
# 生成堆快照
pm2 profile:mem memory.heapsnapshot
# 监控内存趋势
pm2 show my-app
# 解决方案:
# - 分析堆快照
# - 设置合理的 max_memory_restart
# - 定期重启 (cron_restart)5. 日志文件过大
bash
# 查看日志大小
ls -lh ~/.pm2/logs/
# 清空日志
pm2 flush
# 解决方案:
# - 使用 pm2-logrotate 模块
# - 禁用不需要的日志
# - 配置日志轮转6. 开机自启动失败
bash
# 重新生成启动脚本
pm2 unstartup
pm2 startup
# 保存当前状态
pm2 save
# 验证服务状态
systemctl status pm2-<user>调试技巧
bash
# 以调试模式启动
pm2 start app.js --node-args="--inspect=9229"
# 查看完整进程信息
pm2 describe my-app
# 实时监控
pm2 monit
# 检查 PM2 守护进程状态
pm2 ping常见问题解答
Q1: Fork 模式和 Cluster 模式有什么区别?
Fork 模式:
- 单进程运行
- 不支持端口共享
- 适合定时任务、队列消费
- 每个实例监听不同端口
Cluster 模式:
- 多进程运行
- 支持端口共享(负载均衡)
- 适合 HTTP/WebSocket 服务
- 充分利用多核 CPU
Q2: 如何选择实例数量?
| 场景 | 建议配置 |
|---|---|
| HTTP API 服务 | instances: 'max' |
| WebSocket 服务 | instances: 1 或少量实例 |
| 定时任务 | instances: 1 (Fork 模式) |
| 消息队列消费 | instances: <队列分区数> |
| 内存受限 | 根据可用内存计算 |
Q3: reload 和 restart 有什么区别?
bash
# restart: 立即重启,会有短暂服务中断
pm2 restart my-app
# reload: 平滑重载,零停机
pm2 reload my-appreload 采用滚动重启,先启动新进程再停止旧进程,适合生产环境。
Q4: 如何实现零停机部署?
- 使用
pm2 reload而非restart - 应用实现 Graceful Shutdown
- 配置合理的
listen_timeout和kill_timeout
javascript
// 应用代码
process.on('SIGTERM', async () => {
server.close();
await cleanup();
process.exit(0);
});Q5: PM2 配置文件中环境变量如何优先?
环境变量优先级(从高到低):
--env <name>指定的环境配置env基础环境变量- 系统环境变量
javascript
// 使用指定环境
pm2 start --env production // 使用 env_production
// 合并环境变量
// env_production 会覆盖 env 中的同名变量Q6: 如何限制应用内存使用?
javascript
{
// 内存超限自动重启
max_memory_restart: '500M',
// Node.js 堆内存限制
node_args: '--max-old-space-size=400'
}Q7: 如何实现定时重启?
javascript
{
// 每天凌晨 3 点重启
cron_restart: '0 3 * * *',
// 每小时重启
cron_restart: '0 * * * *'
}Q8: PM2 日志如何轮转?
安装 pm2-logrotate 模块:
bash
pm2 install pm2-logrotate
# 配置
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7Q9: 如何在 Docker 中使用 PM2?
dockerfile
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
# 安装 PM2
RUN npm install pm2 -g
# 使用 PM2 启动
CMD ["pm2-runtime", "ecosystem.config.js", "--env", "production"]注意:Docker 中使用 pm2-runtime 而非 pm2 start。
Q10: 如何监控多个服务器的 PM2 应用?
使用 PM2 Plus:
bash
# 安装 PM2 Plus agent
pm2 install pm2-server-monit
# 链接到 PM2 Plus
pm2 link <secret_key> <public_key>访问 https://app.pm2.io 查看监控面板。
最佳实践配置
javascript
module.exports = {
apps: [{
name: 'production-app',
script: './dist/app.js',
// 生产环境最佳配置
instances: 'max', // 充分利用CPU
exec_mode: 'cluster', // 集群模式提高性能
// 资源限制防止内存泄漏
max_memory_restart: '800M',
node_args: '--max-old-space-size=1024',
// 稳定的重启策略
max_restarts: 5,
min_uptime: '30s',
restart_delay: 5000,
// 完整的日志管理
log_file: '/var/log/app/combined.log',
out_file: '/var/log/app/out.log',
error_file: '/var/log/app/error.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
merge_logs: true,
// 生产环境变量
env_production: {
NODE_ENV: 'production',
PORT: 8080,
UV_THREADPOOL_SIZE: 64,
NODE_OPTIONS: '--max-http-header-size=16384'
}
}]
};参考资源
Node.js 22+ 部署新特性
原生 --watch 替代 PM2 的 watch 模式
bash
# 传统 PM2 watch 模式
pm2 start app.js --watch
# Node.js 22+ 原生 watch(开发环境)
node --watch app.js
# 生产环境仍推荐 PM2
pm2 start ecosystem.config.jsNode.js 22+ SEA(Single Executable Applications)
Node.js 22+ SEA 功能更加稳定,可将应用打包为单个可执行文件:
bash
# 1. 创建入口文件
echo "console.log('Hello SEA!')" > sea.js
# 2. 生成 blob
node --experimental-sea-config sea-config.json
# 3. 复制 Node.js 二进制
cp $(which node) hello
# 4. 注入 blob
npx postject hello NODE_SEA_BLOB sea-prep.blob \
--sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2json
// sea-config.json
{
"main": "sea.js",
"output": "sea-prep.blob",
"disableExperimentalSEAWarning": true
}权限模型增强生产安全
bash
# 生产环境启用权限模型
node --permission \
--allow-fs-read=/app/data \
--allow-fs-write=/app/logs \
--allow-fs-write=/app/uploads \
--allow-net=0.0.0.0:3000 \
--allow-net=db.internal:5432 \
app.js--env-file 替代 dotenv
bash
# 传统方式
npm install dotenv
# require('dotenv').config
# Node.js 22+ 原生方式
node --env-file=.env.production app.js
# PM2 中使用
pm2 start app.js --node-args="--env-file=.env.production"