PM2 部署 Node.js 应用:进程守护与集群模式
Node.js 应用直接 node app.js 跑在生产上,一旦进程崩溃或服务器重启,服务就没了。PM2 正是解决这个问题的进程管理器:它让应用常驻后台、崩溃自动重启、开机自启,还能用集群模式榨干多核 CPU。
一、安装与启动
PM2 通过 npm 全局安装:
npm install -g pm2
pm2 start app.js --name my-api
pm2 start 会在后台守护进程并实时监控。常用命令:
pm2 list # 查看所有进程状态
pm2 logs my-api # 查看日志
pm2 monit # 终端实时监控面板
pm2 restart my-api # 重启
pm2 stop my-api # 停止
pm2 delete my-api # 从 PM2 移除
安装后可以用 pm2 -v 确认版本;如果公司网络走 npm 镜像,记得先配好 registry,避免安装超时。
二、集群模式:充分利用多核
默认 fork 模式是单进程,只能用到一个 CPU 核心。对 HTTP/WS 应用,用集群模式按 CPU 数派生多个进程,PM2 内置负载均衡自动分发连接:
pm2 start app.js -i max # 按可用 CPU 数启动
pm2 scale app +3 # 动态扩容 3 个进程
两种模式怎么选,可以看这张表:
| 对比项 | fork 模式 | cluster 模式 |
|---|---|---|
| 进程数 | 1 个 | 按 CPU 核数派生 |
| CPU 利用 | 单核 | 多核 |
| 会话保持 | 不适用 | 内置负载均衡 |
| 适用场景 | 定时任务、CLI 工具 | HTTP/WebSocket 服务 |
生产环境更推荐用配置文件声明:
// ecosystem.config.js
module.exports = {
apps: [{
name: "api",
script: "./src/server.js",
instances: "max", // 集群模式,进程数 = CPU 核数
exec_mode: "cluster",
max_memory_restart: "300M", // 超过 300M 自动重启
env: {
NODE_ENV: "production",
PORT: 3000
},
time: true // 日志带时间戳
}]
};
pm2 start ecosystem.config.js
max_memory_restart 很有用:内存泄漏时自动重启进程,给排查争取时间。敏感配置建议走环境变量文件,配合环境变量与密钥管理一起使用,不要把数据库密码写死在配置里。
三、开机自启:pm2 startup 与 pm2 save
服务器重启后,PM2 需要恢复所有进程。两步解决:
pm2 startup # 生成并安装开机启动脚本(会提示运行一段 sudo 命令)
pm2 save # 保存当前进程列表,开机时自动恢复
pm2 startup 会根据你的系统生成对应的 systemd 单元,让 PM2 及其管理的进程随开机自动拉起。注意:每次新增/删除进程后都要重新 pm2 save,否则下次重启会恢复到旧的进程列表。
四、零停机 reload
普通 pm2 restart 是"先停后起",会有短暂中断。对集群模式下的网络应用,用 pm2 reload 实现逐个进程滚动重启,做到零停机:
pm2 reload all # 优雅滚动重启所有进程
PM2 会先把新进程拉起、等旧连接排空后再切换,适合在CI/CD 自动部署的部署脚本里调用。更系统的零停机策略见零停机部署策略。
五、日志管理
PM2 默认把 stdout/stderr 写入日志,支持轮转:
pm2 logs my-api --lines 200 # 查看最近 200 行
pm2 install pm2-logrotate # 安装日志轮转模块
生产环境建议把日志交给采集系统,配合ELK 日志分析或监控告警统一处理。
六、进程状态监控
pm2 monit 提供实时 CPU/内存监控;pm2 describe <id> 查看进程详细信息。进程重启次数异常升高时,通常是代码崩溃或内存泄漏信号,可接入Prometheus + Grafana做指标采集。把 PM2 的监控作为第一道防线,出现异常先看 pm2 monit 与重启次数,再决定是否深入系统层排查,能省下大量定位时间。
常见问题
- 重启后 PM2 里的进程不见了? 多半是没执行
pm2 save,或者 startup 脚本没装好。重新pm2 save后再执行一次pm2 startup即可。 - 集群模式下内存为什么会翻倍? 这是正常现象——每个实例都有独立的内存空间,N 个实例约等于单实例内存 × N,评估
max_memory_restart时要按总内存换算。 - 端口被占用怎么办? 集群模式各实例默认共享同一个端口,由 PM2 的负载均衡统一分发;如果启动时报 EADDRINUSE,检查是否有残留进程或是否误用了 fork 模式。
七、一个完整的部署脚本
把上面的命令串起来,就是一份简洁的发布脚本:
# deploy.sh
cd /srv/app
git pull origin main
npm ci --production
pm2 reload ecosystem.config.js # 零停机
pm2 save # 保存最新进程列表
第一次部署先 pm2 start ecosystem.config.js,后续发布只用 pm2 reload 加 pm2 save 两行即可。
八、搭配 Nginx 反向代理
PM2 管理的是 Node 进程本身,对外暴露应由Nginx 反向代理完成:Nginx 监听 80/443、终结 TLS、转发到 PM2 监听的本地端口。LNMP 之外的 Node 栈见Node.js Express 指南;如果应用里还跑了后台任务队列,可参考后台任务队列指南统一管理。反向代理放在最外层还有一个好处:无论 Node 进程怎么扩容、重启,对外地址始终不变,客户端与搜索引擎都感受不到内部变化。
16IDC 观察
PM2 的价值在于用极小的学习成本解决了 Node 生产进程的"存活"问题:守护、重启、自启、集群、日志、监控一应俱全。对独立站和中小团队,pm2 start + pm2 startup + pm2 save 三连就能让应用在生产环境稳定运行,再配合 Nginx 反向代理与 CI/CD 自动部署,就形成了一套完整且轻量的 Node 上线方案。
原文来源:https://pm2.keymetrics.io/docs/usage/quick-start/ 、https://pm2.keymetrics.io/docs/usage/process-management/
参考:PM2 集群模式文档 https://pm2.keymetrics.io/docs/usage/cluster-mode/