pm2的 cluster 模式下子应用无法使用jemalloc内存分配模式
- Node.js
- 1天前
- 6热度
关键点:PM2 的 cluster 模式下,不能只在 ecosystem.config.js 的 env 里写 LD_PRELOAD。
因为 LD_PRELOAD 必须在 Node 进程启动、动态链接器加载共享库之前存在;而 PM2 cluster 模式会先启动 Node worker,再把应用环境变量从 pm2_env 写入 process.env,此时加载 jemalloc 已经晚了。
正确做法是:让 PM2 守护进程本身带着 LD_PRELOAD 启动。这样它创建的所有 cluster worker 都会继承 jemalloc。
立即生效
已有 PM2 应用:
先确认 jemalloc 存在
ls -l /usr/local/lib/libjemalloc.so.2
保存当前 PM2 应用列表
pm2 save
关闭当前没有加载 jemalloc 的 PM2 daemon
pm2 kill
带着 jemalloc 重新启动 PM2 daemon,并恢复所有应用
LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 pm2 resurrect
如果是首次通过配置文件启动:
pm2 kill
LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 \
pm2 start ecosystem.config.js --env production
pm2 save
之后执行:
pm2 reload all
pm2 restart all
pm2 scale api 8
新创建的 cluster worker 都会继续继承 jemalloc。PM2 官方项目也记录过 cluster 模式下应用级 LD_PRELOAD 不生效,需要让 PM2 daemon 在该环境变量下启动。
ecosystem.config.js 示例
module.exports = {
apps: [
{
name: "api",
cwd: "/data/api",
script: "./app.js",
exec_mode: "cluster",
instances: "max",
env: {
NODE_ENV: "production"
}
}
]
};
这里不需要再写:
env: {
LD_PRELOAD: "/usr/local/lib/libjemalloc.so.2"
}
即使写了,也只能让代码中的 process.env.LD_PRELOAD 看起来有值,不能证明动态链接器真正加载了 jemalloc。
验证每个 cluster 进程
不要只检查:
pm2 env 0
应该直接检查进程的内存映射。
如果服务器安装了 jq:
pm2 jlist |
jq -r '.[] | select(.pid > 0) | [.name, .pm_id, .pid] | @tsv' |
while IFS=$'\t' read -r name id pid; do
if grep -q jemalloc "/proc/$pid/maps"; then
echo "$name[$id] pid=$pid:jemalloc 已加载"
else
echo "$name[$id] pid=$pid:jemalloc 未加载"
fi
done
正常结果类似:
api[0] pid=21531:jemalloc 已加载
api[1] pid=21542:jemalloc 已加载
api[2] pid=21553:jemalloc 已加载
api[3] pid=21564:jemalloc 已加载
也可以检查单个进程:
grep jemalloc /proc/21531/maps
应该能看到:
/usr/local/lib/libjemalloc.so.2
检查 PM2 daemon 本身:
PM2_PID=$(pgrep -o -f 'PM2.*God Daemon')
echo "PM2 daemon pid: $PM2_PID"
grep jemalloc "/proc/$PM2_PID/maps"
配置服务器重启后仍然生效
如果使用了:
pm2 startup
pm2 save
还需要把 LD_PRELOAD 写入 PM2 的 systemd 服务,否则服务器重启后,systemd 会启动一个没有 jemalloc 的 PM2 daemon。PM2 的启动钩子会通过保存的进程列表恢复应用。
先查看服务名称:
systemctl list-unit-files | grep pm2
通常类似:
pm2-root.service
pm2-ubuntu.service
pm2-node.service
编辑对应服务:
sudo systemctl edit pm2-ubuntu
填写:
[Service]
Environment="LD_PRELOAD=/usr/local/lib/libjemalloc.so.2"
然后执行:
pm2 save
sudo systemctl daemon-reload
sudo systemctl restart pm2-ubuntu
重新验证:
pm2 jlist |
jq -r '.[].pid' |
while read -r pid; do
if [ "$pid" -gt 0 ] && grep -q jemalloc "/proc/$pid/maps"; then
echo "$pid jemalloc loaded"
else
echo "$pid jemalloc not loaded"
fi
done
最终推荐配置
当前立即应用
pm2 save
pm2 kill
LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 pm2 resurrect
永久应用
sudo systemctl edit pm2-你的用户名
systemd 配置:
[Service]
Environment="LD_PRELOAD=/usr/local/lib/libjemalloc.so.2"
这种方式会让同一个 PM2 daemon 管理的所有 cluster 应用和所有实例都使用 jemalloc。不要使用 /etc/ld.so.preload,除非明确希望让服务器上几乎所有动态链接程序都加载 jemalloc。
