Appearance
PM2 进程管理器完全指南
PM2 是一个用于 Node.js 应用程序的生产级进程管理器,具有内置的负载均衡器。它允许您保持应用程序永远活跃,无需停机重新加载它们,并简化常见的系统管理任务。
什么是 PM2
PM2 是一个守护进程管理器,帮助您管理和保持应用程序在线运行。它主要用于 Node.js 应用程序,但也支持其他编程语言。
核心特性
- 零停机重载:优雅地重启应用而不丢失连接
- 负载均衡:内置负载均衡器,支持集群模式
- 自动重启:应用崩溃时自动重启
- 实时监控:CPU 和内存使用监控
- 日志管理:集中化日志管理
- 启动脚本:系统重启后自动启动应用
安装 PM2
bash
# 全局安装 PM2
npm install -g pm2
# 验证安装
pm2 --version
启动应用
start 命令
启动并守护化应用程序:
bash
# 基本用法
pm2 start app.js
# 指定应用名称
pm2 start app.js --name "my-app"
# 启动多个实例(集群模式)
pm2 start app.js -i 4
# 使用生态文件启动
pm2 start ecosystem.config.js
javascript
// 示例应用
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.send("Hello PM2!");
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});
javascript
module.exports = {
apps: [
{
name: "my-app",
script: "./app.js",
instances: "max",
exec_mode: "cluster",
env: {
NODE_ENV: "development",
},
env_production: {
NODE_ENV: "production",
PORT: 8080,
},
},
],
};
常用启动选项
选项 | 描述 | 示例 |
---|---|---|
-n, --name | 设置进程名称 | pm2 start app.js --name api-server |
-i, --instances | 启动实例数量 | pm2 start app.js -i 4 |
--watch | 监听文件变化自动重启 | pm2 start app.js --watch |
-o, --output | 指定 stdout 日志文件 | pm2 start app.js -o ./logs/out.log |
-e, --error | 指定 stderr 日志文件 | pm2 start app.js -e ./logs/error.log |
停止应用
bash
# 停止特定应用
pm2 stop app-name
pm2 stop 0 # 使用进程 ID
# 停止所有应用
pm2 stop all
# 停止并删除应用
pm2 delete app-name
pm2 delete all
重启应用
bash
# 重启特定应用
pm2 restart app-name
# 重启所有应用
pm2 restart all
# 零停机重载(仅支持 HTTP/HTTPS 应用)
pm2 reload app-name
查看进程状态
bash
# 列出所有进程
pm2 list
pm2 ls # 简写
pm2 ps # 简写
pm2 status # 简写
# 以 JSON 格式输出
pm2 jlist
# 紧凑格式显示
pm2 list --mini-list
输出示例:
┌─────┬─────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │ ↻ │ status │ cpu │ mem │ user │ watching │
├─────┼─────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0 │ my-app │ default │ 1.0.0 │ cluster │ 12345 │ 2h │ 0 │ online │ 0.2% │ 45.2mb │ user │ disabled │
└─────┴─────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
describe 命令
获取进程的详细信息:
bash
# 查看应用详细信息
pm2 describe app-name
pm2 desc app-name # 简写
pm2 info app-name # 简写
pm2 show app-name # 简写
输出包含:
- 进程状态和统计信息
- 内存和 CPU 使用情况
- 环境变量
- 日志文件位置
- 重启次数和时间
进程操作
缩放集群
bash
# 将应用扩展到 4 个实例
pm2 scale my-app 4
# 增加 2 个实例
pm2 scale my-app +2
# 减少 1 个实例
pm2 scale my-app -1
发送信号
bash
# 发送自定义信号
pm2 sendSignal SIGUSR2 my-app
# 发送消息到进程
pm2 send my-app message-data
重置计数器
bash
# 重置重启计数器
pm2 reset my-app
pm2 reset all
高级功能
监听文件变化
bash
# 启动时启用文件监听
pm2 start app.js --watch
# 指定监听路径
pm2 start app.js --watch ./src
# 忽略特定文件或目录
pm2 start app.js --watch --ignore-watch "node_modules logs"
# 设置重启延迟
pm2 start app.js --watch --watch-delay 4000
注意
文件监听功能不建议在生产环境中使用,它主要用于开发环境。
内存限制和自动重启
bash
# 内存超过 100MB 时自动重启
pm2 start app.js --max-memory-restart 100M
# 设置最大重启次数
pm2 start app.js --max-restarts 10
# 设置重启延迟
pm2 start app.js --restart-delay 3000
# 指数回退重启延迟
pm2 start app.js --exp-backoff-restart-delay 100
定时重启
bash
# 使用 cron 表达式定时重启
pm2 start app.js --cron "0 0 * * *" # 每天午夜重启
# 常用 cron 表达式示例
# "0 */6 * * *" # 每 6 小时重启一次
# "0 0 */3 * *" # 每 3 天重启一次
# "0 2 * * 1" # 每周一凌晨 2 点重启
执行外部命令
bash
# 执行非 Node.js 应用
pm2 start "python script.py" --name python-app
# 使用特定解释器
pm2 start script.py --interpreter python3
# 传递参数给解释器
pm2 start app.js --node-args="--harmony"
监控与日志
实时监控
monit 命令
bash
# 启动实时监控界面
pm2 monit
# 新版监控面板
pm2 dashboard
监控界面显示:
- CPU 使用率实时图表
- 内存使用量
- 进程状态
- 实时日志流
系统信息
bash
# 显示系统信息
pm2 sysmonit
# 以 JSON 格式显示系统信息
pm2 slist
日志管理
logs 命令
bash
# 查看所有应用日志
pm2 logs
# 查看特定应用日志
pm2 logs my-app
# 实时跟踪日志
pm2 logs --lines 200
# 以 JSON 格式输出日志
pm2 logs --json
# 显示时间戳
pm2 logs --timestamp
# 查看错误日志
pm2 logs my-app --err
# 查看输出日志
pm2 logs my-app --out
日志配置选项
bash
# 启动时指定日志文件
pm2 start app.js \
--output ./logs/out.log \
--error ./logs/error.log \
--log ./logs/combined.log
# 禁用日志
pm2 start app.js --disable-logs
# 合并日志(将不同实例的日志合并)
pm2 start app.js --merge-logs
# 自定义日志时间格式
pm2 start app.js --log-date-format "YYYY-MM-DD HH:mm:ss Z"
# 设置日志类型
pm2 start app.js --log-type json
日志轮转
bash
# 安装日志轮转模块
pm2 install pm2-logrotate
# 配置日志轮转
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 30
pm2 set pm2-logrotate:compress true
# 手动清空日志
pm2 flush
# 重载日志文件
pm2 reloadLogs
配置管理
生态文件
生成生态文件
bash
# 生成默认生态文件
pm2 ecosystem
# 生成简单模式生态文件
pm2 init simple
完整生态文件示例
javascript
module.exports = {
apps: [
{
// 应用基本信息
name: "api-server",
script: "./dist/server.js",
// 实例配置
instances: "max", // 或具体数字,如 4
exec_mode: "cluster", // 'fork' 或 'cluster'
// 环境变量
env: {
NODE_ENV: "development",
PORT: 3000,
DB_HOST: "localhost",
},
env_production: {
NODE_ENV: "production",
PORT: 8080,
DB_HOST: "prod-db-server",
},
// 日志配置
log_file: "./logs/combined.log",
out_file: "./logs/out.log",
error_file: "./logs/error.log",
log_date_format: "YYYY-MM-DD HH:mm:ss Z",
// 重启策略
max_restarts: 10,
min_uptime: "10s",
max_memory_restart: "512M",
restart_delay: 4000,
// 监听配置
watch: false,
watch_delay: 1000,
ignore_watch: ["node_modules", "logs"],
// 其他选项
source_map_support: true,
instance_var: "INSTANCE_ID",
// 健康检查
wait_ready: true,
listen_timeout: 8000,
kill_timeout: 5000,
},
{
name: "worker",
script: "./workers/index.js",
instances: 2,
exec_mode: "fork",
cron_restart: "0 2 * * *", // 每天凌晨 2 点重启
env: {
NODE_ENV: "development",
},
},
],
// 部署配置
deploy: {
production: {
user: "ubuntu",
host: ["prod-server-1", "prod-server-2"],
ref: "origin/master",
repo: "[email protected]:username/repo.git",
path: "/var/www/production",
"pre-deploy-local": "",
"post-deploy": "npm install && pm2 reload ecosystem.config.js --env production",
"pre-setup": "",
},
staging: {
user: "ubuntu",
host: "staging-server",
ref: "origin/develop",
repo: "[email protected]:username/repo.git",
path: "/var/www/staging",
"post-deploy": "npm install && pm2 reload ecosystem.config.js --env staging",
},
},
};
json
{
"apps": [
{
"name": "api-server",
"script": "./dist/server.js",
"instances": "max",
"exec_mode": "cluster",
"env": {
"NODE_ENV": "development",
"PORT": 3000
},
"env_production": {
"NODE_ENV": "production",
"PORT": 8080
}
}
]
}
使用生态文件
bash
# 启动生态文件中的所有应用
pm2 start ecosystem.config.js
# 使用特定环境启动
pm2 start ecosystem.config.js --env production
# 只启动特定应用
pm2 start ecosystem.config.js --only api-server
# 重启生态文件中的应用
pm2 restart ecosystem.config.js
# 重载生态文件(零停机)
pm2 reload ecosystem.config.js
# 停止生态文件中的应用
pm2 stop ecosystem.config.js
# 删除生态文件中的应用
pm2 delete ecosystem.config.js
环境变量管理
查看环境变量
bash
# 查看进程的所有环境变量
pm2 env 0 # 进程 ID 为 0
# 在生态文件中使用不同环境
pm2 start ecosystem.config.js --env staging
动态更新环境变量
bash
# 使用 --update-env 强制更新环境变量
pm2 restart app-name --update-env
# 或简写
pm2 restart app-name -a
集群模式
集群模式基础
集群模式利用 Node.js 的 cluster
模块,可以在多个 CPU 核心上运行应用实例。
启动集群
bash
# 启动最大数量的实例(等于 CPU 核心数)
pm2 start app.js -i max
# 启动指定数量的实例
pm2 start app.js -i 4
# 使用生态文件启动集群
pm2 start ecosystem.config.js
集群配置示例
javascript
// ecosystem.config.js
module.exports = {
apps: [
{
name: "cluster-app",
script: "./app.js",
instances: "max", // 或具体数字
exec_mode: "cluster",
// 集群专用配置
instance_var: "INSTANCE_ID", // 实例变量名
// 负载均衡配置
listen_timeout: 8000,
kill_timeout: 5000,
},
],
};
应用代码适配
javascript
const express = require("express");
const app = express();
// 获取实例 ID(如果设置了 instance_var)
const instanceId = process.env.INSTANCE_ID || "unknown";
app.get("/", (req, res) => {
res.json({
message: "Hello from cluster!",
instance: instanceId,
pid: process.pid,
});
});
app.get("/health", (req, res) => {
res.status(200).json({
status: "ok",
instance: instanceId,
uptime: process.uptime(),
});
});
const port = process.env.PORT || 3000;
const server = app.listen(port, () => {
console.log(`Instance ${instanceId} running on port ${port}, PID: ${process.pid}`);
// 发送 ready 信号(如果启用了 wait_ready)
if (process.send) {
process.send("ready");
}
});
// 优雅关闭处理
process.on("SIGINT", () => {
console.log(`Instance ${instanceId} received SIGINT, shutting down gracefully`);
server.close(() => {
process.exit(0);
});
});
javascript
// 处理 Socket.IO 的粘性会话
const express = require("express");
const http = require("http");
const socketIo = require("socket.io");
const app = express();
const server = http.createServer(app);
// 集群模式下的 Socket.IO 配置
const io = socketIo(server, {
// 启用粘性会话
sticky: true,
// Redis 适配器(用于集群间通信)
adapter: require("socket.io-redis")({
host: "localhost",
port: 6379,
}),
});
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
});
io.on("connection", (socket) => {
console.log(`User connected to instance ${process.env.INSTANCE_ID}`);
socket.on("message", (data) => {
io.emit("message", data); // 广播到所有实例
});
});
server.listen(3000, () => {
console.log(`Socket.IO server running on instance ${process.env.INSTANCE_ID}`);
});
零停机重载
bash
# 对集群应用进行零停机重载
pm2 reload app-name
# 重载所有集群应用
pm2 reload all
# 强制重载(即使没有变化)
pm2 reload app-name --force
零停机重载流程:
1. PM2 向第一个实例发送 SIGINT 信号
2. 应用处理完当前请求后优雅关闭
3. PM2 启动新实例替换旧实例
4. 重复此过程直到所有实例都被重载
集群监控
bash
# 查看集群状态
pm2 list
# 实时监控集群
pm2 monit
# 查看特定实例详情
pm2 describe app-name
生产环境部署
启动脚本
设置开机自启
bash
# 检测平台并生成启动脚本
pm2 startup
# 手动指定平台
pm2 startup systemd # Linux systemd
pm2 startup launchd # macOS
pm2 startup rcd # FreeBSD
pm2 startup systemv # Linux systemv
# 指定用户
pm2 startup --user username
# 设置服务名称
pm2 startup --service-name my-pm2-service
执行后会输出需要以 root 权限运行的命令,例如:
bash
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u username --hp /home/username
保存和恢复进程
bash
# 保存当前进程列表
pm2 save
# 恢复之前保存的进程
pm2 resurrect
# 清空保存的进程
pm2 cleardump
禁用启动脚本
bash
# 禁用开机自启
pm2 unstartup
部署自动化
使用 PM2 Deploy
bash
# 初始化部署环境
pm2 deploy ecosystem.config.js production setup
# 部署应用
pm2 deploy ecosystem.config.js production
# 部署到特定环境
pm2 deploy ecosystem.config.js staging
部署配置详解
javascript
// ecosystem.config.js - 部署配置
module.exports = {
apps: [
/* 应用配置 */
],
deploy: {
production: {
// 服务器信息
user: "deploy",
host: ["prod-1.example.com", "prod-2.example.com"],
// Git 仓库信息
ref: "origin/master",
repo: "[email protected]:username/repo.git",
// 部署路径
path: "/var/www/production",
// SSH 配置
ssh_options: "StrictHostKeyChecking=no",
// 部署钩子
"pre-deploy-local": 'echo "Local pre-deploy task"',
"post-deploy": ["npm install", "npm run build", "pm2 reload ecosystem.config.js --env production"].join(" && "),
"pre-setup": 'echo "Setting up production environment"',
// 环境变量
env: {
NODE_ENV: "production",
},
},
},
};
性能优化
内存和 CPU 优化
javascript
// ecosystem.config.js - 性能配置
module.exports = {
apps: [
{
name: "optimized-app",
script: "./app.js",
// 实例配置
instances: "max",
exec_mode: "cluster",
// 内存管理
max_memory_restart: "1G",
// Node.js 优化参数
node_args: ["--max_old_space_size=1024", "--optimize-for-size", "--gc-interval=100"].join(" "),
// PM2 优化
min_uptime: "60s",
max_restarts: 5,
restart_delay: 5000,
// 禁用不必要的功能
pmx: false,
automation: false,
// 启用源码映射支持
source_map_support: true,
},
],
};
监控和警报
bash
# 安装监控模块
pm2 install pm2-server-monit
# 设置内存警报阈值
pm2 set pm2-server-monit:monitoring true
pm2 set pm2-server-monit:threshold_memory 80
# 安装日志轮转
pm2 install pm2-logrotate
最佳实践
1. 应用设计最佳实践
优雅关闭处理
javascript
// 正确的优雅关闭处理
const express = require("express");
const app = express();
let server;
// 启动服务器
function startServer() {
server = app.listen(process.env.PORT || 3000, () => {
console.log("Server started");
// 通知 PM2 应用已就绪
if (process.send) {
process.send("ready");
}
});
}
// 优雅关闭函数
function gracefulShutdown(signal) {
console.log(`Received ${signal}, shutting down gracefully`);
server.close(() => {
console.log("HTTP server closed");
// 关闭数据库连接等
// database.close();
process.exit(0);
});
// 强制退出超时
setTimeout(() => {
console.error("Could not close connections in time, forcefully shutting down");
process.exit(1);
}, 10000);
}
// 监听关闭信号
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
// 处理未捕获的异常
process.on("uncaughtException", (err) => {
console.error("Uncaught Exception:", err);
gracefulShutdown("uncaughtException");
});
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection at:", promise, "reason:", reason);
gracefulShutdown("unhandledRejection");
});
startServer();
健康检查端点
javascript
// 添加健康检查端点
app.get("/health", (req, res) => {
const health = {
status: "ok",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
pid: process.pid,
};
// 检查数据库连接等
// if (!database.isConnected()) {
// health.status = 'error';
// return res.status(503).json(health);
// }
res.json(health);
});
app.get("/ready", (req, res) => {
// 检查应用是否完全就绪
res.json({ status: "ready" });
});
2. 配置最佳实践
生产环境配置模板
javascript
// ecosystem.production.config.js
module.exports = {
apps: [
{
name: "prod-app",
script: "./dist/server.js",
// 实例配置
instances: "max",
exec_mode: "cluster",
// 环境变量
env_production: {
NODE_ENV: "production",
PORT: 8080,
},
// 日志配置
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,
// 重启策略
max_restarts: 10,
min_uptime: "30s",
max_memory_restart: "1G",
restart_delay: 5000,
exp_backoff_restart_delay: 100,
// 性能优化
node_args: "--max_old_space_size=1024",
// 健康检查
wait_ready: true,
listen_timeout: 10000,
kill_timeout: 5000,
// 禁用开发功能
watch: false,
autorestart: true,
pmx: false,
},
],
};
3. 监控最佳实践
设置监控和警报
bash
# 安装监控插件
pm2 install pm2-server-monit
# 配置监控
pm2 set pm2-server-monit:monitoring true
pm2 set pm2-server-monit:threshold_memory 85
pm2 set pm2-server-monit:threshold_cpu 90
# 安装日志轮转
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 100M
pm2 set pm2-logrotate:retain 30
pm2 set pm2-logrotate:compress true
自定义监控脚本
bash
#!/bin/bash
# health-check.sh - 自定义健康检查脚本
APP_NAME="my-app"
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
# 检查应用状态
STATUS=$(pm2 jlist | jq -r ".[] | select(.name==\"$APP_NAME\") | .pm2_env.status")
if [ "$STATUS" != "online" ]; then
echo "Application $APP_NAME is $STATUS"
# 发送警报
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"🚨 Application $APP_NAME is $STATUS\"}" \
$WEBHOOK_URL
# 尝试重启
pm2 restart $APP_NAME
fi
4. 部署最佳实践
CI/CD 集成示例
yaml
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: "16"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build application
run: npm run build
- name: Deploy to server
uses: appleboy/[email protected]
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /var/www/production
git pull origin main
npm ci --production
npm run build
pm2 reload ecosystem.config.js --env production
蓝绿部署脚本
bash
#!/bin/bash
# blue-green-deploy.sh
APP_NAME="my-app"
CURRENT_ENV=$(pm2 jlist | jq -r ".[] | select(.name==\"$APP_NAME\") | .pm2_env.NODE_ENV")
if [ "$CURRENT_ENV" == "blue" ]; then
NEW_ENV="green"
else
NEW_ENV="blue"
fi
echo "Deploying to $NEW_ENV environment"
# 启动新环境
pm2 start ecosystem.config.js --env $NEW_ENV
# 等待新环境就绪
sleep 10
# 健康检查
HEALTH_CHECK=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/health)
if [ "$HEALTH_CHECK" == "200" ]; then
echo "Health check passed, switching traffic"
# 更新负载均衡器配置
# nginx -s reload
# 停止旧环境
pm2 stop $APP_NAME-$CURRENT_ENV
echo "Deployment successful"
else
echo "Health check failed, rolling back"
pm2 stop $APP_NAME-$NEW_ENV
exit 1
fi
故障排除
常见问题和解决方案
常见问题
问题 1:应用频繁重启
bash
# 检查重启原因
pm2 describe app-name
# 查看错误日志
pm2 logs app-name --err
# 可能的解决方案
# 1. 增加内存限制
pm2 restart app-name --max-memory-restart 2G
# 2. 增加最小运行时间
# 在 ecosystem.config.js 中设置 min_uptime: '60s'
问题 2:零停机重载失败
bash
# 检查应用是否正确处理 SIGINT 信号
# 确保应用实现了优雅关闭
# 增加超时时间
pm2 reload app-name --kill-timeout 10000
问题 3:集群模式下 Socket.IO 问题
javascript
// 使用 Redis 适配器解决
const io = require("socket.io")(server, {
adapter: require("socket.io-redis")({
host: "localhost",
port: 6379,
}),
});
调试命令
bash
# 检查 PM2 守护进程状态
pm2 ping
# 查看 PM2 版本和环境信息
pm2 report
# 重置 PM2 守护进程
pm2 kill
pm2 resurrect
# 更新 PM2
pm2 update
# 查看详细错误信息
pm2 logs --error --lines 100
总结
PM2 是一个功能强大的 Node.js 进程管理器,通过本指南您应该能够:
✅ 掌握基本操作:启动、停止、重启应用
✅ 配置集群模式:充分利用多核 CPU 资源
✅ 实现零停机部署:保证服务的高可用性
✅ 设置监控和日志:及时发现和解决问题
✅ 优化生产环境:确保应用的稳定运行
记住,在生产环境中使用 PM2 时,始终要考虑应用的优雅关闭、健康检查、监控告警等因素,这样才能构建一个真正可靠的应用部署方案。
最后更新时间:2025 年 7 月 22 日