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 reloadpm2 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/