线上用户反馈页面白屏是一个**P0级(最高优先级)**的紧急问题。现在我们的 Next.js 应用是使用 PM2 直接部署在服务器(可能是虚拟机或物理机)上的。排查逻辑——区分客户端与服务端、以日志为中心。
🌙 PM2 环境下的应急响应与定位流程
🌙 第 1 阶段:紧急评估与快速止损 (First 5 Minutes)
1. 复现与浏览器端分析 (与之前一致)
- 隐身模式打开出问题的页面。
- 打开开发者工具 (F12):
- Console (控制台): 是否有 JavaScript 错误?这是区分客户端/服务端问题的第一个关键线索。
- Network (网络): 页面本身(HTML 文档)的请求状态码是 200 还是 5xx?
2. 立刻检查 PM2 日志! 这是整个排查过程的核心。你需要 SSH 登录到应用服务器。PM2 会管理应用的日志输出 (stdout) 和错误输出 (stderr)。
# 1. 查看由 PM2 管理的所有应用的状态
pm2 status
# 2. 查看应用的实时日志 (将 <app_name> 替换为你的应用名或ID)
# 这是最常用的命令,会同时显示标准输出和错误输出
pm2 logs <app_name>
# 3. 只看错误日志,这通常更有价值!
pm2 logs <app_name> --err
# 4. 如果日志太多,可以只看最后几百行
pm2 logs <app_name> --lines 200
2
3
4
5
6
7
8
9
10
11
12
在日志中你要寻找什么?
- 任何
Error:
开头的堆栈跟踪 (stack trace)。 - 数据库连接错误、第三方 API 超时、文件权限问题等。
- 特别注意,如果应用频繁重启,
pm2 status
会显示restarts
次数很高。这通常是致命错误导致进程崩溃的迹象。
3. 快速决策:回滚 如果问题是最近的上线导致的,最快的恢复方法是回滚代码。在 PM2 的场景下,通常是一个原子化的操作:
- 常见的部署策略(如 Capistrano 风格)会将每个版本部署到带时间戳的目录中,并用一个名为
current
的软链接指向当前版本。 - 回滚操作:将
current
软链接指向上一个稳定的版本目录,然后执行pm2 reload <app_name>
。这可以实现零停机回滚。
🌙 第 2 阶段:深度定位问题根源
🌙 场景一:定位客户端错误 (浏览器控制台有 JS 错)
挑战:和 Docker 场景一样,你看到的将是压缩后的代码堆栈。
解决方案:本地复现与 Source Map 解码
获取构建产物: 你的构建产物(包括 Source Maps)就在服务器的文件系统上,位于你的项目部署目录下的
.next
文件夹里。你可以使用scp
或rsync
将整个.next
目录从服务器下载到本地。# 语法: scp -r user@your_server_ip:/path/to/your/app/.next ./ scp -r ubuntu@123.45.67.89:/var/www/my-app/current/.next ./local-next-build
1
2手动解码: 有了
.next
目录,接下来的步骤就和 Docker 场景完全一样了:找到报错的文件名、行列号,然后使用source-map
库或在线工具,结合对应的.map
文件来定位到原始的 TypeScript 代码。
🌙 场景二:定位服务端错误 (页面请求 500 或 PM2 不断重启)
这是 PM2 环境下最需要关注的情况。
解决方案:深挖日志、检查环境和系统资源
深挖 PM2 日志:
pm2 logs --err
是你的好朋友。服务端的堆栈跟踪是可读的,会直接指向你代码中的文件和行号。- 实施结构化日志:这个建议依然适用。将错误包装在 JSON 对象中输出,可以让日志分析事半功倍。
检查环境变量: PM2 应用的环境变量来源主要有两个:
ecosystem.config.js
文件:这是 PM2 的最佳实践。检查项目根目录下的这个文件,特别是env
和env_production
部分。// ecosystem.config.js module.exports = { apps: [{ name: 'my-next-app', script: 'node_modules/.bin/next', args: 'start', env_production: { NODE_ENV: 'production', PORT: 3000, // 检查这里的 DATABASE_URL, API_KEY 等是否正确配置 DATABASE_URL: '...', } }] };
1
2
3
4
5
6
7
8
9
10
11
12
13
14- 系统级环境变量:检查运行 PM2 的用户的环境变量设置,如
~/.bashrc
或~/.profile
。使用echo $VARIABLE_NAME
确认。
检查系统资源 (PM2 独特优势与劣势) 与 Docker 不同,PM2 进程与服务器上的其他进程共享资源。资源耗尽是常见问题。
- 内存溢出 (Out of Memory): Node.js 应用如果内存泄漏,可能会被系统的 OOM Killer 杀死,导致 PM2 不断重启。
- 使用
pm2 monit
:这个命令提供了一个漂亮的终端仪表盘,可以实时监控每个应用的 CPU 和内存使用情况。 - 使用系统命令:
htop
或top
可以看到进程的资源占用。dmesg -T | grep -i "killed process"
可以查看是否有进程被系统杀死。
- 使用
- CPU 占用过高: 检查是否有死循环或高计算量的操作。
- 内存溢出 (Out of Memory): Node.js 应用如果内存泄漏,可能会被系统的 OOM Killer 杀死,导致 PM2 不断重启。
检查文件权限:
- 确认运行 PM2 的用户(在
pm2 status
中可以看到user
)对项目目录有正确的读/写权限,特别是对.next
目录和任何需要写入的缓存目录。 - 使用
ls -l /path/to/your/app
来检查。
- 确认运行 PM2 的用户(在
🌙 PM2 环境下的白屏问题排查清单
[ ]
复现: 在浏览器隐身模式下复现问题,检查控制台和网络面板,初步判断是客户端还是服务端问题。[ ]
查日志:ssh
登录服务器,立即执行pm2 logs <app_name> --err --lines 200
查看错误日志。[ ]
查状态:pm2 status
,检查应用的status
是否为online
,restarts
次数是否异常高。[ ]
决策: 如果是新上线导致,通过修改软链接并pm2 reload <app_name>
立即回滚。[ ]
深度定位:- 客户端问题:
scp
下载.next
目录,在本地进行 Source Map 解码。 - 服务端问题:
[ ]
仔细分析pm2 logs
中的堆栈跟踪。[ ]
检查ecosystem.config.js
中的环境变量是否正确。[ ]
使用pm2 monit
或htop
检查应用的内存和 CPU 使用情况,排查资源耗尽问题。[ ]
使用ls -l
检查文件权限是否正确。
- 客户端问题:
[ ]
修复与预防:[ ]
修复代码后,通过 CI/CD 流程或手动部署新版本。[ ]
强烈建议: 推动运维或自己动手,将 PM2 的日志(位于~/.pm2/logs/
)通过 Filebeat 等工具转发到集中的日志平台(如 ELK, Loki)。[ ]
强烈建议: 为你的应用创建一个/api/health
健康检查接口,并使用外部监控工具(如 Uptime Kuma 或一个简单的 cron 脚本)定期调用,实现应用存活监控。
🌙 总结
线上白屏问题在 PM2 环境下的排查流程与 Docker 有很多相似之处,但也有其独特的挑战和解决方案。通过系统化的日志分析、环境检查和资源监控,你可以快速定位问题并恢复服务。同时,建立良好的日志和健康检查机制,将大大提高未来问题的排查效率。