功能

应用程序的转换状态 (重要)

PM2是进程管理器. PM2可以启动停止重启和删除进程.

启动进程:

$ pm2 start app.js --name "my-api"
$ pm2 start web.js --name "web-interface"

现在需要停止web-interface:

$ pm2 stop web-interface

你可以看到 the process hasn't disappeared. 仍然在那但是 stopped 状态.

重启它:

$ pm2 restart web-interface

现在我要从PM2进程列表 删除 这个应用:

$ pm2 delete web-interface

进程列表

Monit

列出所有运行进程:

$ pm2 list
# Or
$ pm2 [list|ls|l|status]

列出指定进程的详细信息:

$ pm2 describe 0

Automatic restart process based on memory

Value passed is in megaoctets. Internally it uses the V8 flag --max-old-space-size=MEM to make a process exit when memory exceed a certain amount of RAM used.

CLI:

$ pm2 start big-array.js --max-memory-restart 20M

JSON:

{
  "name" : "max_mem",
  "script" : "big-array.js",
  "max_memory_restart" : "20M"
}

Units can be K(ilobyte), M(egabyte), G(igabyte).

监控CPU/内存使用率

Monit

监控启动的所有进程:

$ pm2 monit

日志管理

启用pm2.log的时间戳前缀

export PM2_LOG_DATE_FORMAT="YYYY-MM-DD HH:mm Z"

If this env-variable has been changed, you need to dump your processes and kill daemon, restart it again to take effect, e.g.:

$ pm2 dump
$ pm2 [resurrect|save]

实时显示日志

Monit

Displaying logs of specified process or all processes in realtime:

$ pm2 logs
$ pm2 logs big-api
$ pm2 flush # Clear all the logs

高级日志接口

Navigate between processes logs in realtime with an ergonomic interface:

$ pm2 ilogs

重载所有日志 (SIGUSR2/Logrotate)

To reload all logs, you can send SIGUSR2 to the PM2 process.

You can also reload all logs via the command line with:

$ pm2 reloadLogs

选项

--merge-logs : merge logs from different instances but keep error and out separated
--log-date-format <format>: prefix logs with formated timestamp (http://momentjs.com/docs/#/parsing/string-format/)

合并 outerr 日志

  • If you only want to merge out and err logs into one output file, try with the following examples:

    $ pm2 start name.js -o name.log -e name.log
    
    {
      "apps" : [{
        "name"        : "name",
        "script"      : "name.js",
        "err_file"    : "name.log",
        "out_file"    : "name.log"
      }]
    }
    
  • How about merge out and err logs into one, and also keep the separated logs? e.g.:

    $ pm2 start -l
    
    $ pm2 start -l name.log
    
    {
      "apps" : [{
        "name"        : "name",
        "script"      : "name.js",
        "log_file"    : true
      }]
    }
    
    {
      "apps" : [{
        "name"        : "name",
        "script"      : "name.js",
        "log_file"    : "name.log"
      }]
    }
    

    Notes: When you providing a Boolean (true) value for -l, --log option or log_file property, it means a merged log file will be automatic generated, i.e. ~/.pm2/logs/[name]-[id].log, otherwise it's specific.

集群 (cluster_mode)

The cluster_mode will automatically wrap your Node.js app into the cluster module and will enable you to reload your app without downtime and to scale your processes across all CPUs available.

To enable the cluster_mode, just pass the -i option:

$ pm2 start app.js -i 1

To launch max instances (max depending on the number of CPUs available) and set the load balancer to balance queries among process:

$ pm2 start app.js --name "API" -i 0

DEPRECATED (STILL COMPATIBLE):

$ pm2 start app.js --name "API" -i max

If your app is well-designed (stateless) you'll be able to process many more queries.

Important concepts to make a Node.js app stateless:

热重载

As opposed to restart, which kills and restarts the process, reload achieves a 0-second-downtime reload.

Warning This feature only works for apps in cluster_mode, that uses HTTP/HTTPS/Socket connections.

重载应用:

$ pm2 reload api

If the reload system hasn't managed to reload your app, a timeout will simply kill the process and will restart it.

优雅重载

Sometimes you can experience a very long reload, or a reload that doesn't work (fallback to restart).

It means that your app still has open connections on exit.

To work around this problem you have to use the graceful reload. Graceful reload is a mechanism that will send a shutdown message to your process before reloading it. You can control the time that the app has to shutdown via the PM2_GRACEFUL_TIMEOUT environment variable.

Example:

process.on('message', function(msg) {
  if (msg == 'shutdown') {
    // Your process is going to be reloaded
    // You have to close all database/socket.io/* connections

    console.log('Closing all connections...');

    // You will have 4000ms to close all connections before
    // the reload mechanism will try to do its job

    setTimeout(function() {
      console.log('Finished closing connections');
      // This timeout means that all connections have been closed
      // Now we can exit to let the reload mechanism do its job
      process.exit(0);
    }, 1500);
  }
});

Then use the command:

$ pm2 gracefulReload [all|name]

When PM2 starts a new process to replace an old one, it will wait for the new process to begin listening to a connection or a timeout before sending the shutdown message to the old one. You can define the timeout value with the PM2_GRACEFUL_LISTEN_TIMEOUT environamente variable. I f a script does not need to listen to a connection, it can manually tell PM2 that the process has started up by calling process.send('online').

启动脚本

PM2 has the amazing ability to generate startup scripts and configure them. PM2 is also smart enough to save all your process list and to bring back all your processes on restart.

$ pm2 startup [ubuntu|centos|gentoo|systemd]

Once you have started the apps and want to keep them on server reboot do:

$ pm2 save

Warning It's tricky to make this feature work generically, so once PM2 has setup your startup script, reboot your server to make sure that PM2 has launched your apps!

更多信息

Three types of startup scripts are available:

  • SystemV init script (with the option ubuntu or centos)
  • OpenRC init script (with the option gentoo)
  • SystemD init script (with the systemd option)

The startup options are using:

  • ubuntu will use updaterc.d and the script lib/scripts/pm2-init.sh
  • centos will use chkconfig and the script lib/scripts/pm2-init-centos.sh
  • gentoo will use rc-update and the script lib/scripts/pm2
  • systemd will use systemctl and the script lib/scripts/pm2.service

用户权限

Let's say you want the startup script to be executed under another user.

Just use the -u <username> option !

$ pm2 startup ubuntu -u www

相关命令

Dump all processes status and environment managed by PM2:

$ pm2 dump

It populates the file ~/.pm2/dump.pm2 by default.

To bring back the latest dump:

$ pm2 [resurrect|save]

监控 & 重启

PM2 can automatically restart your app when a file changes in the current directory or its subdirectories:

$ pm2 start app.js --watch

If --watch is enabled, stopping it won't stop watching:

  • pm2 stop 0 'll not stop watching
  • pm2 stop --watch 0 'll stop watching

Restart toggle the watch parameter when triggered.

To watch specific paths, please use a JSON app declaration, watch can take a string or an array of paths. Default is true:

{
  "watch": ["server", "client"],
  "ignoreWatch" : ["node_modules", "client/img"]
}

JSON app 声明

You can define parameters for your apps in processes.json:

{
  "apps" : [{
    "name"        : "echo",
    "script"      : "examples/args.js",
    "args"        : "['--toto=heya coco', '-d', '1']",
    "log_date_format"  : "YYYY-MM-DD HH:mm Z",
    "ignoreWatch" : ["[\\/\\\\]\\./", "node_modules"],
    "watch"       : true,
    "node_args"   : "--harmony",
    "cwd"         : "/this/is/a/path/to/start/script",
    "env": {
        "NODE_ENV": "production",
        "AWESOME_SERVICE_API_TOKEN": "xxx"
    }
  },{
    "name"       : "api",
    "script"     : "./examples/child.js",
    "instances"  : "4",
    "log_date_format"  : "YYYY-MM-DD",
    "log_file"   : "./examples/child.log",
    "error_file" : "./examples/child-err.log",
    "out_file"   : "./examples/child-out.log",
    "pid_file"   : "./examples/child.pid",
    "exec_mode"  : "cluster_mode",
    "port"       : 9005
  },{
    "name"       : "auto-kill",
    "script"     : "./examples/killfast.js",
    "min_uptime" : "100",
    "exec_mode"  : "fork_mode"
  }]
}

Then run:

$ pm2 start processes.json
$ pm2 stop processes.json
$ pm2 delete processes.json
$ pm2 restart processes.json

A few notes about JSON app declarations:

  • All command line options passed when using the JSON app declaration will be dropped i.e.
$ cat node-app-1.json

{
  "name" : "node-app-1",
  "script" : "app.js",
  "cwd" : "/srv/node-app-1/current"
}
  • JSON app declarations are additive. Continuing from above:

    $ pm2 start node-app-2.json
    $ ps aux | grep node-app
    root  14735  5.8  1.1  752476  83932 ? Sl 00:08 0:00 pm2: node-app-1
    root  24271  0.0  0.3  696428  24208 ? Sl 17:36 0:00 pm2: node-app-2
    

    Note that if you execute pm2 start node-app-2 again, it will spawn an additional instance node-app-2.

  • cwd: your JSON declaration does not need to reside with your script. If you wish to maintain the JSON(s) in a location other than your script (say, /etc/pm2/conf.d/node-app.json) you will need to use the cwd feature. (Note, this is especially helpful for capistrano style directory structures that use symlinks.) Files can be either relative to the cwd directory, or absolute (example below.)

  • The following are valid options for JSON app declarations:

    [{
    "name"             : "node-app",
    "cwd"              : "/srv/node-app/current",
    "args"             : "['--toto=heya coco', '-d', '1']",
    "script"           : "bin/app.js",
    "node_args"        : ["--harmony", " --max-stack-size=102400000"],
    "log_date_format"  : "YYYY-MM-DD HH:mm Z",
    "error_file"       : "/var/log/node-app/node-app.stderr.log",
    "out_file"         : "log/node-app.stdout.log",
    "pid_file"         : "pids/node-geo-api.pid",
    "instances"        : 6, //or 0 => 'max'
    "min_uptime"       : "200s", // 200 seconds, defaults to 1000
    "max_restarts"     : 10, // defaults to 15
    "max_memory_restart": "1M", // 1 megabytes, e.g.: "2G", "10M", "100K", 1024... the default unit is byte.
    "cron_restart"     : "1 0 * * *",
    "watch"            : false,
    "ignoreWatch"      : ["[\\/\\\\]\\./", "node_modules"],
    "merge_logs"       : true,
    "exec_interpreter" : "node",
    "exec_mode"        : "fork",
    "env": {
      "NODE_ENV": "production",
      "AWESOME_SERVICE_API_TOKEN": "xxx"
    }
    }]