
PM2 进程持久化实战:深度解析 save 与 startup 的协同机制
在生产环境中,Node.js 应用的稳定性至关重要。本文将深入剖析 PM2 的两个核心命令------
save与startup,揭示它们如何协同工作,实现应用的"永不停机"。
一、为什么需要 save 与 startup?
在开发环境中,我们习惯直接运行 pm2 start app.js,但生产环境面临更严峻的挑战:
- 服务器重启:系统更新、硬件故障、电源中断
- 进程崩溃:内存溢出、未捕获的异常
- 配置丢失:手动启动的命令历史难以追溯
解决方案 :save 记录状态 + startup 接管系统启动 = 全自动恢复
二、核心机制图解
2.1 整体架构
🚀 应用层
💾 持久化数据层
⚙️ PM2 守护进程层
🖥️ 操作系统层
启动
ExecStart
读取
解析配置
启动
启动
启动
systemd
系统初始化
/etc/systemd/system/pm2-*.service
PM2 服务单元文件
PM2 Daemon
进程管理器
pm2 resurrect
恢复命令
~/.pm2/dump.pm2
应用状态快照
(JSON格式)
API Gateway
cluster_mode x4
Worker Service
fork_mode + cron
Web Frontend
fork_mode + watch
2.2 命令执行时序
💾 磁盘存储 🖥️ systemd ⚙️ PM2 CLI 👤 运维人员 💾 磁盘存储 🖥️ systemd ⚙️ PM2 CLI 👤 运维人员 首次配置阶段 应用部署阶段 服务器重启后(自动) pm2 startup systemd 生成服务单元文件 返回启用命令 执行 systemctl enable 写入 /etc/systemd/system/pm2-*.service pm2 start app.js --name api -i 4 进程已启动 PID: 1234 pm2 start worker.js --name job --cron "0 * * * *" 进程已启动 PID: 5678 pm2 save 序列化进程配置 → ~/.pm2/dump.pm2 开机触发 pm2 resurrect 读取 dump.pm2 返回 JSON 配置数组 解析配置,重建进程树 所有进程已恢复
三、dump.pm2 文件深度解析
3.1 文件结构
contains
dump.pm2
array
apps
应用配置数组
应用配置对象
string
name
应用名称(唯一ID)
string
script
启动脚本绝对路径
string
cwd
工作目录
string
exec_mode
执行模式:fork/cluster
number
instances
实例数量
object
env
环境变量键值对
boolean
watch
是否监视文件变化
string
max_memory_restart
内存限制
string
cron_restart
定时重启规则
string
pm2_version
PM2版本号
3.2 实际配置示例
json
[
{
"name": "api-gateway",
"script": "/opt/app/gateway/server.js",
"cwd": "/opt/app/gateway",
"exec_mode": "cluster_mode",
"instances": 4,
"max_memory_restart": "512M",
"env": {
"NODE_ENV": "production",
"DB_HOST": "127.0.0.1"
},
"autorestart": true,
"restart_delay": 3000,
"pm2_version": "5.3.0"
},
{
"name": "data-processor",
"script": "/opt/app/worker/index.js",
"exec_mode": "fork_mode",
"instances": 1,
"cron_restart": "0 2 * * *",
"env": {
"NODE_ENV": "production",
"REDIS_URL": "redis://localhost:6379"
},
"pm2_version": "5.3.0"
}
]
四、save 与 startup 的协作流程
4.1 配置阶段(手动)
生成
包含
启动
记录
pm2 save
pm2 startup systemd
系统服务文件
ExecStart=pm2 resurrect
pm2 start app.js
应用进程
内存中的进程表
写入 dump.pm2
4.2 恢复阶段(自动)
systemd
执行
读取
解析 JSON
有效
无效
cluster_mode
fork_mode
服务器开机
启动 PM2 服务
pm2 resurrect
~/.pm2/dump.pm2
配置验证
按配置启动应用
记录错误日志
启动多个实例
负载均衡
启动单实例
独立进程
应用运行中
五、关键命令详解
5.1 完整配置流程
bash
# ========== 步骤1:配置系统服务(仅需一次)==========
$ pm2 startup systemd
[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
# 执行生成的命令(必须!)
$ sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
[PM2] Done.
# ========== 步骤2:部署应用 ==========
$ pm2 start ecosystem.config.js
# 或
$ pm2 start app.js --name "api" -i 4 --max-memory-restart 500M
# ========== 步骤3:持久化状态(关键!)==========
$ pm2 save
[PM2] Saving current process list...
[PM2] Successfully saved in /home/ubuntu/.pm2/dump.pm2
# ========== 验证配置 ==========
$ systemctl is-enabled pm2-ubuntu
enabled
$ cat ~/.pm2/dump.pm2 | jq '. | length'
3 # 表示保存了3个应用
5.2 生成的系统服务文件
ini
# /etc/systemd/system/pm2-ubuntu.service
[Unit]
Description=PM2 process manager
Documentation=https://pm2.keymetrics.io/
After=network.target
[Service]
Type=forking
User=ubuntu
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Environment=PM2_HOME=/home/ubuntu/.pm2
PIDFile=/home/ubuntu/.pm2/pm2.pid
Restart=on-failure
RestartSec=3
ExecStart=/usr/lib/node_modules/pm2/bin/pm2 resurrect # ← 关键!
ExecReload=/usr/lib/node_modules/pm2/bin/pm2 reload all
ExecStop=/usr/lib/node_modules/pm2/bin/pm2 kill
[Install]
WantedBy=multi-user.target
六、常见陷阱与解决方案
6.1 陷阱矩阵
✅ 解决方案
❌ 导致后果
⚠️ 常见配置陷阱
只执行 startup
未执行 save
应用路径使用相对路径
./app.js
修改应用后
忘记重新 save
多用户环境下
路径冲突
开机后 PM2 启动
但无应用运行
resurrect 失败
找不到文件
新配置未生效
仍用旧配置启动
权限错误
无法读取 dump.pm2
启动应用后
必须执行 save
使用绝对路径
/home/user/app.js
任何配置变更后
重新 save
每个用户独立配置
--hp 参数指定
6.2 诊断脚本
bash
#!/bin/bash
# check-pm2-health.sh - PM2 健康检查脚本
echo "🔍 PM2 持久化配置健康检查"
echo "================================"
# 检查1:系统服务
SERVICE_NAME="pm2-$(whoami)"
if systemctl list-unit-files | grep -q "$SERVICE_NAME"; then
echo "✅ 系统服务已配置: $SERVICE_NAME"
STATUS=$(systemctl is-enabled $SERVICE_NAME 2>/dev/null)
echo " 开机自启状态: $STATUS"
else
echo "❌ 系统服务未配置,请执行: pm2 startup systemd"
fi
# 检查2:dump.pm2 文件
DUMP_FILE="$HOME/.pm2/dump.pm2"
if [ -f "$DUMP_FILE" ]; then
SIZE=$(stat -f%z "$DUMP_FILE" 2>/dev/null || stat -c%s "$DUMP_FILE" 2>/dev/null)
echo "✅ dump.pm2 存在 (大小: $SIZE 字节)"
# 检查是否为空数组
if command -v jq &> /dev/null; then
APP_COUNT=$(jq '. | length' "$DUMP_FILE")
echo " 包含应用数量: $APP_COUNT"
if [ "$APP_COUNT" -eq 0 ]; then
echo "⚠️ 警告: dump.pm2 为空,请执行 pm2 save"
fi
fi
else
echo "❌ dump.pm2 不存在,请执行 pm2 save"
fi
# 检查3:当前运行状态
RUNNING_COUNT=$(pm2 list 2>/dev/null | grep -c "online" || echo "0")
echo "✅ 当前运行中的应用: $RUNNING_COUNT"
echo ""
echo "📋 建议操作:"
if [ "$RUNNING_COUNT" -gt 0 ]; then
echo " 如需持久化当前状态,请执行: pm2 save"
fi
七、高级技巧
7.1 多环境管理
🚀 生产环境
💻 开发环境
pm2 save
dump.dev.pm2
pm2 save
dump.prod.pm2
app-dev
worker-dev
app-prod x4
worker-prod
monitor
~/.pm2/dump.pm2
~/.pm2/dump.pm2
7.2 备份与迁移策略
bash
# 备份 dump.pm2
cp ~/.pm2/dump.pm2 ~/.pm2/dump.pm2.backup.$(date +%Y%m%d)
# 迁移到新服务器
scp ~/.pm2/dump.pm2 user@new-server:~/.pm2/
ssh user@new-server 'pm2 resurrect'
# 或使用配置化部署(推荐)
pm2 ecosystem # 生成 ecosystem.config.js
# 将配置文件纳入版本控制,而非 dump.pm2
八、总结
PM2 持久化
save命令
记录运行时状态
生成 JSON 快照
需手动触发
配置变更后必须执行
startup命令
注册系统服务
建立开机钩子
只需执行一次
指定用户和路径
协同工作
startup 提供触发机制
save 提供数据基础
resurrect 作为桥梁
生产建议
使用绝对路径
定期备份 dump.pm2
结合 ecosystem.config.js
监控 resurrect 日志
核心要点:
pm2 startup= 告诉操作系统"开机时请启动 PM2 并执行 resurrect"pm2 save= 告诉 PM2"我现在运行的应用,下次开机请原样恢复"- 两者缺一不可:startup 是触发器,save 是数据源
- 变更即保存 :任何应用增删改后,必须重新执行
pm2 save
💡 最佳实践 :将
pm2 save纳入部署流程的最后一步,确保生产环境的任何变更都能被持久化。
本文示例基于 PM2 5.x 版本,不同版本可能略有差异。
我问启玄关,艾理顺万绪!