胖叔网络科技 网络科技 · 技术笔记
部署运维

PM2 实用配置:小内存机器的进程守护方案

PM2 不只是"让进程后台运行"。配好内存上限重启、日志轮转和开机自启,才算真正把服务交给它管。

# Node.js# PM2# 日志# 进程守护

#PM2 解决的是什么问题

直接 node server.js 有四个致命问题:

  1. 关掉 SSH 进程就没了 —— 需要 nohup 或 systemd
  2. 崩溃后不会自动恢复 —— 半夜挂了没人知道
  3. 重启服务器后不会自启 —— 需要额外配置
  4. 日志无管理,会撑爆磁盘 —— 这是最容易被忽略的

PM2 把这四件事一次解决。

#用配置文件而不是命令行参数

命令行启动的参数下次重建就忘了。写成配置文件,才能复现和版本管理:

JavaScript
// ecosystem.config.cjs
module.exports = {
  apps: [{
    name: 'nebula-blog',
    script: 'server/index.js',
    cwd: '/www/wwwroot/blog',

    instances: 1,
    exec_mode: 'fork',          // 见下文说明,低配机器不要用 cluster

    max_memory_restart: '380M', // 小内存机器的保命参数
    autorestart: true,
    max_restarts: 20,
    min_uptime: '20s',
    restart_delay: 3000,

    env: {
      NODE_ENV: 'production',
      PORT: 3000,
      HOST: '127.0.0.1',
    },

    output: '/www/wwwlogs/blog-out.log',
    error:  '/www/wwwlogs/blog-err.log',
    merge_logs: true,
    time: true,
  }],
};

启动:

Bash
pm2 start ecosystem.config.cjs
pm2 save

#几个参数的实际意义

#max_memory_restart 是最重要的一个

1 GB 内存的机器上,如果 Node 进程因为内存泄漏涨到 600 MB,整个系统会开始 swap,所有服务一起变慢,比直接重启这个进程糟糕得多。

设成 380M 的效果是:进程涨到 380 MB 时 PM2 主动重启它,用一个短暂的中断换整个系统的稳定

这个值怎么定?参考公式:

CODE
max_memory_restart ≈ (可用内存 - 其他服务占用) × 0.6

比如可用 432 MB,其他服务占 100 MB,那么:

CODE
(432 - 100) × 0.6200 MB

保守一点设 380 MB 也是可以的,关键不要设成接近物理内存上限的值

#min_uptime 防止无限重启循环

如果应用有启动即崩的问题,没有 min_uptime 的话 PM2 会疯狂重启,刷满日志文件并占满 CPU

min_uptime: '20s' 表示"进程活过 20 秒才算启动成功"。配合 max_restarts: 20,20 次都失败后 PM2 会停止尝试并标记为 errored,这时你能从 pm2 list 一眼看出问题。

#exec_mode 为什么选 fork

cluster 模式会用 Node 的 cluster 模块起多个进程共享端口,理论上能利用多核。但有两个坑

  1. 每个 worker 是独立进程,内存翻倍 —— 2 核机器就是 2 倍内存
  2. 如果用 SQLite 这类文件数据库,多进程写会锁冲突

对 1 GB 内存的个人站点,fork 单实例是正确选择。真需要多核,说明该考虑升级机器或换架构了。

#日志管理

#内置日志没有轮转

PM2 默认的日志文件会无限增长。一个中等流量的站点,几个月就能写到几个 GB,最后把磁盘写满导致服务崩溃。

必须装日志轮转模块

Bash
pm2 install pm2-logrotate

# 配置:单文件最大 20MB,保留 7 份,每天切割
pm2 set pm2-logrotate:max_size 20M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:rotateInterval '0 0 * * *'
pm2 set pm2-logrotate:compress true

验证:

Bash
pm2 list   # 应该能看到 pm2-logrotate 模块在运行
ls -lh /root/.pm2/logs/

#应用自身也应该写日志

PM2 抓的是 stdout/stderr。结构化日志建议应用自己写到文件,便于按级别过滤:

JavaScript
function log(level, msg, extra = {}) {
  const line = JSON.stringify({
    t: new Date().toISOString(),
    level,
    msg,
    ...extra,
  });
  fs.appendFileSync('/www/wwwlogs/app.log', line + '\n');
}

配合 logrotate 按天切割即可。

#开机自启

Bash
pm2 startup

这个命令会输出一行需要你手动执行的命令(类似 sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u root --hp /root)。复制执行它,然后:

Bash
pm2 save    # 保存当前进程列表,开机时按这个列表恢复

pm2 save 容易被忘掉。 只做 pm2 startupsave,重启后是空的。

注意:每次新增/删除应用后都要重新 pm2 save,否则重启后恢复的是旧列表。

#日常运维命令

Bash
pm2 list                      # 进程列表
pm2 monit                     # 实时监控(CPU/内存/日志)
pm2 logs nebula-blog          # 实时日志
pm2 logs nebula-blog --lines 100 --nostream   # 看最近 100 行
pm2 describe nebula-blog      # 详细信息
pm2 restart nebula-blog       # 重启
pm2 reload nebula-blog        # 平滑重载(fork 模式下等同 restart)
pm2 stop nebula-blog          # 停止
pm2 delete nebula-blog        # 删除
pm2 flush                     # 清空所有日志

#部署更新代码后的正确流程

Bash
cd /www/wwwroot/blog
git pull            # 或上传新代码
npm install --omit=dev
pm2 reload nebula-blog
pm2 save

顺序不能反:先更新代码和依赖,再重启进程。反过来会出现"新进程加载旧代码"的情况。

#常见问题

Q:pm2 list 显示 online 但访问 502

进程活着不代表在监听端口。查:

Bash
ss -ltn | grep 3000
pm2 logs nebula-blog --lines 50 --nostream

多半是启动时报错但进程还没退出,或者监听地址写错了。

Q:pm2 startup 后重启还是没自启

检查 pm2 save 有没有执行,以及 systemd 服务是否真的启用了:

Bash
systemctl status pm2-root

Q:内存限制触发了但日志里没有重启记录

max_memory_restart 触发时 PM2 会记录一条 PM2 | Process ... restarted because it exceeded the max memory。如果没有,说明是被系统 OOM Killer 杀的,而不是 PM2 主动重启 —— 这两种情况要分开排查。

#相关资源

以下资料可直接下载:

SH

服务器只读巡检脚本

一条命令采集系统、CPU、内存、磁盘、Docker、端口与防火墙信息,不修改任何系统状态,适合部署前体检。

server-inspect.sh 1.4 KB 83 次下载

#小结

PM2 用好的关键就三条:

  1. 配置文件化,参数进版本管理
  2. max_memory_restart 必设,小内存机器的保命符
  3. 日志轮转必装,否则迟早被日志写满磁盘

配置模板可以在文末下载。

附件下载 共 2 个文件,点击直接下载

SH

数据自动备份脚本

每日定时备份站点数据库与附件目录,自动保留最近 14 天并输出清理日志,可直接配到宝塔「计划任务」。

auto-backup.sh 1.2 KB 26 次下载 来自:PM2 实用配置:小内存机器的进程守护方案
CJS

PM2 进程守护配置模板

Node 服务的 PM2 配置,含内存上限自动重启、日志切割、开机自启与环境变量加载,适配低内存服务器。

ecosystem.config.cjs 956 B 71 次下载 来自:PM2 实用配置:小内存机器的进程守护方案