PM2是Node.js应用最常用的进程管理工具能实现进程守护自动重启集群负载均衡日志管理监控等等功能非常强大。

很多人都用过PM2但是大部分人只会基本的启动停止重启对于一些进阶的功能和,最佳实践可能不太了解。

我在工作中大量使用PM2管理Node.js应用踩了很多坑也积累了一些经验。

今天想总结一下PM2进程管理的最佳实践包括配置集群日志监控部署等等帮大家更好地使用PM2管理Node.js应用。

一、PM2简介

在讲最佳实践之前,先简单介绍一下PM2。

PM2是一个Node.js的生产环境进程管理器由Keymetrics公司开发开源免费功能强大是,Node.js应用部署的事实标准。

PM2的主要功能:

  1. 进程守护:应用崩溃或退出后自动重启保证应用持续运行。
  2. 集群模式:支持多进程集群自动负载均衡充分利用多核CPU。
  3. 日志管理:自动管理应用日志支持日志切割合并查看等等。
  4. 监控:实时监控应用的CPU内存状态等等。
  5. 部署:支持一键部署从Git仓库拉取代码部署应用。
  6. 配置文件:支持用配置文件管理应用配置方便版本控制和复用。
  7. 模块化:支持插件扩展功能。

PM2的使用很简单安装后用命令行就能管理应用也支持配置文件更灵活。

二、安装和基本使用

安装

npm install -g pm2

全局安装PM2安装完成后就能用pm2命令。

基本命令

# 启动应用
pm2 start app.js

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

# 查看应用列表
pm2 list

# 查看应用详情
pm2 show my-app

# 查看日志
pm2 logs my-app

# 重启应用
pm2 restart my-app

# 停止应用
pm2 stop my-app

# 删除应用
pm2 delete my-app

# 监控应用
pm2 monit

# 保存进程列表开机自启
pm2 save
pm2 startup

这些是基本的命令大部分人都会,但是要用好PM2还需要了解更多进阶的功能和最佳实践。

三、使用配置文件

最佳实践第一条就是用配置文件管理应用不要每次都用命令行参数启动应用。

配置文件的好处:

  1. 可版本控制:配置文件能提交到Git版本控制方便追踪变更和回滚。
  2. 可复用:配置文件能在不同环境复用只需要修改少量参数。
  3. 更清晰:所有配置都在一个文件里一目了然比命令行参数更清晰易读。
  4. 支持复杂配置:配置文件支持更复杂的配置,比如多个应用环境变量等等命令行参数不方便表达。

配置文件格式

PM2支持多种格式的配置文件,比如JSONYAMLJS等等推荐用JS格式,因为更灵活能写逻辑和注释。

创建ecosystem.config.js

module.exports = {
  apps: [
    {
      name: 'my-app',
      script: './app.js',
      cwd: '/var/www/my-app',
      instances: 'max',
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'production',
        PORT: 3000
      },
      env_staging: {
        NODE_ENV: 'staging',
        PORT: 3001
      },
      max_memory_restart: '500M',
      error_file: './logs/error.log',
      out_file: './logs/out.log',
      log_date_format: 'YYYY-MM-DD HH:mm:ss',
      merge_logs: true,
      autorestart: true,
      watch: false,
      ignore_watch: ['node_modules', 'logs'],
      max_restarts: 10,
      restart_delay: 3000
    }
  ]
};

用配置文件启动:

pm2 start ecosystem.config.js

指定环境:

pm2 start ecosystem.config.js --env staging

配置项说明

  • name:应用名称用于标识应用。
  • script:启动脚本路径。
  • cwd:工作目录。
  • instances:集群实例数'max'表示用所有CPU核心。
  • exec_mode:执行模式'cluster'集群模式'fork'单进程模式。
  • env:环境变量默认环境。
  • env_xxx:指定环境的环境变量。
  • maxmemoryrestart:内存超过指定值自动重启防止内存泄漏。
  • error_file:错误日志文件路径。
  • out_file:输出日志文件路径。
  • logdateformat:日志日期格式。
  • merge_logs:集群模式下是否合并日志。
  • autorestart:是否自动重启。
  • watch:是否监听文件变化自动重启开发环境用生产环境建议关闭。
  • ignore_watch:监听时忽略的文件/目录。
  • max_restarts:最大重启次数超过后不再重启防止应用崩溃后无限重启。
  • restart_delay:重启延迟毫秒防止频繁重启。

四、集群模式

最佳实践第二条就是用集群模式充分利用多核CPU提高应用性能和稳定性。

Node.js是单线程的一个进程只能用一个CPU核心现在的服务器都是多核的不用集群就浪费了其他核心。

PM2的集群模式能启动多个进程自动负载均衡把请求分发到不同的进程充分利用多核CPU也能提高应用的稳定性一个进程崩溃其他进程还能继续服务。

配置集群模式

{
  name: 'my-app',
  script: './app.js',
  instances: 'max', // 用所有CPU核心
  exec_mode: 'cluster' // 集群模式
}

instances: 'max'表示启动和CPU核心数相同的进程也能指定具体的数字,比如instances: 4启动4个进程。

集群模式的注意事项

  1. 不要用instances: 0:instances不能为0否则PM2会报错至少为1。
  2. 状态共享问题:集群模式下多个进程是独立的内存不共享,如果应用用内存存状态,比如session缓存等等多个进程的状态不一致会出问题。建议用Redis等外部存储共享状态。
  3. 日志合并:集群模式下多个进程的日志默认是分开的要设置merge_logs: true合并日志方便查看。
  4. 端口冲突:集群模式下多个进程监听同一个端口不会冲突PM2会自动处理负载均衡,但是要确保应用支持集群模式,比如ExpressKoa等等都支持。
  5. 重启策略:集群模式下重启应用建议用pm2 reload而不是pm2 restartreload是,逐个重启进程不会中断服务零停机重启restart是全部重启会短暂中断服务。

零停机重启

pm2 reload my-app

reload会逐个重启集群中的进程每个进程重启前会等待当前请求处理完再重启,所以不会中断服务是生产环境推荐的重启方式。

五、日志管理

最佳实践第三条就是做好日志管理方便排查问题和监控。

PM2自动管理应用的日志,但是默认配置可能不适合生产环境需要调整。

日志配置

{
  error_file: './logs/error.log',
  out_file: './logs/out.log',
  log_date_format: 'YYYY-MM-DD HH:mm:ss',
  merge_logs: true,
  time: true
}
  • error_file:错误日志文件stderr的输出。
  • out_file:输出日志文件stdout的输出。
  • logdateformat:日志前缀的日期格式。
  • merge_logs:集群模式下合并日志。
  • time:是否在日志前加时间戳。

日志切割

默认PM2的日志文件会越来越大不会自动切割需要用pm2-logrotate插件实现日志切割。

安装插件:

pm2 install pm2-logrotate

配置插件:

pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 30
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:rotateInterval '0 0 * * *'
  • max_size:单个日志文件最大大小超过就切割。
  • retain:保留的日志文件数量超过就删除旧的。
  • compress:是否压缩旧的日志文件节省空间。
  • rotateInterval:切割间隔cron表达式上面是每天0点切割。

日志查看

# 查看所有日志
pm2 logs

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

# 查看最后100行
pm2 logs my-app --lines 100

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

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

# 不实时刷新只看历史
pm2 logs my-app --nostream

日志最佳实践

  1. 应用里用日志库:应用里建议用专业的日志库,比如winstonpinobunyan等等不要直接用console.log能更好地控制日志级别格式输出等等。
  2. 区分日志级别:区分debuginfowarnerror等等级别生产环境只输出info及以上的日志避免日志太多。
  3. 日志里加关键信息:日志里要加请求ID用户ID时间等等关键信息方便排查问题追踪请求。
  4. 不要输出敏感信息:日志里不要输出密码token身份证号等等敏感信息防止泄露。
  5. 定期清理日志:用logrotate定期切割和,清理日志防止日志文件太大占满磁盘。

六、监控和告警

最佳实践第四条就是做好监控和告警及时发现应用的问题。

PM2自带基础的监控功能能查看应用的CPU内存状态等等,但是功能比较基础生产环境建议配合专业的监控工具。

PM2自带监控

# 实时监控
pm2 monit

# 查看应用详情
pm2 show my-app

# 查看进程列表
pm2 list

pm2 monit能实时查看所有应用的CPU内存日志等等很直观,但是只能在终端看不能历史查询和告警。

Keymetrics监控

PM2的开发公司Keymetrics提供了在线监控服务能把PM2的监控数据上传到云端在线查看历史数据设置告警等等功能很强大,但是是付费服务免费版有限制。

注册Keymetrics后在服务器上登录:

pm2 link <secret_key> <public_key>

就能把监控数据上传到Keymetrics在线查看。

第三方监控

也能用第三方监控工具,比如Prometheus + GrafanaZabbixDatadog等等监控服务器和应用的状态设置告警。

PM2能导出监控数据到Prometheus用pm2-prometheus-exporter插件,或者自己写脚本调用PM2的API获取监控数据导出。

告警

监控的目的是及时发现问题,所以告警很重要。

常见的告警指标:

  1. 应用状态:应用崩溃停止重启次数过多。
  2. CPU使用率:CPU持续过高超过80%。
  3. 内存使用率:内存持续过高超过80%或持续增长可能内存泄漏。
  4. 响应时间:应用响应时间过长超过阈值。
  5. 错误率:应用错误率过高超过阈值。
  6. 磁盘使用率:磁盘使用率过高超过80%特别是日志目录。

告警方式邮件钉钉企业微信Slack短信电话等等根据重要性选择不同的告警方式。

七、内存泄漏处理

Node.js应用常见的问题就是内存泄漏PM2能帮助发现和处理内存泄漏。

发现内存泄漏

pm2 monitpm2 show查看应用的内存使用情况,如果内存持续增长不下降可能就是内存泄漏。

也能用maxmemoryrestart配置内存超过阈值自动重启临时缓解内存泄漏的问题,但是这只是治标不治本还是要找到泄漏的原因修复。

配置自动重启

{
  max_memory_restart: '500M'
}

内存超过500M自动重启应用防止内存用满导致服务器OOM。

排查内存泄漏

  1. 用node --inspect调试:启动应用时加--inspect参数用Chrome DevTools调试查看内存快照分析泄漏的对象。
  2. 用heapdump:用heapdump模块在,应用里生成内存快照用Chrome DevTools分析。
  3. 用clinic.js:用clinic.js工具分析应用的性能和,内存问题很方便。
  4. 逐步排查:注释代码逐步缩小范围找到泄漏的代码。

常见的内存泄漏原因:

  1. 全局变量:意外创建的全局变量不会被回收。
  2. 闭包:闭包引用的变量不会被回收。
  3. 事件监听:没有移除的事件监听引用的对象不会被回收。
  4. 定时器:没有清除的setIntervalsetTimeout引用的对象不会被回收。
  5. 缓存:无限增长的缓存没有淘汰机制内存持续增长。
  6. :没有正确关闭的流可能泄漏。

八、部署最佳实践

最佳实践第五条就是做好部署流程保证部署的稳定和可靠。

PM2部署功能

PM2自带部署功能能从Git仓库拉取代码部署应用。

配置ecosystem.config.js

module.exports = {
  apps: [
    {
      name: 'my-app',
      script: './app.js',
      instances: 'max',
      exec_mode: 'cluster'
    }
  ],
  deploy: {
    production: {
      user: 'deploy',
      host: '192.168.1.100',
      ref: 'origin/master',
      repo: 'git@github.com:user/my-app.git',
      path: '/var/www/my-app',
      'post-deploy': 'npm install && pm2 reload ecosystem.config.js --env production'
    }
  }
};

部署:

# 首次部署
pm2 deploy production setup

# 部署
pm2 deploy production

# 回滚
pm2 deploy production revert 1

PM2的部署功能能实现一键部署和回滚很方便,但是功能比较基础复杂的部署流程建议用专业的CI/CD工具,比如JenkinsGitLab CIGitHub Actions等等。

部署最佳实践

  1. 零停机部署:用pm2 reload逐个重启进程零停机部署不要用pm2 restart
  2. 蓝绿部署:准备两套环境蓝和绿部署到备用环境验证通过后切换流量更安全。
  3. 灰度发布:先部署到部分服务器验证没问题再全量部署降低风险。
  4. 回滚机制:部署前备份当前版本部署出问题能快速回滚。
  5. 部署后验证:部署完成后要验证应用是否正常运行接口是否正常日志是否有错误。
  6. 健康检查:配置健康检查接口部署后自动检查应用健康状态。

九、开机自启

最佳实践第六条就是配置开机自启保证服务器重启后应用自动启动。

配置开机自启

# 生成开机自启脚本
pm2 startup

# 保存当前进程列表
pm2 save

pm2 startup会生成系统的开机自启脚本,比如systemd服务执行输出的命令就能配置开机自启。

pm2 save会把当前的进程列表保存到~/.pm2/dump.pm2开机的时候,候PM2会自动恢复这些进程。

注意事项

  1. 修改进程后要重新save:每次修改了进程列表,比如新增删除应用都要执行pm2 save保存,否则重启后修改会丢失。
  2. 用root配置:配置开机自启建议用root用户,或者有sudo权限的用户,否则可能权限不够。
  3. 指定用户:如果应用需要用特定用户运行要在startup命令里指定用户,比如pm2 startup systemd -u deploy --hp /home/deploy

十、常见的坑和注意事项

在使用PM2的过程中我踩了很多坑总结一下常见的坑和注意事项。

坑1:集群模式下session丢失

集群模式下多个进程内存不共享,如果用内存存session用户的请求被分发到不同的进程session就会丢失用户需要重新登录。

解决方法:用Redis等外部存储存session不要用内存存。

坑2:watch模式导致频繁重启

开发环境用watch模式监听文件变化自动重启,但是,如果配置不好,比如没有忽略node_moduleslogs等目录文件变化会导致频繁重启影响开发。

解决方法:配置ignore_watch忽略不需要监听的目录和,文件生产环境关闭watch。

坑3:日志文件太大占满磁盘

没有配置日志切割日志文件会越来越大最终占满磁盘导致服务器异常。

解决方法:安装pm2-logrotate配置日志切割和保留数量定期清理旧日志。

坑4:应用崩溃后无限重启

应用有bug启动就崩溃PM2会自动重启,但是应用还是崩溃又重启无限循环占用大量CPU和磁盘IO。

解决方法:配置maxrestartsrestartdelay限制重启次数和,间隔超过次数后不再重启防止无限重启。

坑5:环境变量不生效

修改了配置文件的环境变量,但是重启应用后环境变量不生效。

解决方法:PM2的环境变量是启动时加载的修改配置文件后要用pm2 restart --update-env或者,先delete再start才能更新环境变量普通的restart不会更新环境变量。

pm2 restart ecosystem.config.js --update-env

坑6:PM2版本太旧

PM2版本太旧有bug或者不支持新功能影响使用。

解决方法:定期更新PM2到最新稳定版npm install -g pm2@latest更新后要pm2 update更新守护进程。

十一、写在最后

以上就是PM2进程管理的最佳实践和经验总结。

PM2是一个非常优秀的Node.js进程管理工具功能强大使用简单是,Node.js应用生产环境部署的好帮手。

但是要用好PM2也需要了解它的进阶功能和最佳实践做好配置集群日志监控部署等等才能保证应用的稳定和可靠。

希望我的经验能帮大家更好地使用PM2管理Node.js应用少踩坑多办事。

如果有什么问题,或者更好的经验欢迎在评论区留言我们一起交流。

最后用一句话结束这篇文章:"PM2是Node.js生产环境的好帮手用好它能让你的应用稳定运行。"

愿大家的Node.js应用都能稳定运行少出问题用户满意。