05. 使用 PM2 管理 Node.js 后端
1. 本章目标
本章把 Express 构建为生产 JavaScript,并用 PM2 保持进程运行、查看日志和配置开机启动。
2. 为什么不能直接使用开发命令
开发命令 tsx watch src/server.ts 会监听文件变化,适合本地热更新。生产环境应先用 TypeScript 编译,再运行 dist/server.js:
TypeScript 源码
→ npm run build -w server
→ server/dist/server.js
→ Node.js 22
→ PM2 管理进程
PM2 不替你执行类型检查,也不会让错误代码变正确。发布前必须先完成质量检查。
3. 准备后端构建产物
cd /var/www/airport-operations-explorer
npm ci
npm run typecheck
npm test
npm run build -w server
test -f server/dist/server.js && echo "server build exists"
npm ci 严格使用 package-lock.json,并要求 lockfile 与 package.json 一致,适合可重复部署。
先前台测试:
cd /var/www/airport-operations-explorer/server
NODE_ENV=production node dist/server.js
另一个终端执行:
curl -i http://127.0.0.1:3000/health
成功后按 Ctrl+C 停止前台进程。
4. 创建 ecosystem 配置
在仓库根目录创建 ecosystem.config.cjs。项目使用 ESM,但 PM2 配置用 .cjs 可以避免配置加载器差异:
module.exports = {
apps: [
{
name: "airport-api",
cwd: "/var/www/airport-operations-explorer/server",
script: "dist/server.js",
interpreter: "node",
instances: 1,
exec_mode: "fork",
env: {
NODE_ENV: "production",
HOST: "127.0.0.1",
PORT: "3000"
},
max_memory_restart: "750M",
kill_timeout: 10000,
listen_timeout: 10000,
time: true,
autorestart: true
}
]
};
字段解释:
| 字段 | 含义 |
|---|---|
name |
PM2 命令和日志中使用的稳定名称 |
cwd |
应用工作目录,决定 dotenv 从哪里读取 .env |
script |
编译后的 Node.js 入口 |
interpreter |
使用 Node.js 执行脚本 |
instances |
进程数量,初学阶段先使用 1 |
exec_mode |
fork 是单进程模式,行为最容易理解 |
env |
非敏感的生产运行参数 |
max_memory_restart |
超过阈值时重启,但不能代替内存泄漏调查 |
kill_timeout |
允许 Express 完成优雅关闭的最长等待时间 |
time |
PM2 日志带时间戳 |
数据库密码保存在权限为 600 的 server/.env,不要写进 ecosystem 文件并提交 Git。
项目的 server.ts 会监听 SIGINT 和 SIGTERM,停止接收请求并关闭数据库连接。kill_timeout 给这个过程留出时间。
5. 启动和检查 PM2
cd /var/www/airport-operations-explorer
pm2 start ecosystem.config.cjs
pm2 status
pm2 describe airport-api
pm2 logs airport-api --lines 100
测试健康检查:
curl -i http://127.0.0.1:3000/health
常用命令:
pm2 restart airport-api
pm2 stop airport-api
pm2 start airport-api
pm2 delete airport-api
pm2 logs airport-api
pm2 monit
restart停止旧进程后再启动;reload主要用于 cluster 模式的平滑替换;- 当前单实例 fork 模式使用
restart,会有很短的中断; delete会从 PM2 进程列表移除应用,不会删除代码。
6. 配置服务器重启后自动启动
先执行:
pm2 startup
PM2 会输出一条带 sudo、当前用户名、home 路径和 PATH 的命令。复制执行它,不要自行简化,因为 nvm 的 Node 路径需要写入 systemd 环境。
保存当前进程列表:
pm2 save
验证 systemd 服务名:
systemctl list-units --type=service | grep pm2
最终可以安排一次维护窗口重启服务器:
sudo reboot
重新登录后:
pm2 status
curl -i http://127.0.0.1:3000/health
只执行 pm2 startup 而不执行 pm2 save,systemd 启动时没有要恢复的应用清单。
7. 环境变量变化后的处理
PM2 重启时默认可能沿用已有进程环境。修改 ecosystem 的 env 后:
pm2 restart ecosystem.config.cjs --only airport-api --update-env
本项目的敏感配置由 dotenv 从 server/.env 读取。修改 .env 后也需要重启进程:
pm2 restart airport-api
然后重新执行健康检查。
8. 日志和轮转
查看路径:
pm2 describe airport-api
pm2 logs airport-api --lines 200
安装日志轮转模块:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 20M
pm2 set pm2-logrotate:retain 14
pm2 set pm2-logrotate:compress true
检查磁盘:
du -sh ~/.pm2/logs
df -h
日志必须保留 request ID、路径、状态码和耗时,但不能记录数据库密码、Cookie 或 Authorization。
9. 常见问题
PM2 显示 errored
pm2 logs airport-api --err --lines 200
常见原因是 cwd 错误、dist/server.js 不存在、端口已占用或环境变量校验失败。
PM2 使用了错误 Node 版本
which node
node --version
pm2 report
重新执行正确用户环境下的 pm2 startup,并检查 systemd 服务中的 PATH。
地址已被占用
sudo ss -lntp | grep ':3000'
确定占用进程是谁。不要直接杀掉不认识的进程。
数据库 down
PM2 在线只表示 Node.js 进程存在。/health 返回 503 时应检查 MySQL 和应用账号,不要反复重启 PM2。
10. 本章验收
pm2 status中airport-api为 online;127.0.0.1:3000/health返回预期状态;- 退出 SSH 后进程仍运行;
- PM2 已保存进程清单;
- 服务器重启后应用能恢复;
- 日志轮转已配置。