Robin的主页

pm2 是一个可用于生产环境的 Node.js 的进程管理器,较为广泛地被使用在各种 node 服务器项目中。

PM2是一款非常优秀的Node进程管理工具,能够充分利用多核CPU且能够负载均衡、能够帮助应用在崩溃后自动重启、能够监控资源的使用情况并且支持API方式查看、并且有配套的Keymetrics可以用于服务的监控。 from pm25

类似地,国内也有着 pm25 这样优秀的开源 Node.js 服务监控报警系统。

本文将基于个人的实际的 pm2 开发运维经验写几个较为关键的 pm2 运维注意点。

注意点:

注意点一

保证你的 pm2 启动的目录不能在停止 pm2 前被删除,否则将会导致往后的 pm2 因为找不到执行路径产生 ERROR,停止运行。

如何查看 pm2 应用的执行路径:执行 ps ax | grep pm2,在执行结果中寻在 pm2 进程 PID,执行 ls -l /proc/PM2_PID/cwd,可以查看到该进程的执行路径。

注意点二

执行大部分的 pm2 指令将会初始化 pm2,pm2pm2 kill 除外,在非目标的路径下执行 pm2 指令时千万要注意,pm2 的 God Daemon 可能已经被启动。如:PM2 Successfully daemonized

➜  _drafts git:(master) ✗ pm2 --help
[PM2] Spawning PM2 daemon with pm2_home=/Users/xianyu/.pm2
[PM2] PM2 Successfully daemonized

注意点三

使用 pm2 的 ecosystem.json 配置来启动你的 pm2 程序,简单的配置让启动过程更加清晰,从而提高你的工作效率。

在项目根路径执行 ` pm2 ecosystem,会生成一个 ecosystem.json` 文件。

例如:

module.exports = {
  /**
   * Application configuration section
   * http://pm2.keymetrics.io/docs/usage/application-declaration/
   */
  apps : [

    // First application
    {
      name      : "API",
      script    : "app.js",
      cwd       : "/opt/someProject/server",
      env: {
        COMMON_VARIABLE: "true"
      },
      env_production : {
        NODE_ENV: "production"
      }
    },

    // Second application
    {
      name      : "WEB",
      script    : "web.js"
    }
  ],

  /**
   * Deployment section
   * http://pm2.keymetrics.io/docs/usage/deployment/
   */
  deploy : {
    production : {
      user : "node",
      host : "212.83.163.1",
      ref  : "origin/master",
      repo : "git@github.com:repo.git",
      path : "/var/www/production",
      "post-deploy" : "npm install && pm2 startOrRestart ecosystem.json --env production"
    },
    dev : {
      user : "node",
      host : "212.83.163.1",
      ref  : "origin/master",
      repo : "git@github.com:repo.git",
      path : "/var/www/development",
      "post-deploy" : "npm install && pm2 startOrRestart ecosystem.json --env dev",
      env  : {
        NODE_ENV: "dev"
      }
    }
  }
}

apps 字段中的每一个配置都对应一个 pm2 app,你可以使用 pm2 startOrRestart ecosystem.json 来启动所有应用,使用 pm2 startOrRestart ecosystem.json --only API 只启动 API 这个应用。

注意到,我在 apps 字段中增加了一个配置 cwd: "/opt/someProject/server",这个配置对应了该应用的代码执行路径,换而言之,当我们在程序中执行 process.cwd() 时,返回的就是这个 /opt/someProject/server 路径。因此,我们可以把 /opt 路径当作 pm2 的启动路径,从而管理多个项目服务进程。

在 pm2 的文档中也有类似的文档:Capistrano like deployments

注意点四

如何在不影响当前 “app”(pm2 启动的程序叫做 app)的情况下重新启动 app?

使用 pm2 startOrRestart 或者 pm2 startOrGracefulReload 来在每次源代码发布之后重新启动你的 pm2 apps。

注意点五

完全关闭 pm2 的方法是 pm2 kill,使用 pm2 delete someApp 只能关闭所有的 pm2 应用,而不能关闭 pm2。

使用 ps ax | grep pm2ps ax | grep PM2ps ax | grep God Daemon 来确定是否完全关闭了 pm2。

注意点六

pm2 gracefulReload ecosystem.config.js --env production doesn’t pick up new settings if I have changed something in the ecosystem.config.js

在更改了ecosystem.config.js配置之后,执行pm2 gracefulReload ecosystem.config.js并不会更新新配置,需要先执行pm2 kill


常见的错误与调试解决方案:

排查常见错误一般先执行 pm2 log 或者 pm2 logs 查看 pm2 日志,从日志中看看具体的情况再做后续处理。

  1. pm2 不断重启应用,最后以 error 告终。

错误日志:

 
path.js:1144
 
         cwd = process.cwd();
 
                       ^
 
Error: ENOENT: no such file or directory, uv_cwd
 
   at Error (native)
 
   at Object.resolve (path.js:1144:25)
 
   at Function.Module._resolveLookupPaths (module.js:361:17)
 
   at Function.Module._resolveFilename (module.js:431:31)
 
   at Function.Module._load (module.js:388:25)
 
   at Module.require (module.js:468:17)
 
   at require (internal/module.js:20:19)
 
   at Object.<anonymous> (/home/sankuai/.nvm/versions/node/v6.2.1/lib/node_modules/pm2/bin/pm2:7:17)
 
   at Module._compile (module.js:541:32)
 
   at Object.Module._extensions..js (module.js:550:10)
 
[sankuai@dx-fd-fe-edw-staging01 fe-edw-tools]$ pwd
 
/opt/meituan/fd-fe/fe-edw-tools
 
[sankuai@dx-fd-fe-edw-staging01 fe-edw-tools]$ node
 
> process.cwd()
 
Error: ENOENT: no such file or directory, uv_cwd
 
   at Error (native)
 
   at repl:1:9
 
   at REPLServer.defaultEval (repl.js:272:27)
 
   at bound (domain.js:280:14)
 
   at REPLServer.runBound [as eval] (domain.js:293:12)
 
   at REPLServer.<anonymous> (repl.js:441:10)
 
   at emitOne (events.js:101:20)
 
   at REPLServer.emit (events.js:188:7)
 
   at REPLServer.Interface._onLine (readline.js:224:10)
 
   at REPLServer.Interface._line (readline.js:566:8)
 
>

这种错误一般出现在 pm2 的执行路径被删除的情况下。也就是说,删除了一个目录,cwd = process.cwd(); 这句代码又在这个目录执行命令产生的错误。

如何调试:

执行 ps -ef | grep pm2,可以看到:

[sankuai@dx-fd-fe-edw-staging01 fe-edw-tools]$ ps -ef | grep pm2
 
sankuai   2032     1  0 19:28 ?        00:00:00 node /home/sankuai/.nvm/versions/node/v6.2.1/bin/pm2 startOrGracefulReload bin/ecosystem.json --only fd-fe-edw-tools --no-daemon
 
sankuai   2462 32322  0 19:30 pts/1    00:00:00 grep pm2
 
sankuai  18526     1  0 19:05 ?        00:00:03 PM2 v2.1.5: God Daemon (/home/sankuai/.pm2)

接着执行 ls -l /proc/PM2_PID/cwd 命令,会看到:

[sankuai@target machine fd-fe]$ ls -l /proc/14926/cwd
lrwxrwxrwx 1 sankuai sankuai 0 Nov 25 21:56 /proc/14926/cwd -> /opt/.plus_workspace/plus_deploy_backup/ef37e5d5-f3c9-4114-93c2-6038304cf136-10.32.198.142/target/fe-edw-tools (deleted)

在最末尾我们看到这个执行路径已经被删除了 (deleted),然后导致了错误。

解决方案:

不要在 停止 pm2 之前删除 pm2 启动的目录,因为 pm2 的执行路径会跟随这个文件夹移动。如果在部署或者其他情况下,在一个不会被删除的路径下启动 pm2。


Tonight Scitech Outlet, to buy some clothes!

December 24, 2016 at Wangjing CBD

- - - - - -
written by 陈烨彬 Robin Chen , and published under (CC) BY-NC-SA.