应用写完了、测试过了,最后一步永远是”怎么让它稳稳地在服务器上跑起来、挂了能自动拉起、还能顺手看日志”。直接 node app.js 起进程的问题很直白:SSH 一断进程就没了,崩了没人管,多进程要手动一个个盯。PM2 就是专门解决这件事的守护进程管理工具——它常驻在后台守护你的应用,进程异常退出会自动重启,同时把多进程管理、内置负载均衡、日志系统、监控面板打包在一个命令行里。一般在服务上线时,我们就用 PM2 来托管进程。
这篇把 PM2 从安装、常用命令、两种使用方式,一路讲到两个实战场景(资讯类 Node 应用部署、批量调度 Python 脚本),并附上一份 ecosystem 启动参数速查表。
一句话定位:PM2 = 进程守护 + 自动重启 + 多进程管理 + 日志/监控,把”手动
node app.js”升级成”上线级的进程托管”。
TL;DR
- PM2 能干什么:守护进程(崩溃/异常自动重启)、统一管理多个进程、
cluster模式内置负载均衡、集中日志、内存超限重启、监听文件改动自动重启、监控面板。 - 安装:
npm i pm2 -g,全局安装后用pm2命令操作。 - 两种用法:命令行把参数写在命令里,适合临时起单个进程;配置文件(
ecosystem.config.js)把参数持久化到文件里,适合批量、可控、可复现的上线管理——推荐用配置文件。 - 速查:
pm2 start / list / show / logs / monit / restart / reload / stop / delete / flush / kill;reload是 0 秒停机热重载(cluster 模式),restart是先杀后起。 - 开机自启:
pm2 startup生成开机脚本 +pm2 save保存当前进程列表,两者配合才能重启机器后自动拉起。 - 不止 Node:通过
interpreter参数(如python),PM2 也能守护和批量调度 Python 等脚本,还能各自注入环境变量、参数、日志路径。
1. PM2 安装
PM2 是一个全局命令行工具,直接用 npm 全局安装:
npm i pm2 -g
装完后 pm2 命令就全局可用了。第一次执行 pm2 命令时,它会自动在后台拉起一个常驻的守护进程(daemon),后续所有被管理的应用都挂在它下面。
2. 常用命令速查
PM2 的日常操作基本围绕”启动 → 查看 → 重启/停止 → 日志”这条主线。下面按功能分组,0 代表进程 id,all 代表所有进程。
| 目的 | 命令 |
|---|---|
| 启动应用(进程名默认取文件名) | pm2 start app.js |
| 启动并指定进程名 | pm2 start app.js --name mynode |
| 列出所有进程 | pm2 list / pm2 ls |
| 查看某进程详情 | pm2 show 0 / pm2 info 0 |
| 监控面板(CPU/内存) | pm2 monit |
| 停止 / 停止全部 | pm2 stop 0 / pm2 stop all |
| 删除 / 删除全部 | pm2 delete 0 / pm2 delete all |
| 重启 / 重启全部 | pm2 restart 0 / pm2 restart all |
| 0 秒停机热重载 / 全部 | pm2 reload 0 / pm2 reload all |
| 查看日志 / 指定进程日志 | pm2 logs / pm2 logs 0 |
| 清空所有日志 | pm2 flush |
| 重载日志 | pm2 reloadLogs |
| 杀掉 PM2 守护进程本身 | pm2 kill |
对应的完整命令示例:
# 启动 Node 应用,进程的默认名称为文件名(app)
pm2 start app.js
# 启动 Node,并指定进程名称为 mynode
pm2 start app.js --name mynode
# 显示所有进程
pm2 list # 或 pm2 ls
# 查看进程 id 为 0 的详细信息
pm2 show 0 # 或 pm2 info 0
# 进入监视页面,监视每个进程的 CPU 和内存使用情况
pm2 monit
# 停止 id 为 0 的进程 / 停止所有进程
pm2 stop 0
pm2 stop all
# 删除 id 为 0 的进程 / 删除所有进程
pm2 delete 0
pm2 delete all
# 重启 id 为 0 的进程 / 重启所有进程
pm2 restart 0
pm2 restart all
# 0 秒停机重载 id 为 0 的进程(用于 networked 进程)
pm2 reload 0
pm2 reload all
# 显示所有进程日志 / 指定进程日志
pm2 logs
pm2 logs 0
# 清空所有日志文件 / 重载日志
pm2 flush
pm2 reloadLogs
# 杀死 PM2 守护进程
pm2 kill
restart和reload的区别:restart是”先停止旧进程、再启动新进程”,中间会有短暂的服务中断;reload是滚动热重载,在cluster模式下逐个替换 worker,做到 0 秒停机,适合线上不能断流量的服务。
3. 两种使用方式:命令行 vs 配置文件
使用 PM2 主要有 2 种方式:
- 命令行
- 配置文件
虽然使用配置文件的方式最终仍然需要用命令行来启动,但两者的主要区别是:
- 命令行方式:需要把各种配置参数在命令行里逐个输入;
- 配置文件方式:把各种配置参数放在配置文件里,命令行只负责”按这个文件启动”。
举个例子:你需要启动一个应用,指定应用名称为 newApp、入口文件路径为 index.js。我们看看这两个参数怎么用两种方式带进去。
3.1 命令行方式
pm2 start index.js --name newApp
3.2 配置文件方式
先创建一个配置文件(pm2.config.js),内容如下:
// 文件名为 pm2.config.js
module.exports = {
apps: [
{
name: "newApp", // 应用名称
script: "./index.js", // 入口文件
},
],
};
然后在命令行执行以下命令,表示以指定的配置文件启动应用:
pm2 start pm2.config.js
3.3 该用哪种?
对比下来结论很清晰:使用配置文件的方式,可以把各种参数、环境变量等内容持久化地保留在文件里,方便批量管理多个应用,也避免在命令行里由于遗忘、手误等原因导致启动参数不可控。因此除了临时调试起单个进程,上线场景一律推荐用配置文件。
4. 开机自启与持久化
守护进程解决的是”应用崩了自动拉起”,但机器重启后 PM2 自身也没了,进程自然也不在了。要让服务器重启后自动恢复所有应用,需要两步配合:
# 1. 生成并注册开机自启脚本(根据当前系统 init 生成对应脚本)
pm2 startup
# 2. 保存当前正在运行的进程列表(作为开机恢复的快照)
pm2 save
pm2 startup 负责”让 PM2 守护进程在开机时被拉起”,pm2 save 负责”记住此刻要恢复哪些应用”。两者缺一不可:只 startup 不 save,重启后 PM2 起来了却是空的;只 save 不 startup,快照存了但开机根本没人来恢复。
5. 实战一:资讯类 Node 应用部署
以一个资讯类站点为例(构建产物 + npm start 启动),上线流程大致如下:
cd /project
npm install
npm run build
# 用 npm start 作为启动命令,指定进程名为站点域名,便于识别
pm2 start --name "funnylegends.top" npm -- start
# 查看进程状态
pm2 ls
这里的关键是 pm2 start ... npm -- start:让 PM2 托管的是 npm start 这条命令(-- 之后的 start 是传给 npm 的参数),而不是直接跑某个 .js 文件。这样就能复用项目 package.json 里已经定义好的启动脚本。
6. 实战二:批量调度 Python 脚本
PM2 并不局限于 Node——通过 interpreter 指定解释器,它同样能守护和批量调度 Python 脚本,还能给每个实例单独注入环境变量、命令行参数和日志路径。下面用一个最小示例演示。
6.1 Python 脚本
一个读取环境变量、接收命令行参数的简单脚本:
# test.py
import os
import sys
env = os.environ.get('SCRIPT_ENV')
print(env)
arg1 = sys.argv[1]
arg2 = sys.argv[2]
print(arg1, arg2)
def add_func(a, b):
sums = a + b
return sums
a = 1
b = 2
print(add_func(a, b))
6.2 PM2 调度脚本
用一份 ecosystem.config.js 同时定义两个实例,各自带不同的参数、环境变量和日志文件:
// ecosystem.config.js
module.exports = {
apps: [
{
name: "cph-sg1-00001",
args: ["cph-sg1-00001", "game-001"],
cwd: "/data/apps/python",
script: "/data/apps/python/test.py",
interpreter: "python",
out_file: "/data/apps/python/cph-sg1-00001.log",
error_file: "/data/apps/python/cph-sg1-00001.log",
autorestart: false,
log_date_format: "YYYY-MM-DD HH:mm:ss",
env: {
SCRIPT_ENV: "production",
},
},
{
name: "cph-sg1-00002",
args: ["cph-sg1-00002", "game-002"],
cwd: "/data/apps/python",
script: "/data/apps/python/test.py",
interpreter: "python",
out_file: "/data/apps/python/cph-sg1-00002.log",
error_file: "/data/apps/python/cph-sg1-00002.log",
autorestart: false,
log_date_format: "YYYY-MM-DD HH:mm:ss",
env: {
SCRIPT_ENV: "development",
},
},
],
};
几个要点:
interpreter: "python"告诉 PM2 用 Python 来跑script,而不是当成 Node 脚本;args是传给脚本的命令行参数,对应脚本里的sys.argv;env给每个实例注入独立的环境变量(这里一个production、一个development);autorestart: false表示脚本正常跑完退出后不自动重启(适合一次性/定时任务,而非常驻服务);out_file/error_file指定各自的日志文件,配合log_date_format给每行日志打上时间戳。
6.3 执行日志
启动后,两个实例分别按各自的环境变量和参数运行,日志落到对应文件:
cat cph-sg1-00001.log
2024-06-14 16:50:55: production
2024-06-14 16:50:55: ('cph-sg1-00001', 'game-001')
2024-06-14 16:50:55: 3
cat cph-sg1-00002.log
2024-06-14 16:50:55: development
2024-06-14 16:50:55: ('cph-sg1-00002', 'game-002')
2024-06-14 16:50:55: 3
可以看到 SCRIPT_ENV 和 args 都按各自实例的配置生效了——这就是用一份配置文件批量拉起、参数各自隔离的效果。
7. ecosystem 启动参数速查
配置文件(以及命令行)里常用的启动参数如下:
| 参数 | 说明 |
|---|---|
script | 启动脚本路径 |
instances | 应用启动实例个数,仅在 cluster 模式有效,默认为 fork |
exec_mode | 应用启动模式,支持 fork 和 cluster 模式 |
name | 指定 app 名字 |
watch | 监听重启:启用后文件夹或子文件夹变化时应用自动重启 |
ignore_watch | 忽略监听的文件夹,支持正则,配合 watch 使用 |
max_memory_restart | 最大内存限制,超出自动重启 |
env | 环境变量,object 类型,如 {"NODE_ENV":"production","ID":"42"} |
log | 指定 log 位置;若要指定新位置,需先删掉原 process 再重新启动 |
output | 指定 output(stdout)log 位置 |
error | 指定 error(stderr)log 位置 |
log_date_format | 指定日志日期格式,如 YYYY-MM-DD HH:mm:ss |
args | 传给脚本的额外参数 |
restart_delay | 异常自动重启时的延时(毫秒) |
autorestart | 默认为 true,发生异常时自动重启 |
cron_restart | 按 crontab 时间格式重启应用,目前只支持 cluster 模式 |
小结
PM2 的核心价值可以浓缩成一句话:把”手动跑进程”升级为”上线级的进程托管”。
- 日常操作围绕
start / list / logs / restart / reload / stop / delete展开,其中reload的 0 秒热重载是线上不断流的关键; - 临时调试用命令行,正式上线用
ecosystem.config.js配置文件,把参数、环境变量、日志路径持久化下来,做到可复现、可批量; - 记得
pm2 startup+pm2 save配合,才能扛住机器重启; - 借助
interpreter,PM2 还能跳出 Node 的范畴,统一托管和批量调度 Python 等脚本,每个实例参数、环境、日志各自隔离。