Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

PM2 - Node.js 프로세스 관리 도구

서버에서 웹 애플리케이션을 운영할 때 보통 데몬으로 서버를 띄워야 하고 Node.js의 경우 서버가 크래시나면 재시작을 하기 위해서 워치독(watchdog) 류의 프로세스 관리자를 사용하게 된다. 나 같은 경우 아주 초기에는 Upstart와 Monit을 사용했고 이후에는 Node.js 전용으로 나온 forever라는 도구를 사용했다. 지금은 프로세스 관리 도구로 PM2로 갈아탄 지 2년 가까이 되었는데 이에 대해 따로 글은 안 쓰고 있다가 지난달에 1.0이 나와서 새기능을 정리도 할 겸 글을 남긴다.

PM2 로고


PM2 기본 사용방법

pm2 명령어를 사용해야 하므로 npm을 이용해서 전역으로 설치한다.

$ npm install pm2 -g
$ pm2 version
1.0.1

Node.js 애플리케이션의 실행 파일이 app.js라고 했을 때 다음과 같이 pm2 start 명령어로 앱을 실행한다. pm2를 쓰지 않는다면 node app.js가 될 것이다.

$ pm2 start app.js
[PM2] Spawning PM2 daemon
[PM2] PM2 Successfully daemonized
[PM2] Starting app.js in fork_mode (1 instance)
[PM2] Done.
┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid  │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ app      │ 0  │ fork │ 4004 │ online │ 0       │ 0s     │ 14.492 MB   │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

앱의 이름을 지정하지 않으면 실행한 파일명이 된다. 명확한 이름을 지정하고 싶다면 --name 옵션을 사용한다.

$ pm2 start app.js --name "example"
[PM2] Spawning PM2 daemon
[PM2] PM2 Successfully daemonized
[PM2] Starting app.js in fork_mode (1 instance)
[PM2] Done.
┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ example  │ 0  │ fork │ 41402 │ online │ 0       │ 0s     │ 14.582 MB   │ disabled │
└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

forever를 쓴지 오래돼서 그사이에 얼마나 달라졌는지는 잘 모르지만 실행된 프로세스 목록을 forever보다 훨씬 깔끔한 표로 보여주어서 보기가 쉽다. 이 목록에서 현재 프로세스의 상태와 몇 번 재시작 되었는지 실행한 지 얼마나 되었는지를 파악할 수 있다. pm2로 애플리케이션을 실행하면 자동으로 애플리케이션을 데몬으로 실행하고 stdout/stderr로 출력된 메시지도 로그파일에 쌓아준다.

실행된 프로세스 목록을 보고 싶다면 pm2 list를 사용한다.

$ pm2 list
┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid   │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ example  │ 0  │ fork │ 41402 │ online │ 0       │ 71s    │ 34.754 MB   │ disabled │
└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app
 ```

 `pm2 show <id|name>` 명령어를 사용하면 해당 프로세스의 자세한 내용을 볼 수 있다. 이 정보에는 로그파일 위치와 실행한 옵션 등의 정보 등이 포함되어 있다. 특정 애플리케이션에 대해서 명령을 실행할 때는 App name이나 id 중 편한 것을 사용하면 된다.

 ```bash
 $ pm2 show example
Describing process with id 0 - name example
┌───────────────────┬───────────────────────────────────────────────┐
│ status            │ online                                        │
│ name              │ example                                       │
│ node.js version   │ 4.3.0                                         │
│ id                │ 0                                             │
│ path              │ /Users/outsider/temp/app.js                   │
│ args              │ N/A                                           │
│ exec cwd          │ /Users/outsider/temp                          │
│ error log path    │ /Users/outsider/.pm2/logs/example-error-0.log │
│ out log path      │ /Users/outsider/.pm2/logs/example-out-0.log   │
│ pid path          │ /Users/outsider/.pm2/pids/example-0.pid       │
│ mode              │ fork_mode                                     │
│ node v8 arguments │ N/A                                           │
│ watch & reload    │ ✘                                             │
│ interpreter       │ node                                          │
│ restarts          │ 0                                             │
│ unstable restarts │ 0                                             │
│ uptime            │ 3m                                            │
│ created at        │ 2016-02-21T13:41:59.371Z                      │
└───────────────────┴───────────────────────────────────────────────┘
Process configuration

Probes value
┌────────────┬────────┐
│ Loop delay │ 0.92ms │
└────────────┴────────┘

만약 해당 애플리케이션이 Git으로 관리되고 있다면(다른 형상관리도구로 테스트는 해보지 않았다.) pm2 show 명령어를 사용할 때 다음과 같이 리비전 정보가 함께 나온다. 이는 서버에서 현재 실행된 애플리케이션이 어떤 소스를 기반으로 실행되어 있는지 확인할 때 유용하다.

$ pm2 show example
Describing process with id 0 - name example
┌───────────────────┬───────────────────────────────────────────────┐
│ status            │ online                                        │
│ name              │ example                                       │
│ node.js version   │ 4.3.0                                         │
│ id                │ 0                                             │
│ path              │ /Users/outsider/temp/app.js                   │
│ args              │ N/A                                           │
│ exec cwd          │ /Users/outsider/temp                          │
│ error log path    │ /Users/outsider/.pm2/logs/example-error-0.log │
│ out log path      │ /Users/outsider/.pm2/logs/example-out-0.log   │
│ pid path          │ /Users/outsider/.pm2/pids/example-0.pid       │
│ mode              │ fork_mode                                     │
│ node v8 arguments │ N/A                                           │
│ watch & reload    │ ✘                                             │
│ interpreter       │ node                                          │
│ restarts          │ 0                                             │
│ unstable restarts │ 0                                             │
│ uptime            │ 3m                                            │
│ created at        │ 2016-02-21T13:41:59.371Z                      │ 
└───────────────────┴───────────────────────────────────────────────┘
Process configuration

Revision control metadata
┌──────────────────┬──────────────────────────────────────────┐
│ revision control │ git                                      │
│ remote url       │ git@github.com:outsideris/example.git    │
│ repository root  │ /Users/outsider/temp                     │
│ last update      │ 2016-02-22T11:37:34.000Z                 │
│ revision         │ cf10797c885556b0e9cb3349f54ba60c16c97a26 │
│ comment          │ fix something                            │
│ branch           │ master                                   │
└──────────────────┴──────────────────────────────────────────┘
Probes value
┌────────────┬────────┐
│ Loop delay │ 2.25ms │
└────────────┴────────┘

애플리케이션을 새로 배포하거나 해서 애플리케이션을 재시작 하려면 pm2 restart <id|name> 명령어를 사용한다.

$ pm2 restart example
[PM2] restartProcessId process id 0
┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid  │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ example  │ 0  │ fork │ 3255 │ online │ 1       │ 0s     │ 12.359 MB   │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use pm2 show <id|name> to get more details about an app

애플리케이션을 멈추려면 pm2 stop <id|name> 명령어를 사용한다.

$ pm2 stop example
[PM2] Stopping example
[PM2] stopProcessId process id 0
┌──────────┬────┬──────┬─────┬─────────┬─────────┬────────┬────────┬──────────┐
│ App name │ id │ mode │ pid │ status  │ restart │ uptime │ memory │ watching │
├──────────┼────┼──────┼─────┼─────────┼─────────┼────────┼────────┼──────────┤
│ example  │ 0  │ fork │ 0   │ stopped │ 1       │ 0      │ 0 B    │ disabled │
└──────────┴────┴──────┴─────┴─────────┴─────────┴────────┴────────┴──────────┘
Use pm2 show <id|nam> to get more details about an app

pm2에 많은 기능이 있지만 2년 정도 전에 pm2로 갈아탄 이유 중 하나가 멈춘 프로세스도 pm2가 관리해 준다는 것이다. forever의 경우 특별한 이유로 잠시 앱을 멈추려면 목록에서 사라지기 때문에 다시 실행하려고 할 때 기존의 옵션 등을 찾아야 했지만 pm2는 멈춘 프로세스도 관리하에 있으므로 필요할 때 바로 실행할 수 있고 운영하는 중에는 중단해 놓은 프로세스 목록을 확인하는 것도 중요하다.

프로세스 목록에서 제거하려면 pm2 delete <id|name>명령어를 사용하고 pm2 데몬 자체를 죽이려면 pm2 kill 명령어를 사용한다.

로그파일 확인

애플리케이션 서버를 실행하면 디버깅도 해야 하고 운영도 해야 하므로 로그파일을 확인해야 한다. 위에서 각 프로세스의 상태를 확인했을 때 보았듯이 pm2가 로그파일을 직접 관리해 주므로 이 파일을 tail등의 명령어로 확인하면 로그를 확인할 수 있다.

$ pm2 logs example
[PM2] Tailing last 20 lines for [example] process

example-0 (out): GET / 304 261.475 ms - -
example-0 (out): GET /stylesheets/style.css 304 1.960 ms - -
example-0 (out): GET / 304 13.963 ms - -
example-0 (out): GET /stylesheets/style.css 304 0.732 ms - -
example-0 (out): GET / 304 18.248 ms - -
example-0 (out): GET / 200 11.425 ms - 170
example-0 (out): GET /stylesheets/style.css 304 0.299 ms - -

[PM2] Streaming realtime logs for [example] process

pm2 logs 혹은 특정 프로세스의 로그만 보고 싶다면 pm2 logs <id|name>을 실행하면 마지막 로그내용과 함께 이후 출력되는 로그 내용을 화면에 뿌려주게 되므로 파일을 일일이 찾을 필요없이 쉽게 로그내용을 확인할 수 있다. 로그파일을 지우려면 pm2 flush를 실행하면 된다.

클러스터 모드

Node.js에도 클러스터 모듈이 존재하지만, PM2를 이용하면 CPU를 최대한 활용할 수 있도록 클러스터 모드로 실행할 수 있다.

$ pm2 start app.js -i 0 --name "example"
[PM2] Starting app.js in cluster_mode (0 instance)
[PM2] Done.
┌──────────┬────┬─────────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode    │ pid   │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼─────────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ example  │ 0  │ cluster │ 72720 │ online │ 0       │ 0s     │ 37.293 MB   │ disabled │
│ example  │ 1  │ cluster │ 72721 │ online │ 0       │ 0s     │ 37.508 MB   │ disabled │
│ example  │ 2  │ cluster │ 72734 │ online │ 0       │ 0s     │ 37.457 MB   │ disabled │
│ example  │ 3  │ cluster │ 72753 │ online │ 0       │ 0s     │ 37.824 MB   │ disabled │
│ example  │ 4  │ cluster │ 72772 │ online │ 0       │ 0s     │ 37.766 MB   │ disabled │
│ example  │ 5  │ cluster │ 72789 │ online │ 0       │ 0s     │ 30.938 MB   │ disabled │
│ example  │ 6  │ cluster │ 72806 │ online │ 0       │ 0s     │ 27.734 MB   │ disabled │
│ example  │ 7  │ cluster │ 72823 │ online │ 0       │ 0s     │ 23.613 MB   │ disabled │
└──────────┴────┴─────────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

인스턴스를 실행할 때 -i 옵션을 사용하면 클러스터 모드로 실행되고 -i뒤에는 실행할 인스턴스의 개수를 지정하면 된다. 여기서 0을 지정하면 사용 가능한 CPU 개수만큼 실행이 된다. 앞에 실행과 비교해 보면 modefork가 아니라 cluster로 표시됨을 알 수 있다. 애플리케이션 서버가 무상태(stateless)라면 다른 수정 없이 바로 클러스터 모드를 사용할 수 있고 자동으로 로드 밸런싱이 된다. 나는 노드의 클러스터 모듈 대신 서버 내에 인스턴스를 여러 개 띄우는 걸 선호하기 때문에 이 방법은 무척 편리하다.

클러스터 모드를 사용할 때 pm2 restart <id|name> 대신 pm2 reload <id|name>를 사용하면 다운타임 없이 서버를 재기동할 수 있다. 단 클러스터 모드는 Node.js v0.10에서는 지원하지 않는다.


JSON 설정 파일

앞에서는 명령어로 서버를 실행했지만 실행할 인스턴스의 설정을 JSON 파일로 관리할 수 있다. JSON 파일로 관리하면 좋은 점은 어떤 서버나 환경에서도 같은 설정을 사용해서 서버를 실행할 수 있다는 점이다. 앞에서는 간단한 옵션만을 사용했지만, PM2의 제공하는 수많은 옵션을 모두 JSON으로 관리할 수 있다.

다음과 같은 ecosystem.json이 있다고 해보자.

{
  "apps": [{
    "name": "example",
    "script": "app.js",
    "watch": false,
    "env": {
      "NODE_ENV": "production",
      "API_PORT": 4000
    },
    "exec_mode": "cluster",
    "instances": 0
  }]
}

pm2로 실행할 때 이 JSON 파일로 실행하면 지정한 설정으로 서버를 실행할 수 있다.

$ pm2 start ecosystem.json
[PM2] Process launched
┌──────────┬────┬─────────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode    │ pid   │ status │ restart │ uptime │ memory      │ watching │
├──────────┼────┼─────────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤
│ example  │ 0  │ cluster │ 17074 │ online │ 0       │ 0s     │ 37.551 MB   │ disabled │
│ example  │ 1  │ cluster │ 17075 │ online │ 0       │ 0s     │ 37.516 MB   │ disabled │
│ example  │ 2  │ cluster │ 17090 │ online │ 0       │ 0s     │ 37.988 MB   │ disabled │
│ example  │ 3  │ cluster │ 17109 │ online │ 0       │ 0s     │ 37.617 MB   │ disabled │
│ example  │ 4  │ cluster │ 17124 │ online │ 0       │ 0s     │ 37.695 MB   │ disabled │
│ example  │ 5  │ cluster │ 17141 │ online │ 0       │ 0s     │ 31.508 MB   │ disabled │
│ example  │ 6  │ cluster │ 17160 │ online │ 0       │ 0s     │ 26.074 MB   │ disabled │
│ example  │ 7  │ cluster │ 17179 │ online │ 0       │ 0s     │ 20.156 MB   │ disabled │
└──────────┴────┴─────────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app


모니터링

PM2에서는 자체적으로 모니터링 기능도 제공하고 있다. pm2 monit을 실행하면 터미널에서 다음과 같은 모니터링 대시보드를 볼 수 있다. 서버 배포 후나 테스트를 할 때 서버의 부하를 확인하는 용도로 좋다.

PM2 모니터링 화면

2016/02/23 23:48 2016/02/23 23:48