[Node.js] PM2(Process Manager) 알아보기

4 minute read

현재 회사에서 운영 중인 프로젝트들 모두 PM2를 이용해 프로세스를 관리하고 있다. PM2 는 코드를 작성하는 데에 직접적인 영향을 주지 않아서 필자는 큰 관심을 가지지 않고 있었고, 타 팀원이 설정해둔 그대로 사용하고 있었다.

그러던 와중에 해당 팀원이 퇴사를 했고(..😭) PM2가 원인으로 생각되는 이슈가 발생해 겸사겸사 공부하게 됐다. 이번 글에서는 이번에 알게 된 PM2 관련 지식을 정리하고자 한다.

PM2란?

PM2 홈페이지에서는 PM2를 다음과 같이 정의하고 있다.

PM2 is a daemon process manager that will help you manage and keep your application online. Getting started with PM2 is straightforward, it is offered as a simple and intuitive CLI, installable via NPM.

PM2는 Node.js 어플리케이션을 쉽게 관리할 수 있게 해주는 Process Manager이다. Node.js 어플리케이션을 cluster mode 로 실행시킨다거나, 메모리가 넘친다거나, 오류로 인해 프로세스가 종료되는 등의 상황에 직면했을 때 각각의 상황을 사용자가 모두 신경 써서 처리해줄 수도 있지만, 너무 복잡하고 신경 써야 할 일들이 많아진다.

PM2를 이용하면 간단한 설정만으로도 이러한 처리를 손쉽게 해결할 수 있다.

PM2 cluster mode

출처: pm2 홈페이지 / cluster mode

PM2의 cluster 모드는 Node.js의 cluster module을 이용해 기본적으로 싱글 스레드인 Node.js를 멀티 스레드로 구동시켜준다.

싱글 스레드의 경우 구동 중인 서버의 CPU 개수와 상관없이 1개만 사용할 수 있기에 서버의 성능을 제대로 끌어내지 못한다. 반면, 멀티 스레드는 최대 서버 CPU 수만큼(하이퍼스레딩을 지원한다면 x2) 프로세스를 생성해 최대 성능을 끌어낼 수 있다.

PM2에서 cluster mode를 사용하기 위해서는 다음과 같이 설정해주면 된다.

// ecosystem.config.js
module.exports = {
  apps : [{
    script    : "app.js", 
    instances : "max", // 실행시킬 프로세스의 갯수(max로 입력할 경우 최대 갯수로 설정한다.)
    exec_mode : "cluster" // cluster 모드로 어플리케이션을 구동시킨다.
  }]
}

PM2 start

PM2 프로세스를 시작하기 위해서는 pm2 start script or 설정 파일명 명령어를 사용하면 된다. 위에서 설정한 값을 기준으로 프로세스를 시작하면 아래 결과를 얻을 수 있다.

  pm2 git:(master)  pm2 start ecosystem.config.js 

[PM2] Spawning PM2 daemon with pm2_home=/Users/armadillo.ko/.pm2
[PM2] PM2 Successfully daemonized
[PM2][WARN] Applications app not running, starting...
[PM2] App [app] launched (4 instances)

┌─────┬────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
 id   name    namespace    version  mode     pid       uptime       status     cpu       mem       user      watching 
├─────┼────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
 0    app     default      0.0.0    cluster  15660     0s      0     online     17%       33.9mb    arm      disabled 
 1    app     default      0.0.0    cluster  15661     0s      0     online     21%       38.1mb    arm      disabled 
 2    app     default      0.0.0    cluster  15663     0s      0     online     12%       31.3mb    arm      disabled 
 3    app     default      0.0.0    cluster  15668     0s      0     online     0%        26.5mb    arm      disabled 
└─────┴────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

총 4개의 프로세스가 실행된 것을 확인할 수 있는데, 이건 필자 컴퓨터의 CPU가 듀얼코어이면서 하이퍼 스레딩을 지원하기 때문이다.

시작 시점에 다양한 설정을 할 수 있는데, 이 링크를 참고하면 좋다.

PM2 모니터링

실행된 PM2 프로세스를 모니터링 하려면 pm2 monit 명령어를 입력하면 된다. 해당 명령어를 실행하면 아래 항목들을 실시간으로 확인할 수 있다.

  • 각 프로세스의 메모리, CPU 사용률, 현재 상태
  • 선택된 프로세스의 로그
  • 전체 프로세스의 Heap 사이즈, 사용률
  • 어플리케이션 정보

필자는 주로 부하 테스트를 수행할 때 메모리와 CPU 사용량이 어느정도까지 올라가는지 체크하는 용도로 사용하고 있다.

  pm2 git:(master)  pm2 monit

┌─ Process List ─────────────────────────────────┐┌──  app Logs  ────────────────────────────────────────────────────────────────────────────────────────────────────┐
[ 0] app       Mem:  35 MB    CPU:  0 %  online ││ app > Hello world!                                                                                               
[ 1] app       Mem:  36 MB    CPU:  0 %  online ││ app > Hello world!                                                                                               
[ 2] app       Mem:  35 MB    CPU:  0 %  online ││ app > Hello world!                                                                                               
[ 3] app       Mem:  38 MB    CPU:  0 %  online ││ app > Hello world!                                                                                               
                                                ││ app > Hello world!                                                                                               
                                                ││ app > Hello world!                                                                                               
                                                ││ app > Hello world!                                                                                               
                                                ││ app > Hello world!                                                                                               
                                                ││ app > Hello world!                                                                                               
                                                ││ app > Hello world!                                                                                               
                                                ││ app > Hello world!                                                                                               
                                                ││ app > Hello world!                                                                                               
                                                ││ app > Hello world!                                                                                               
└────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
┌─ Custom Metrics ───────────────────────────────┐┌─ Metadata ───────────────────────────────────────────────────────────────────────────────────────────────────────┐
 Heap Size                            9.86 MiB  ││ App Name              app                                                                                        
 Heap Usage                            75.65 %  ││ Namespace             default                                                                                    
 Used Heap Size                       7.46 MiB  ││ Version               0.0.0                                                                                      
└────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
 left/right: switch boards | up/down/mouse: scroll | Ctrl-C: exit                                                             To go further check out https://pm2.io/

PM2 reload

PM2 워커 프로세스를 임의로 재시작하거나 특정 조건에서 재시작 시킬 수 있다. 이 때 PM2는 무중단 재시작을 지원한다.

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

임의로 재시작

pm2 reload all | app-name | process-id 명령어를 통해 재시작이 가능하다. 모든 프로세스, 특정 어플리케이션, 특정 프로세스 아이디를 이용해 재시작이 가능하다.

메모리 limit 재시작

임의 재시작 외에도 프로세스의 메모리가 지정된 제한선을 넘어서면 자동으로 reload시킬 수 있다. 필자가 관리중인 서비스도 모두 메모리 제한을 설정해 두었다.

// ecosystem.config.js
module.exports = {
  apps : [{
    script    : "app.js", 
    instances : "max", 
    exec_mode : "cluster",
    max_memory_restart: '300M' // 프로세스의 메모리가 300MB에 도달하면 reload 실행
  }]
}

PM2를 이용한 무중단 배포

이 부분은 LINE의 기술 블로그에 잘 정리되어 있어 링크를 공유한다.

참고자료

Comments