입사 초기 프론트엔드 서비스 배포 환경이 EC2와 PM2로 구축되어 있었다.

PM2에 대해 공부하며 리서치한 내용이다.

PM2는 Process Manager의 약자로 NodeJS 프로세서를 관리하는, 원활한 서버 운영을 위한 패키지

  • 서비스를 제공하고 있는 도중 갑자기 서버가 중지되어도 서버를 다시 실행
  • Node.js는 싱글 스레드 기반이지만, 멀티 코어 혹은 하이퍼 스레딩을 사용할 수 있게 해줌
  • 클라이언트로부터 요청이 올 때 알아서 요청을 여러 노드 프로세스에 고르게 분배 (로드 밸런싱)

작동 원리

  1. PM2의 Cluster mode는 Node.js의 Cluster module을 통해 작동
  2. Node.js Cluster module은 Node.js에서 멀티코어 시스템을 이용할 수 있도록 Port를 공유하는 Child process를 생성
  3. 요청을 Parent process가 받은 후 Child process들에 재분배
  4. 각 Child process는 각각의 V8에서 작동하므로, Node 인스턴스를 더 띄운 것과 같이 동작
  5. 인메모리에 의존하지 않는 프로그램을 만들면 위와 같은 확장에 더 용이
  • Node.js 공식 홈페이지의 Cluster module에 대한 설명

A single instance of Node.js runs in a single thread. To take advantage of multi-core systems, the user will sometimes want to launch a cluster of Node.js processes to handle the load.

>

Node.js의 단일 인스턴스는 단일 스레드에서 실행되는데, 멀티코어 시스템을 이용하기 위해서 Node.js 프로세스들을 클러스터로 사용할 수 있다. 이를 통해 모든 서버 port를 공유하는 하위 프로세스를 생성한다.

The worker processes are spawned using the child_process.fork() method, so that they can communicate with the parent via IPC and pass server handles back and forth.

>

이 하위 프로세스들은 child_process.fork() 메서드를 사용해서 생성되는데, 부모 자식 간의 통신을 위한 IPC(Inter-process communication) 채널을 가지고 있으며 생성된 각 프로세스는 자체 V8인스턴스가 있다.


명령어

pm2 start <파일명> 
# pm2를 이용해 앱을 실행한다면 fork 모드(자식 프로세스)로 설정되고, 
# 서버 실행 즉시 Daemon화 되어, 종료하거나 에러가 발생하지 않는 이상 24시간 계속 유지된다.

$ pm2 start app.js --watch -i 2 # 프로세스를 watching하고 멀티 코어로 서버 실행

$ pm2 start app.js -i max # 최대 코어 갯수로 클러스터링
$ pm2 start app.js -i 0

$ pm2 scale app +2 # app 프로세스 갯수 +2
$ pm2 scale app 4 # app 프로세스 갯수 4개로 고정

$ pm2 status
$ pm2 ls # status와 ls를 이용해 현재 프로세스 리스트를 띄움

$ pm2 stop <app_name | namespace | id | 'all' | json_conf>
$ pm2 stop 2 # id 2번 프로세스를 멈춤
$ pm2 stop app # 이름을 줘도 됨. (전체 프로세스 멈춤)

$ pm2 restart <app_name | namespace | id | 'all' | json_conf>
$ pm2 restart 2 # 위에서 중지한 2번 프로세스를 재시작

$ pm2 reload <app_name | namespace | id | 'all' | json_conf>
$ pm2 reload app
$ pm2 reload all # 리로드

# - pm2 restart는 모든 프로세스를 죽인다음 다시 시작하는 방식. 그래서 아주 잠깐 동안 서비스를 이용하지 못하는 상황이 생긴다.
# - pm2 reload는 하나씩 프로세스를 죽여서, 최소한 1개 이상의 프로세스를 유지하며 하나씩 재시작 하는 방식이다. 
# 그래서 잠깐 재시작 하는 동안 서비스를 이용못하는 상황을 미연에 방지할 수 있다. 
# 단,이러한 방식 때문에 reload는 restart보다 재실행 속도가 느리다는 단점이 있다.

$ pm2 delete <app_name | namespace | id | 'all' | json_conf>
$ pm2 delete 2 # 특정 프로세스 삭제
$ pm2 delete app # 프로세스 name이 app인 거 모두 삭제

$ pm2 kill # 프로세스 전체 삭제

$ pm2 log # 전체 프로세스 로그 보기
$ pm2 log [process name | process id] # 특정 프로세스 로그 보기
$ pm2 log --lines 200 # 200줄 까지만 보기
$ pm2 log --err 200 # 에러 로그만 보기

설정 방법

  • pm2 설정 파일 만들기 (ecosystem.config.js)
$ pm2 ecosystem # ecosystem.config.js 파일이 생성
# ecosystem.config.js
module.exports = {

   /* apps 항목은 우리가 pm2에 사용할 옵션을 기재 */
   apps: [
      {
         name: 'projectName', // app이름
         script: './index.js', // 실행할 스크립트 파일
         instances: 2, // cpu 코어수 만큼 프로세스 생성 (instance 항목값을 ‘0’으로 설정하면 CPU 코어 수 만큼 프로세스를 생성)
         exec_mode: 'cluster', // 클러스터 모드
         max_memory_restart: '300M', // 프로세스의 메모리가 300MB에 도달하면 reload 실행
         
         watch: ['bin', 'routes'], //bin폴더, routes폴더를 감시해서 변경사항 실행
         ignore_watch: ['node_modules'], // 반대로 해당폴더의 파일변경은 무시
         
         env: {
            // 환경변수 지정
            Server_PORT: 4000,
            NODE_ENV: 'development',
            Redis_HOST: 'localhost',
            Redis_PORT: 6379,
         },
         
         output: '~/logs/pm2/console.log', // 로그 출력 경로 재설정
         error: '~/logs/pm2/onsoleError.log', // 에러 로그 출력 경로 재설정
      },
   ],

   /* deploy는 원격 서버와 git을 연동해서 배포하는 방식 */
   deploy: {
      production: {
         user: 'SSH_USERNAME',
         host: 'SSH_HOSTMACHINE',
         ref: 'origin/master',
         repo: 'GIT_REPOSITORY',
         path: 'DESTINATION_PATH',
         'pre-deploy-local': '',
         'post-deploy': 'npm install && pm2 reload ecosystem.config.js --env production',
         'pre-setup': '',
      },
   },
};
$ pm2 start ecosystem.config.js # 설정 파일을 start하면, 세팅대로 pm2 명령어가 실행

참고 자료