PM2 生产故障排查:Daemon 崩溃 + EADDRINUSE 端口冲突全记录
记录 PM2 daemon 数据丢失、端口被旧进程占用的完整排查过程。从 lsof 不可靠到 ss 正确诊断、从 systemd User 不匹配到 pm2 save 恢复,一次典型的 Node.js 生产运维实战。
PM2 生产故障排查:Daemon 崩溃 + EADDRINUSE 端口冲突全记录
背景
博客(Next.js)部署在腾讯云 Ubuntu 22.04 上,通过 PM2 管理进程生命周期。某天发现博客无法访问,登录服务器检查,PM2 进程列表为空,端口 3000 无响应。
本次故障排查涉及三个连锁问题:
- PM2 daemon 崩溃导致进程列表丢失
- orphaned next-server 进程导致端口冲突(EADDRINUSE)
- systemd 自启配置用户不匹配
第一阶段:PM2 Daemon 崩溃
现象
执行 pm2 status 出现异常——每次运行都重新 spawn daemon:
[PM2] Spawning PM2 daemon with pm2_home=/root/.pm2
[PM2] PM2 Successfully daemonized
正常状态下 PM2 daemon 是常驻的,pm2 status 应该瞬间返回。每次执行都重建 daemon,说明之前的 daemon 已经挂掉了。
排查
检查 ~/.pm2/ 目录:
ls -la ~/.pm2/
发现所有文件的时间戳都是当前时间(22:26),意味着目录被清空过。dump.pm2 不存在——PM2 丢失了所有已管理应用的记录。
检查是否是 OOM 导致:
dmesg | grep -i oom
grep -i oom /var/log/kern.log
无任何 OOM 记录。检查 crontab 也没有可疑的清理脚本。
结论:PM2 daemon 非正常退出且 ~/.pm2/ 被清空,根因不明,但 PM2 7.0.1 的 daemon 稳定性确实存在隐患(部署当天已出现多次 rebuild)。
第二阶段:EADDRINUSE 端口冲突
尝试重启
cd /home/ubuntu/JoeBlog
pm2 start npm --name "joe-blog" -- start
PM2 显示 online,但 curl http://localhost:3000 无响应。
查看日志
pm2 logs
日志显示:
Error: listen EADDRINUSE: address already in use :::3000
说明端口 3000 已经被占用,新进程无法绑定。
坑:lsof 不可靠
lsof -i :3000
返回空!但 ss -tlnp 却正确显示:
ss -tlnp | grep 3000
输出显示旧的 next-server 进程(PID 58532)仍在监听 3000。这是因为早期部署时直接运行 npm start 启动的进程,PM2 重启后它变成了 orphaned 进程。
教训:排查端口占用优先用 ss 而不是 lsof,lsof 在某些环境下可能漏报。
修复
kill 58532 # 杀死旧进程释放端口
# 或者用 fuser
fuser -k 3000/tcp
清理后重新启动 PM2:
pm2 delete joe-blog && pm2 start npm --name "joe-blog" -- start
成功!curl localhost:3000 返回 HTTP 200。
注意:PM2 默认重启上限是 15 次(↺15),到达上限后标记为 errored。所以清理端口后,需要先 pm2 delete 删除错误记录再重新 start。
第三阶段:systemd 自启配置修复
发现问题
pm2 startup
cat /etc/systemd/system/pm2-ubuntu.service
发现配置中有严重不匹配:
User=ubuntu
但 PM2 的实际运行用户是 root(PM2_HOME=/root/.pm2)。重启后 systemd 会以 ubuntu 用户启动 PM2,但 /root/.pm2 目录 ubuntu 用户无权访问,导致 pm2 resurrect 失败。
修复
sed -i 's/User=ubuntu/User=root/' /etc/systemd/system/pm2-ubuntu.service
systemctl daemon-reload
持久化进程列表
pm2 save
成功写入 /root/.pm2/dump.pm2。
验证
systemctl is-enabled pm2-ubuntu # ✅ enabled
systemctl status pm2-ubuntu # ✅ active
curl http://localhost:3000 # ✅ HTTP 200
总结与反思
| 问题 | 根因 | 解决方案 |
|---|---|---|
| PM2 进程列表丢失 | daemon 崩溃 + ~/.pm2/ 被清空 | pm2 start + pm2 save 重建 |
| EADDRINUSE | 旧 next-server 进程 orphaned | kill 释放端口 |
| systemd 自启失效 | User=ubuntu 但 PM2_HOME=/root | 改 User=root |
三个关键教训:
- 端口排查用
ss别用lsof——lsof可能漏报,ss -tlnp更可靠 - pm2 start 成功后必须 pm2 save——不 save 的话重启后 PM2 不会自动恢复进程
- pm2 startup 生成的 systemd service 需要手动检查 User 字段——用谁的身份运行 PM2,User 就该是谁