跳到正文
Joeplover
工程·2026-06-27·约 3 分钟阅读

PM2 生产故障排查:Daemon 崩溃 + EADDRINUSE 端口冲突全记录

记录 PM2 daemon 数据丢失、端口被旧进程占用的完整排查过程。从 lsof 不可靠到 ss 正确诊断、从 systemd User 不匹配到 pm2 save 恢复,一次典型的 Node.js 生产运维实战。

Server rack with diagnostic equipment

PM2 生产故障排查:Daemon 崩溃 + EADDRINUSE 端口冲突全记录

背景

博客(Next.js)部署在腾讯云 Ubuntu 22.04 上,通过 PM2 管理进程生命周期。某天发现博客无法访问,登录服务器检查,PM2 进程列表为空,端口 3000 无响应。

本次故障排查涉及三个连锁问题:

  1. PM2 daemon 崩溃导致进程列表丢失
  2. orphaned next-server 进程导致端口冲突(EADDRINUSE)
  3. 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 进程 orphanedkill 释放端口
systemd 自启失效User=ubuntu 但 PM2_HOME=/root改 User=root

三个关键教训:

  1. 端口排查用 ss 别用 lsof——lsof 可能漏报,ss -tlnp 更可靠
  2. pm2 start 成功后必须 pm2 save——不 save 的话重启后 PM2 不会自动恢复进程
  3. pm2 startup 生成的 systemd service 需要手动检查 User 字段——用谁的身份运行 PM2,User 就该是谁