Skip to content

PM2 进程管理器完全指南

PM2 是一个用于 Node.js 应用程序的生产级进程管理器,具有内置的负载均衡器。它允许您保持应用程序永远活跃,无需停机重新加载它们,并简化常见的系统管理任务。

什么是 PM2

PM2 是一个守护进程管理器,帮助您管理和保持应用程序在线运行。它主要用于 Node.js 应用程序,但也支持其他编程语言。

核心特性

  • 零停机重载:优雅地重启应用而不丢失连接
  • 负载均衡:内置负载均衡器,支持集群模式
  • 自动重启:应用崩溃时自动重启
  • 实时监控:CPU 和内存使用监控
  • 日志管理:集中化日志管理
  • 启动脚本:系统重启后自动启动应用

安装 PM2

bash
# 全局安装 PM2
npm install -g pm2

# 验证安装
pm2 --version

启动应用

start 命令

启动并守护化应用程序:

bash
# 基本用法
pm2 start app.js

# 指定应用名称
pm2 start app.js --name "my-app"

# 启动多个实例(集群模式)
pm2 start app.js -i 4

# 使用生态文件启动
pm2 start ecosystem.config.js
javascript
// 示例应用
const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.send("Hello PM2!");
});

app.listen(3000, () => {
  console.log("Server running on port 3000");
});
javascript
module.exports = {
  apps: [
    {
      name: "my-app",
      script: "./app.js",
      instances: "max",
      exec_mode: "cluster",
      env: {
        NODE_ENV: "development",
      },
      env_production: {
        NODE_ENV: "production",
        PORT: 8080,
      },
    },
  ],
};

常用启动选项

选项描述示例
-n, --name设置进程名称pm2 start app.js --name api-server
-i, --instances启动实例数量pm2 start app.js -i 4
--watch监听文件变化自动重启pm2 start app.js --watch
-o, --output指定 stdout 日志文件pm2 start app.js -o ./logs/out.log
-e, --error指定 stderr 日志文件pm2 start app.js -e ./logs/error.log

停止应用

bash
# 停止特定应用
pm2 stop app-name
pm2 stop 0  # 使用进程 ID

# 停止所有应用
pm2 stop all

# 停止并删除应用
pm2 delete app-name
pm2 delete all

重启应用

bash
# 重启特定应用
pm2 restart app-name

# 重启所有应用
pm2 restart all

# 零停机重载(仅支持 HTTP/HTTPS 应用)
pm2 reload app-name

查看进程状态

bash
# 列出所有进程
pm2 list
pm2 ls    # 简写
pm2 ps    # 简写
pm2 status # 简写

# 以 JSON 格式输出
pm2 jlist

# 紧凑格式显示
pm2 list --mini-list

输出示例:

┌─────┬─────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id  │ name        │ namespace   │ version │ mode    │ pid      │ uptime │ ↻    │ status    │ cpu      │ mem      │ user     │ watching │
├─────┼─────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0   │ my-app      │ default     │ 1.0.0   │ cluster │ 12345    │ 2h     │ 0    │ online    │ 0.2%     │ 45.2mb   │ user     │ disabled │
└─────┴─────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

describe 命令

获取进程的详细信息:

bash
# 查看应用详细信息
pm2 describe app-name
pm2 desc app-name  # 简写
pm2 info app-name  # 简写
pm2 show app-name  # 简写

输出包含:

  • 进程状态和统计信息
  • 内存和 CPU 使用情况
  • 环境变量
  • 日志文件位置
  • 重启次数和时间

进程操作

缩放集群

bash
# 将应用扩展到 4 个实例
pm2 scale my-app 4

# 增加 2 个实例
pm2 scale my-app +2

# 减少 1 个实例
pm2 scale my-app -1

发送信号

bash
# 发送自定义信号
pm2 sendSignal SIGUSR2 my-app

# 发送消息到进程
pm2 send my-app message-data

重置计数器

bash
# 重置重启计数器
pm2 reset my-app
pm2 reset all

高级功能

监听文件变化

bash
# 启动时启用文件监听
pm2 start app.js --watch

# 指定监听路径
pm2 start app.js --watch ./src

# 忽略特定文件或目录
pm2 start app.js --watch --ignore-watch "node_modules logs"

# 设置重启延迟
pm2 start app.js --watch --watch-delay 4000

注意

文件监听功能不建议在生产环境中使用,它主要用于开发环境。

内存限制和自动重启

bash
# 内存超过 100MB 时自动重启
pm2 start app.js --max-memory-restart 100M

# 设置最大重启次数
pm2 start app.js --max-restarts 10

# 设置重启延迟
pm2 start app.js --restart-delay 3000

# 指数回退重启延迟
pm2 start app.js --exp-backoff-restart-delay 100

定时重启

bash
# 使用 cron 表达式定时重启
pm2 start app.js --cron "0 0 * * *"  # 每天午夜重启

# 常用 cron 表达式示例
# "0 */6 * * *"    # 每 6 小时重启一次
# "0 0 */3 * *"    # 每 3 天重启一次
# "0 2 * * 1"      # 每周一凌晨 2 点重启

执行外部命令

bash
# 执行非 Node.js 应用
pm2 start "python script.py" --name python-app

# 使用特定解释器
pm2 start script.py --interpreter python3

# 传递参数给解释器
pm2 start app.js --node-args="--harmony"

监控与日志

实时监控

monit 命令

bash
# 启动实时监控界面
pm2 monit

# 新版监控面板
pm2 dashboard

监控界面显示:

  • CPU 使用率实时图表
  • 内存使用量
  • 进程状态
  • 实时日志流

系统信息

bash
# 显示系统信息
pm2 sysmonit

# 以 JSON 格式显示系统信息
pm2 slist

日志管理

logs 命令

bash
# 查看所有应用日志
pm2 logs

# 查看特定应用日志
pm2 logs my-app

# 实时跟踪日志
pm2 logs --lines 200

# 以 JSON 格式输出日志
pm2 logs --json

# 显示时间戳
pm2 logs --timestamp

# 查看错误日志
pm2 logs my-app --err

# 查看输出日志
pm2 logs my-app --out

日志配置选项

bash
# 启动时指定日志文件
pm2 start app.js \
  --output ./logs/out.log \
  --error ./logs/error.log \
  --log ./logs/combined.log

# 禁用日志
pm2 start app.js --disable-logs

# 合并日志(将不同实例的日志合并)
pm2 start app.js --merge-logs

# 自定义日志时间格式
pm2 start app.js --log-date-format "YYYY-MM-DD HH:mm:ss Z"

# 设置日志类型
pm2 start app.js --log-type json

日志轮转

bash
# 安装日志轮转模块
pm2 install pm2-logrotate

# 配置日志轮转
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 30
pm2 set pm2-logrotate:compress true

# 手动清空日志
pm2 flush

# 重载日志文件
pm2 reloadLogs

配置管理

生态文件

生成生态文件

bash
# 生成默认生态文件
pm2 ecosystem

# 生成简单模式生态文件
pm2 init simple

完整生态文件示例

javascript
module.exports = {
  apps: [
    {
      // 应用基本信息
      name: "api-server",
      script: "./dist/server.js",

      // 实例配置
      instances: "max", // 或具体数字,如 4
      exec_mode: "cluster", // 'fork' 或 'cluster'

      // 环境变量
      env: {
        NODE_ENV: "development",
        PORT: 3000,
        DB_HOST: "localhost",
      },
      env_production: {
        NODE_ENV: "production",
        PORT: 8080,
        DB_HOST: "prod-db-server",
      },

      // 日志配置
      log_file: "./logs/combined.log",
      out_file: "./logs/out.log",
      error_file: "./logs/error.log",
      log_date_format: "YYYY-MM-DD HH:mm:ss Z",

      // 重启策略
      max_restarts: 10,
      min_uptime: "10s",
      max_memory_restart: "512M",
      restart_delay: 4000,

      // 监听配置
      watch: false,
      watch_delay: 1000,
      ignore_watch: ["node_modules", "logs"],

      // 其他选项
      source_map_support: true,
      instance_var: "INSTANCE_ID",

      // 健康检查
      wait_ready: true,
      listen_timeout: 8000,
      kill_timeout: 5000,
    },
    {
      name: "worker",
      script: "./workers/index.js",
      instances: 2,
      exec_mode: "fork",
      cron_restart: "0 2 * * *", // 每天凌晨 2 点重启
      env: {
        NODE_ENV: "development",
      },
    },
  ],

  // 部署配置
  deploy: {
    production: {
      user: "ubuntu",
      host: ["prod-server-1", "prod-server-2"],
      ref: "origin/master",
      repo: "[email protected]:username/repo.git",
      path: "/var/www/production",
      "pre-deploy-local": "",
      "post-deploy": "npm install && pm2 reload ecosystem.config.js --env production",
      "pre-setup": "",
    },
    staging: {
      user: "ubuntu",
      host: "staging-server",
      ref: "origin/develop",
      repo: "[email protected]:username/repo.git",
      path: "/var/www/staging",
      "post-deploy": "npm install && pm2 reload ecosystem.config.js --env staging",
    },
  },
};
json
{
  "apps": [
    {
      "name": "api-server",
      "script": "./dist/server.js",
      "instances": "max",
      "exec_mode": "cluster",
      "env": {
        "NODE_ENV": "development",
        "PORT": 3000
      },
      "env_production": {
        "NODE_ENV": "production",
        "PORT": 8080
      }
    }
  ]
}

使用生态文件

bash
# 启动生态文件中的所有应用
pm2 start ecosystem.config.js

# 使用特定环境启动
pm2 start ecosystem.config.js --env production

# 只启动特定应用
pm2 start ecosystem.config.js --only api-server

# 重启生态文件中的应用
pm2 restart ecosystem.config.js

# 重载生态文件(零停机)
pm2 reload ecosystem.config.js

# 停止生态文件中的应用
pm2 stop ecosystem.config.js

# 删除生态文件中的应用
pm2 delete ecosystem.config.js

环境变量管理

查看环境变量

bash
# 查看进程的所有环境变量
pm2 env 0  # 进程 ID 为 0

# 在生态文件中使用不同环境
pm2 start ecosystem.config.js --env staging

动态更新环境变量

bash
# 使用 --update-env 强制更新环境变量
pm2 restart app-name --update-env

# 或简写
pm2 restart app-name -a

集群模式

集群模式基础

集群模式利用 Node.js 的 cluster 模块,可以在多个 CPU 核心上运行应用实例。

启动集群

bash
# 启动最大数量的实例(等于 CPU 核心数)
pm2 start app.js -i max

# 启动指定数量的实例
pm2 start app.js -i 4

# 使用生态文件启动集群
pm2 start ecosystem.config.js

集群配置示例

javascript
// ecosystem.config.js
module.exports = {
  apps: [
    {
      name: "cluster-app",
      script: "./app.js",
      instances: "max", // 或具体数字
      exec_mode: "cluster",

      // 集群专用配置
      instance_var: "INSTANCE_ID", // 实例变量名

      // 负载均衡配置
      listen_timeout: 8000,
      kill_timeout: 5000,
    },
  ],
};

应用代码适配

javascript
const express = require("express");
const app = express();

// 获取实例 ID(如果设置了 instance_var)
const instanceId = process.env.INSTANCE_ID || "unknown";

app.get("/", (req, res) => {
  res.json({
    message: "Hello from cluster!",
    instance: instanceId,
    pid: process.pid,
  });
});

app.get("/health", (req, res) => {
  res.status(200).json({
    status: "ok",
    instance: instanceId,
    uptime: process.uptime(),
  });
});

const port = process.env.PORT || 3000;

const server = app.listen(port, () => {
  console.log(`Instance ${instanceId} running on port ${port}, PID: ${process.pid}`);

  // 发送 ready 信号(如果启用了 wait_ready)
  if (process.send) {
    process.send("ready");
  }
});

// 优雅关闭处理
process.on("SIGINT", () => {
  console.log(`Instance ${instanceId} received SIGINT, shutting down gracefully`);
  server.close(() => {
    process.exit(0);
  });
});
javascript
// 处理 Socket.IO 的粘性会话
const express = require("express");
const http = require("http");
const socketIo = require("socket.io");

const app = express();
const server = http.createServer(app);

// 集群模式下的 Socket.IO 配置
const io = socketIo(server, {
  // 启用粘性会话
  sticky: true,

  // Redis 适配器(用于集群间通信)
  adapter: require("socket.io-redis")({
    host: "localhost",
    port: 6379,
  }),
});

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

io.on("connection", (socket) => {
  console.log(`User connected to instance ${process.env.INSTANCE_ID}`);

  socket.on("message", (data) => {
    io.emit("message", data); // 广播到所有实例
  });
});

server.listen(3000, () => {
  console.log(`Socket.IO server running on instance ${process.env.INSTANCE_ID}`);
});

零停机重载

bash
# 对集群应用进行零停机重载
pm2 reload app-name

# 重载所有集群应用
pm2 reload all

# 强制重载(即使没有变化)
pm2 reload app-name --force

零停机重载流程:

1. PM2 向第一个实例发送 SIGINT 信号
2. 应用处理完当前请求后优雅关闭
3. PM2 启动新实例替换旧实例
4. 重复此过程直到所有实例都被重载

集群监控

bash
# 查看集群状态
pm2 list

# 实时监控集群
pm2 monit

# 查看特定实例详情
pm2 describe app-name

生产环境部署

启动脚本

设置开机自启

bash
# 检测平台并生成启动脚本
pm2 startup

# 手动指定平台
pm2 startup systemd  # Linux systemd
pm2 startup launchd  # macOS
pm2 startup rcd      # FreeBSD
pm2 startup systemv  # Linux systemv

# 指定用户
pm2 startup --user username

# 设置服务名称
pm2 startup --service-name my-pm2-service

执行后会输出需要以 root 权限运行的命令,例如:

bash
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u username --hp /home/username

保存和恢复进程

bash
# 保存当前进程列表
pm2 save

# 恢复之前保存的进程
pm2 resurrect

# 清空保存的进程
pm2 cleardump

禁用启动脚本

bash
# 禁用开机自启
pm2 unstartup

部署自动化

使用 PM2 Deploy

bash
# 初始化部署环境
pm2 deploy ecosystem.config.js production setup

# 部署应用
pm2 deploy ecosystem.config.js production

# 部署到特定环境
pm2 deploy ecosystem.config.js staging

部署配置详解

javascript
// ecosystem.config.js - 部署配置
module.exports = {
  apps: [
    /* 应用配置 */
  ],

  deploy: {
    production: {
      // 服务器信息
      user: "deploy",
      host: ["prod-1.example.com", "prod-2.example.com"],

      // Git 仓库信息
      ref: "origin/master",
      repo: "[email protected]:username/repo.git",

      // 部署路径
      path: "/var/www/production",

      // SSH 配置
      ssh_options: "StrictHostKeyChecking=no",

      // 部署钩子
      "pre-deploy-local": 'echo "Local pre-deploy task"',
      "post-deploy": ["npm install", "npm run build", "pm2 reload ecosystem.config.js --env production"].join(" && "),
      "pre-setup": 'echo "Setting up production environment"',

      // 环境变量
      env: {
        NODE_ENV: "production",
      },
    },
  },
};

性能优化

内存和 CPU 优化

javascript
// ecosystem.config.js - 性能配置
module.exports = {
  apps: [
    {
      name: "optimized-app",
      script: "./app.js",

      // 实例配置
      instances: "max",
      exec_mode: "cluster",

      // 内存管理
      max_memory_restart: "1G",

      // Node.js 优化参数
      node_args: ["--max_old_space_size=1024", "--optimize-for-size", "--gc-interval=100"].join(" "),

      // PM2 优化
      min_uptime: "60s",
      max_restarts: 5,
      restart_delay: 5000,

      // 禁用不必要的功能
      pmx: false,
      automation: false,

      // 启用源码映射支持
      source_map_support: true,
    },
  ],
};

监控和警报

bash
# 安装监控模块
pm2 install pm2-server-monit

# 设置内存警报阈值
pm2 set pm2-server-monit:monitoring true
pm2 set pm2-server-monit:threshold_memory 80

# 安装日志轮转
pm2 install pm2-logrotate

最佳实践

1. 应用设计最佳实践

优雅关闭处理

javascript
// 正确的优雅关闭处理
const express = require("express");
const app = express();

let server;

// 启动服务器
function startServer() {
  server = app.listen(process.env.PORT || 3000, () => {
    console.log("Server started");

    // 通知 PM2 应用已就绪
    if (process.send) {
      process.send("ready");
    }
  });
}

// 优雅关闭函数
function gracefulShutdown(signal) {
  console.log(`Received ${signal}, shutting down gracefully`);

  server.close(() => {
    console.log("HTTP server closed");

    // 关闭数据库连接等
    // database.close();

    process.exit(0);
  });

  // 强制退出超时
  setTimeout(() => {
    console.error("Could not close connections in time, forcefully shutting down");
    process.exit(1);
  }, 10000);
}

// 监听关闭信号
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
process.on("SIGINT", () => gracefulShutdown("SIGINT"));

// 处理未捕获的异常
process.on("uncaughtException", (err) => {
  console.error("Uncaught Exception:", err);
  gracefulShutdown("uncaughtException");
});

process.on("unhandledRejection", (reason, promise) => {
  console.error("Unhandled Rejection at:", promise, "reason:", reason);
  gracefulShutdown("unhandledRejection");
});

startServer();

健康检查端点

javascript
// 添加健康检查端点
app.get("/health", (req, res) => {
  const health = {
    status: "ok",
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    pid: process.pid,
  };

  // 检查数据库连接等
  // if (!database.isConnected()) {
  //   health.status = 'error';
  //   return res.status(503).json(health);
  // }

  res.json(health);
});

app.get("/ready", (req, res) => {
  // 检查应用是否完全就绪
  res.json({ status: "ready" });
});

2. 配置最佳实践

生产环境配置模板

javascript
// ecosystem.production.config.js
module.exports = {
  apps: [
    {
      name: "prod-app",
      script: "./dist/server.js",

      // 实例配置
      instances: "max",
      exec_mode: "cluster",

      // 环境变量
      env_production: {
        NODE_ENV: "production",
        PORT: 8080,
      },

      // 日志配置
      log_file: "/var/log/app/combined.log",
      out_file: "/var/log/app/out.log",
      error_file: "/var/log/app/error.log",
      log_date_format: "YYYY-MM-DD HH:mm:ss Z",
      merge_logs: true,

      // 重启策略
      max_restarts: 10,
      min_uptime: "30s",
      max_memory_restart: "1G",
      restart_delay: 5000,
      exp_backoff_restart_delay: 100,

      // 性能优化
      node_args: "--max_old_space_size=1024",

      // 健康检查
      wait_ready: true,
      listen_timeout: 10000,
      kill_timeout: 5000,

      // 禁用开发功能
      watch: false,
      autorestart: true,
      pmx: false,
    },
  ],
};

3. 监控最佳实践

设置监控和警报

bash
# 安装监控插件
pm2 install pm2-server-monit

# 配置监控
pm2 set pm2-server-monit:monitoring true
pm2 set pm2-server-monit:threshold_memory 85
pm2 set pm2-server-monit:threshold_cpu 90

# 安装日志轮转
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 100M
pm2 set pm2-logrotate:retain 30
pm2 set pm2-logrotate:compress true

自定义监控脚本

bash
#!/bin/bash
# health-check.sh - 自定义健康检查脚本

APP_NAME="my-app"
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

# 检查应用状态
STATUS=$(pm2 jlist | jq -r ".[] | select(.name==\"$APP_NAME\") | .pm2_env.status")

if [ "$STATUS" != "online" ]; then
    echo "Application $APP_NAME is $STATUS"

    # 发送警报
    curl -X POST -H 'Content-type: application/json' \
        --data "{\"text\":\"🚨 Application $APP_NAME is $STATUS\"}" \
        $WEBHOOK_URL

    # 尝试重启
    pm2 restart $APP_NAME
fi

4. 部署最佳实践

CI/CD 集成示例

yaml
# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          node-version: "16"

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Build application
        run: npm run build

      - name: Deploy to server
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd /var/www/production
            git pull origin main
            npm ci --production
            npm run build
            pm2 reload ecosystem.config.js --env production

蓝绿部署脚本

bash
#!/bin/bash
# blue-green-deploy.sh

APP_NAME="my-app"
CURRENT_ENV=$(pm2 jlist | jq -r ".[] | select(.name==\"$APP_NAME\") | .pm2_env.NODE_ENV")

if [ "$CURRENT_ENV" == "blue" ]; then
    NEW_ENV="green"
else
    NEW_ENV="blue"
fi

echo "Deploying to $NEW_ENV environment"

# 启动新环境
pm2 start ecosystem.config.js --env $NEW_ENV

# 等待新环境就绪
sleep 10

# 健康检查
HEALTH_CHECK=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/health)

if [ "$HEALTH_CHECK" == "200" ]; then
    echo "Health check passed, switching traffic"

    # 更新负载均衡器配置
    # nginx -s reload

    # 停止旧环境
    pm2 stop $APP_NAME-$CURRENT_ENV

    echo "Deployment successful"
else
    echo "Health check failed, rolling back"
    pm2 stop $APP_NAME-$NEW_ENV
    exit 1
fi

故障排除

常见问题和解决方案

常见问题

问题 1:应用频繁重启

bash
# 检查重启原因
pm2 describe app-name

# 查看错误日志
pm2 logs app-name --err

# 可能的解决方案
# 1. 增加内存限制
pm2 restart app-name --max-memory-restart 2G

# 2. 增加最小运行时间
# 在 ecosystem.config.js 中设置 min_uptime: '60s'

问题 2:零停机重载失败

bash
# 检查应用是否正确处理 SIGINT 信号
# 确保应用实现了优雅关闭

# 增加超时时间
pm2 reload app-name --kill-timeout 10000

问题 3:集群模式下 Socket.IO 问题

javascript
// 使用 Redis 适配器解决
const io = require("socket.io")(server, {
  adapter: require("socket.io-redis")({
    host: "localhost",
    port: 6379,
  }),
});

调试命令

bash
# 检查 PM2 守护进程状态
pm2 ping

# 查看 PM2 版本和环境信息
pm2 report

# 重置 PM2 守护进程
pm2 kill
pm2 resurrect

# 更新 PM2
pm2 update

# 查看详细错误信息
pm2 logs --error --lines 100

总结

PM2 是一个功能强大的 Node.js 进程管理器,通过本指南您应该能够:

掌握基本操作:启动、停止、重启应用
配置集群模式:充分利用多核 CPU 资源
实现零停机部署:保证服务的高可用性
设置监控和日志:及时发现和解决问题
优化生产环境:确保应用的稳定运行

记住,在生产环境中使用 PM2 时,始终要考虑应用的优雅关闭、健康检查、监控告警等因素,这样才能构建一个真正可靠的应用部署方案。


最后更新时间:2025 年 7 月 22 日