
做 Node.js 服务端开发的人基本都会在终端里遇到 PM2。它叫“Node.js 进程管理器”但实际用起来更像一个全天候盯进程的守护者。以前我部署 Node 应用要么用node app.js裸跑要么挂个 systemd 服务日志和重启全靠自己折腾。后来换成 PM2进程一挂自己能拉起来日志自动落盘开机还能自启很多“脏活”总算不用自己干了。这篇文章不打算把官方文档搬一遍就按我实际使用时的思路把 PM2 是什么、能做什么、怎么用好一层层讲清楚。刚入门 Node 的前端、自己做项目部署的独立开发者都可以对照着操作。1. 没有 PM2 的时候Node 应用是怎么“裸奔”的1.1 直接node app.js的三大问题很多人第一次跑 Node 服务都是在本地终端敲一句node app.js看到终端里打印出Server running at http://localhost:3000就觉得很踏实。但在线上服务器上这种运行方式问题很多。第一个问题是“关终端等于杀进程”。你在 SSH 登录的终端里启动应用只要网络一断、窗口一关应用多半跟着没了。即便你加了nohup让程序后台运行哪天进程崩溃了它不会自己回来你只能再登录服务器手动拉起。第二个问题是“崩了没人知道”。Node 应用挂在线上后某个接口万一抛出了未捕获的异常进程可能直接退出。用户访问不了如果没人盯着监控这问题甚至能躺一天。你只能等别人反馈才意识到服务挂了。第三个问题是日志不好处理。裸跑时console.log的内容喷在终端里应用退出后这些内容就丢了。想排查问题没有结构化日志、没有按天切割原始输出根本没法长期留档。1.2 为什么不用 systemd / pm2 不行有人会说Linux 下本来就有 systemd写个 service 文件不就能守护进程了吗这话没错但对于天天改代码的开发者来说systemd 配置比较重。一个 Node 应用可能要频繁重启、动态传参、按 CPU 核数扩展实例每次改完配置都要systemctl daemon-reload开发体验很割裂。PM2 则把“进程守护日志自动重启资源监控部署”集中到一个命令里对 Node 生态尤其友好。它原本就叫 Process Manager 2最初就是给 Node 写的。它能一个命令跑多个应用能开 cluster 模式还能把当前进程列表保存下来、开机自动恢复。这种体验是裸跑和手写 systemd 都难比的。2. PM2 到底帮你干了哪些活2.1 守护进程挂了自动拉起来PM2 的核心功能是守护。你把 Node 应用交给它之后它会持续监听。进程正常退出、异常崩溃、甚至服务器重启它都能按预设策略恢复。它默认的“重启”策略比较直白进程退出后立刻尝试重启。如果你不想让它无限重启也可以用--max-restarts限制重启次数或者设置一个时间间隔让它在某段时间内重启太频繁时“认怂”避免一台机器陷入死循环。这里有个很容易忽略的点PM2 不是把同一个进程“保持存活”而是当原进程退出后重新 fork 一个新进程。所以你在代码里如果需要保存运行时状态别只放在变量里进程一重启内存里的数据就没了。2.2 日志管理stdout 和 stderr 自动分流PM2 默认会接管 Node 进程的console.log和console.error输出。你可以用pm2 logs在前台实时盯日志也可以让日志写到指定文件里。实际用的时候我会把普通日志和错误日志分开存放。比如pm2 start app.js --log /var/log/myapp/app.log \ --out /var/log/myapp/out.log \ --error /var/log/myapp/error.log--out管 stdout--error管 stderr两者互不污染。排查错误时直接盯 error.log 就行效率高很多。后面我会专门讲日志运维的细节。2.3 资源监控不用再反复敲 toppm2 monit打开以后是一个实时面板能显示每个进程的 CPU 使用率、内存占用、请求频率。它不像商业化监控那么强大但日常看资源够用了。如果你想持续采集指标还可以用 PM2 Plus 或对接外部监控系统。不过个人项目一般用不到我通常是pm2 monit扫一眼发现问题再深入看。3. 装好 PM2把第一个进程跑起来3.1 环境准备与安装要装 PM2得先有 Node.js 环境。如果没有建议安装 LTS 版本版本号至少 14 以上。装好后在终端验证node -v npm -v然后全局安装 PM2npm install -g pm2 pm2 --version这里有个小坑如果 npm 的全局安装路径不在系统 PATH 里执行pm2会提示 command not found。此时需要把 npm 全局 bin 目录加到 PATH。怎么查目录执行npm prefix -g然后把输出里的 bin 目录 export 到 shell 配置里。3.2 用 pm2 start 启动一个应用假设项目根目录下有个app.js最简单的启动方式就是cd /path/to/your/project pm2 start app.js启动后建议执行pm2 list看一眼状态。表格里会显示字段含义id进程 ID注意这是 PM2 内部的 ID不是系统 PIDname应用名称默认取文件名比如 appmodefork 或 cluster这里显示 forkstatusonline / stopped / errored 等cpuCPU 占比memory内存占用restarts重启次数uptime已运行时间第一次跑完你会看到 status 是 onlinememory 可能只有几十兆这是正常现象。如果 status 是 errored那就得看日志了。3.3 常用操作命令速查我平时最常用的几个命令pm2 list # 查看所有进程 pm2 logs app # 查看 app 的实时日志 pm2 restart app # 重启 app pm2 reload app # 平滑重载 app pm2 stop app # 停止 app但保留进程条目 pm2 delete app # 删除 app从列表中移除 pm2 kill # 杀掉 PM2 守护进程本身所有托管进程也会停stop和delete的区别要分清。停止只是不再运行删除会把这个进程从 PM2 管理列表里拿掉。如果你只是临时停服用 stop如果确定不要了再 delete。4. 日志、自启与监控日常运维三板斧4.1 日志切割与轮转PM2 自带的日志文件如果一直写很快就会膨胀。最简单的办法是用内置的pm2-logrotate模块或者直接交给系统 logrotate。我一般在项目里加一个模块pm2 install pm2-logrotate安装后可以设置切割策略pm2 set pm2-logrotate:max_size 10M pm2 set pm2-logrotate:retain 7 pm2 set pm2-logrotate:compress truemax_size 10M表示单文件到 10M 就切分retain 7保留 7 个文件compress true把历史日志压缩成 gz。这个模块会定期检查不需要自己写 crontab。4.2 开机自启pm2 startup 和 pm2 save服务器重启后服务要自动恢复这是线上部署的基本要求。PM2 提供了两条命令pm2 startup pm2 savepm2 startup会生成一个系统服务脚本让 Linux 在开机时拉起 PM2 守护进程。pm2 save会把当前进程列表保存成 dump 文件PM2 启动时会从这个 dump 恢复所有应用。需要提醒的是pm2 startup输出里往往会让你执行一条带特定参数的命令你得把那条命令复制到终端执行只敲pm2 startup不够。我第一次用的时候就没看提示结果 reboot 后服务没起来后来仔细看输出才发现漏了。4.3 在线监控与日志定位线上出问题我习惯按这个顺序排查pm2 list看进程状态是不是 onlinepm2 logs app --err只看错误输出pm2 monit看 CPU 和内存是不是被“顶满”如果进程频繁重启pm2 describe app看重启时间线和重启次数。pm2 describe app是个被低估的命令。它能显示进程的启动命令、cwd、日志路径、环境变量还会列出最近重启的时间点。排查“为什么老重启”时特别有用。5. 生产环境必须会的 cluster 模式与平滑重载5.1 Node 单线程的短板与 cluster 模式Node.js 默认是单线程的跑在一台多核服务器上等于只把一颗核压满。要提升并发能力最直接的办法是多开几个进程。PM2 的 cluster 模式就是干这个的。启动时加一个-i参数pm2 start app.js -i maxmax代表按服务器 CPU 核数自动创建进程。也可以手动指定数量比如-i 4就开四个实例。这些实例共享同一个端口PM2 内部会做负载分发。这里有个关键点cluster 模式下每个实例都是独立的内存空间。如果你的应用把用户会话存在内存变量里那用户请求打到实例 A 登录了下次请求打到实例 B 就“失忆”了。需要共享状态的时候得用 Redis 这类外部存储。5.2 reload 和 restart 的区别这是很多新手踩坑的地方。restart是先把进程停掉再启动会产生一个空窗期reload是平滑重载能够逐个重启实例保证总有一个实例活着。代码更新后用 reload 可以有效避免请求中断pm2 reload app但如果代码结构变化很大或者需要清空内存中的缓存我还是会用 restart。cluster 模式下 reload 特别重要线上更新代码时我基本都走pm2 reload。5.3 内存上限与自动重启策略Node 应用最常见的问题之一就是内存泄漏。PM2 可以设置一个内存阈值超过后自动重启pm2 start app.js --max-memory-restart 300M比如设置 300M进程跑到 300M 以上PM2 就会把它重启一次。这只能“治标”让服务别一直涨到崩溃真正解决还是得查代码里的内存泄漏点。我用这个参数的主要目的是给服务加个保险丝。6. 用 ecosystem.config.js 管理复杂项目6.1 为什么要用配置文件命令行参数适合临时调试真正稳定运行的项目还是得用 PM2 的配置文件统一管。ecosystem.config.js就是 PM2 支持的配置文件把应用的名字、脚本路径、启动参数、环境变量都写进去。我一般放在项目根目录内容类似module.exports { apps: [ { name: my-api, script: ./src/server.js, instances: max, exec_mode: cluster, max_memory_restart: 300M, env: { NODE_ENV: development }, env_production: { NODE_ENV: production }, out_file: /var/log/my-api/out.log, error_file: /var/log/my-api/error.log, merge_logs: true, time: true } ] };然后统一启动pm2 start ecosystem.config.js --env production--env production会激活env_production里的变量这样就不用手动改NODE_ENV了。6.2 多应用管理如果你一台服务器上同时跑 API 服务、定时任务、WebSocket 服务可以在同一个配置文件里放多个 app 对象module.exports { apps: [ { name: api, script: ./src/api.js, instances: 2 }, { name: worker, script: ./src/worker.js, instances: 1, cron_restart: 0 3 * * * } ] };cron_restart表示每天凌晨 3 点重启 worker这种定时任务进程非常适用。6.3 关于部署环节的一个经验项目要发布新代码我常用的流程是拉取最新代码npm install --production安装依赖pm2 reload ecosystem.config.js --env production平滑重载pm2 logs确认启动正常。这一步看起来简单但脚本里的--env参数经常会被漏掉。漏掉的后果就是应用以 development 环境启动可能加载一堆调试依赖日志刷得飞起数据库连接也指向测试库。所以我把环境变量固化在配置文件里之后启动命令就成了肌肉记忆。7. 常见问题与排查实录7.1 command not foundpm2 命令找不到装了 PM2 但终端不认大概率是全局 npm 目录不在 PATH 里。可以执行npm prefix -g然后把得到的路径下的 bin 目录加进 PATH比如export PATH$(npm prefix -g)/bin:$PATH想永久生效就写进~/.bashrc或~/.zshrc。7.2 端口被占用EADDRINUSE重启发应用时报 EADDRINUSE十有八九是旧进程没退干净。先用 PM2 看有没有同名进程在跑没有就去系统层面查lsof -i :3000找到占用端口的 PID确认无误后 kill 掉再启动应用。千万记得如果这个 PID 是 PM2 托管的另一个进程别乱 kill否则会连带其他服务出问题。7.3 进程无限重启如何快速定位status一直是 online 但 restarts 数字猛涨说明应用启动后立刻崩溃或者健康检查没过。此时我会执行pm2 logs app --err --lines 200重点看最后崩之前的报错堆栈。还有一种情况是 PM2 把重启间隔设置得太短导致没有足够时间启动。可以用pm2 start app.js --restart-delay 3000让每次重启间隔至少 3 秒给应用一个缓冲时间。7.4 watch 模式带来的坑PM2 支持文件监听自动重启pm2 start app.js --watch本地开发很方便但线上不建议随意开。原因很简单很多项目在构建时会生成 dist 目录监听目录里如果没写 ignore构建一次文件变化就触发一次重启。正确的做法是只监听 src 目录并忽略无关文件pm2 start app.js --watch --ignore-watchnode_modules dist logs我从线上因为 watch 误触发重启过不止一次现在基本都是手动pm2 reload把控制权握在自己手里。8. 我个人最后想说的几点用 PM2 这几年我最大的体会是它给你的不是“永不崩溃”的保证而是一套标准化的进程管理流程。进程、日志、环境变量、启动策略都放在一处换机器、换项目时思路是一致的这比什么都重要。最后分享两个小习惯。一是每次上线改完配置记得pm2 save避免服务器重启后恢复的是旧列表。二是pm2 delete前先pm2 describe看清名字防止误删。不要问我怎么知道的一台服务器上同时管十个应用时眼花删错进程的感觉真的很酸爽。