티스토리 뷰

back-end/nodejs

[PM2]Nodejs process manager

kmj24 2021. 11. 27. 23:03

nodejs는 node.js는 Chrome V8 javascript엔진으로 빌드된 브라우저 밖에서 javascript를 실행하도록 해주는 javascript run time 환경이다.

Event Driven, Non-Blocking I/O 모델을 사용하여 가볍고 성능이 뛰어나서 Server side 개발할 때 Nodejs를 많이 선택한다고 한다.

 

PM2 (Nodejs Process Manager)

PM2 설치 및 실습

npm 명령어를 통해 global 설치를 진행한다

npm install -g pm2

 

PM2로 무중단 서비스 제공

만약 어떤 서비스를 배포하여 서비스하고 있다고 하자. 배포된 서비스의 평균 동시접속 유저의 숫자가 5000명이다.

근데 어떠한 이유(처리하지 못한 예외사항)등으로 배포된 프로그램이 종료되었다. 이때 이 서비스를 이용하는 5000명 이상의 유저가 피해를 보게 된다. 정기결제를 해야되는 서비스이거나, 결제 프로세스와 같이 금액이 발생하는 서비스일 경우 이러한 프로그램 중단으로 인한 피해는 더욱 심각지게 될것이다.

PM2는 프로세스를 관찰하고 있다가, 프로세스가 중단될 경우 자동으로 재실행시켜줄 수 있다.

 

  간단한 application이 있다.

// app.js
const http = require("http");
let i = 0;
let app = http.createServer((req, res) => {
  console.log(i);
  res.end(String(i));
  i = i + 1;
});
app.listen(3000);

(위의 코드는 localhost:3000에 접근할 때 마다 i가 증가되어 출력되는 코드이다.)

pm2 exmples를 터미널에 입력하면 나오는 pm2 명령어들

pm2를 이용하여 위의 코드를 실행시켜보자. 

pm2 start app.js

pm2를 이용하여 프로그램을 실행시킨 모습

위의 코드는 IPv4 0.0.0.0의 3000번 포트를 이용한다.

localhost:3000 으로 접속하여 application의 실행을 확인할 수 있다.

 

pm2 

(window 기준)

이제 해당 프로세스를 강제로 종료시켜 pm2가 Application을 다시 실행시키는지 확인해보자.

1. netstat -a -n -o 명령어로 할당된 포트번호와 PID를 찾는다.

2. taskkill 명령어로 port 3000번의 프로세스를 강제로 죽여보았다. 현재 할당된 pid는 6460이다.

(taskkill 명령어로 성공적으로 프로세스가 종료된다면 순간적으로 터미널 창에서 프로세스 종료 성공 메시지를 띄운다.)

taskkill /f /pid 6460

3. nestat -a -n -o 명령어로 프로세스가 작동하는지 확인해보자.

 

4. port 3000번을 사용하는 프로세스는 pm2가 관찰하고 있는 프로세스이고 pm2가 해당 프로세스가 종료되자 되살린 모습이다.

 

위의 과정에서 일어난 일은 pm2의 명령어를 통하여 확인도 가능하다.

pm2를 실행 시켰을때의 사진이다.

위에서 보인것 처럼 pid는 6460인것을 알 수 있다.

프로세스를 죽이고 pm2가 다시 되살렸을때 pm2의 프로세스를 확인해보자.

pm2 list

pid가 바뀐것을 볼수 있다.

pm2 stop/restart 명령어도 있다. 

start한 application을 사용해도 되고 pm2 list에서 확인할 수 있는 id(고유값)으로 stop/restart가 가능하다.

pm2 stop app.js
pm2 restart app.js

pm2 stop 0
pm2 restart 0

 

pm2 stop으로 application을 종료할 경우 status에서 stopped라고 뜬다.

list에 있는 프로세스 id 또는 name으로 얼마든지 start가 가능하다.

 

pm2 list에서 프로세스를 제거하고 싶다면

pm2 delete 0

이렇게 되면 pm2의 list에서 없어지고 더이상 id로 start할 수 없게 된다.

 

그 외에도 pm2는 hot reload, log출력 등 편리한 기능이 있다.

// hot reload
pm2 app.js --watch

// log 확인
pm2 log

 

또한 pm2를 이용하여 개발할 때 pm2-dev 명령어로 develop mode에서 개발을 할 수 있다.

pm2-dev app.js

 

pm2를 이용하여 device를 끄고 다시 실행시켜도 재실행 할 수 있다.

1. pm2를 이용하여 application을 실행한다.

pm2 start app.js

2. pm2 명령어로 현재 상태를 저장한다.

pm2 save

3. pm2 명령어로 OS가 실행되었을 때 동작해야되는 프로세스를 실행시켜주는(스케줄링) 스크립트를 생성한다.

pm2 startup

4. 생성된 스크립트를 실행한다.

window OS 환경에서는 pm2 startup을 지원하지 않는다고 한다.

https://pm2.keymetrics.io/docs/usage/startup/#windows-consideration

 

PM2 - Startup Script

Advanced process manager for production Node.js applications. Load balancer, logs facility, startup script, micro service management, at a glance.

pm2.keymetrics.io

pm2-installer를 사용하여 setup을 할 수 있지만 여기서는 다루지 않겠다.

 

5. 스케줄링 된 pm2 프로세스를 해제하려면 다음의 명령어를 실행한다.

pm2 unstartup systemd

 

 

PM2로 Multi thread 관리

Node.js는 Event-loop라는 single thread를 사용하고, Node.js Application은 CPU의 single core에서만 실행되므로, multi core 시스템은 사용할 수 없다. 이러한 문제를 해결하기위해 Cluster 모듈을 통해 single 프로세스를 multi process(worker)로 늘릴 수 있는 방법을 제공한다. 그렇다면 클러스터 모듈을 사용하여 마스터 프로세스에서 CPU 코어 수 만큼 워커 프로세스를 생성해서 모든 코어를 사용하게끔 개발할 수 있다.

 Application 실행 시 처음에는 마스터 프로세스만 생성되는데, 이때 CPU 개수만큼 워커 프로세스를 생성하고 마스터 프로세스와 워커 프로세스가 각각 수행해야 할 일들을 정리하여 구현하면 되는데 이는 매우 번거로운 작업이지만 PM2를 이용하여 간단하게 실행할 수 있다.

 

pm2의 간단한 명령어로 thread의 숫자를 조절할 수 있다.

pm2 start app.js -i max

24개의 thread를 사용한다.

현재 사용중인 cpu의 thread는 24개이고 정확하게 24개의 프로세스에 할당한다.

현재 사용중인 cpu의 core 수와 thread 수이다.

위에서는 모든 thread를 사용한다고 했지만 몇개의 thread만 사용하고 싶을 경우 간단하게 숫자만 조절해주면 된다.

10개의 thread에 할당한다고 했을때의 모습이다.

와 너무 편해 ~

PM2의 옵션을 이용하여 Application실행.

위에서 사용한 예제들은 PM2기본모드인 fork 모드로 application을 실행한다.

PM2에서는 다양한 옵션들이 있는데 이를 편리하게 사용하기 위해 설정 파일을 만들고 실행할 수 있다.

ecosystem.config.js파일을 만들고 옵션을 설정한다.

module.exports = {
  apps: [
    {
      name: "app",
      script: "./app.js",
      instance: 0,
      exec_mode: "cluster",
    },
  ],
};

이 파일을 pm2로 실행하면 파일에 설정해둔 옵션으로 pm2가 실행된다.

pm2 start ecosystem.config.js

(설정 파일 옵션들은 공식문서에서 확인이 가능하다.)

https://pm2.keymetrics.io/docs/usage/application-declaration/

 

PM2 - Ecosystem File

Advanced process manager for production Node.js applications. Load balancer, logs facility, startup script, micro service management, at a glance.

pm2.keymetrics.io

설정 파일을 이용하여 pm2 설정을 편하게 관리할 수 있다.

 

 

PM2 가 서비스를 무중단으로 할 수 있는 방법.

프로세스 재시작 과정 (reload)

1. process가 10개 실행되거 있다고 가정했을 때, reload를 실행했을 경우,

2. 기존 0번 프로세스를 '_old_0' 프로세스로 옮겨둔 후 새로운 0번 프로세스를 만든다.

3. 새로운 0번 프로세스는 요청을 처리할 준비가 되면 마스터 프로세스에 'ready' 이벤트를 보낸다.

4. 더이상 필요 없어진 '_old_0' 프로세스에게 'SIGINT' 시그널을 보내고 프로세스가 종료되기를 기다린다.

 4.1 'SIGINT'는 프로세스를 종료시키는 신호이다.

5. 만약 'SIGINT'시그널을 보낸 후 일정시간(1600ms)이 지났는데 종료되지 않는다면 'SIGKILL' 시그널을 보내 프로세스를 강제로 종료시킨다.

6. 위를 할당된 process 수 만큼 반복한다.

 

재시작 과정 서비스 중단이 발생한 경우

위의 프로세스 재시작 과정에서 서비스 중단이 발생할 수 있다.

※ 서비스 중단 시나리오 

ex) 새로 만들어진 프로세스가 아직 요청을 받을 준비가 되지 않았는데 ready이벤트를 보낸 경우

1. 프로세스 재시작의 2번에서 프로세스를 생성한 뒤 3에서 프로세스 구동이 완료되기도 전에 마스터 프로세스에게 ready 이벤트를 보낸다면 마스터 프로세스는 새로운 프로세스가 요청받을 준비가 되었다고 판단해버린다. (실제로는 준비가 안된 상태)

2. 이때 기존 프로세스('_old_0')는 더이상 필요없다고 판단하여 SIGINT 시그널로 프로세스를 종료한다.

3. 만약 기존 프로세스가 삭제되기 전에 새로운 프로세스 구동이 완료될 경우에는 문제가 발생하지 않지만, 기존 프로세스가 이미 종료된 상태에서 새로운 프로세스는 request가 발생해도 처리할 수 없게 된다. 즉 서비스 중단이 발생하게 된다.

 

※ 이 문제를 해결하기 위해 프로세스가 수행될 때 바로 ready 이벤트를 보내지 말고 프로세스가 요청 받을 준비가 완료된 시점에 ready 이벤트를 보내도록 처리해야 된다.

이는 pm2 설정으로 해결할 수 있다.

ecosystem.config.js

module.exports = {
  apps: [
    {
      name: "app",
      script: "./app.js",
      instance: 10,
      exec_mode: "cluster",
      wait_ready: true,
      listen_timeout: 10000,
    },
  ],
};

wait_ready를 true로 설정하고 listen_timeout옵션으로 ready이벤트를 기다릴 시간을 설정한다.

그리고 app.js에서 app.listen이 완료되면 실행되는 callback 함수에서 마스터 프로세스로 ready 이벤트를 보내도록 한다.

app.js

const express = require("express");
const app = express();
const port = 3000;
app.get("/", (req, res) => {
  res.send("pm2 test");
});
app.listen(port, () => {
  process.send("ready");
  console.log("ready");
});

만약 wait_ready를 설정해두었는데 app.listen에서 process.send('ready')를 보내지 않는다면 ecosystem.config.js에서 설정한 listen_time 만큼 기다린 후 진행되므로 꼭 wait_ready시 procss.send('ready')를 설정해두도록 하자.

 

클라이언트 요청 처리 도중 프로세스가 중단되버리는 경우

※ 서비스 중단 시나리오

ex) SIGINT 시그널이 전달된 상태에서 사용자 요청을 받았고, 그 요청을 처리하는데 일정시간(1600ms)보다 더 큰 5000ms 라고 가정

위에서 재시작 과정 도중 서비스 중단이 발생한것과 동일한 상황이 된다.

1. SIGINT 시그널을 받은뒤 1600ms이후에도 종료되지 않고 SIGKILL 시그널을 받아 프로세스가 종료된다.

2. 따라서 5000ms 가 걸리는 프로세스가 실행 도중 죽어버린다. (예를 들어 2400ms 쯤에 프로세스가 죽어버린다.)

3. 따라서 사용자에게 응답을 보내주지 못한채 종료되고, 프로세스가 강제 종료 되었기 때문에 클라이언트와의 연결은 끊어지게 된다.

 

위 문제를 해결하기 위해 다음의 프로세스로 진행되도록 한다.

1. SIGINT 시그널을 listening하다가 해당 시그널이 전달되면, app.close 명령어로 프로세스가 새로운 요청을 받는 것을 거절하고 기존 연결은 유지하게 처리한다.

2. 사용자 요청을 처리하기에 충분한 시간을 kill_timeout에 설정하고, 기존에 유지되고 있던 연결이 종료되면 프로세스가 종료되도록 설정한다.

위 프로세스로 진행되도록 ecosystem.config.js에 설정한다.

module.exports = {
  apps: [
    {
      name: "app",
      script: "javascript/pm2/app.js",
      instance: 10,
      exec_mode: "cluster",
      wait_ready: true,
      listen_timeout: 10000,
      kill_timeout: 5000,
    },
  ],
};

kill_timeout 옵션으로 SIGINT ~ SIGKILL 시그널 사이의 대기시간을 기본(1600ms)에서 5000ms로 늘렸다.

const express = require("express");
const app = express();
const port = 3000;
app.get("/", function (req, res) {
  res.send("pm2 test");
});
app.listen(port, function () {
  process.send("ready");
  console.log("ready");
});
process.on("SIGINT", function () {
  app.close(function () {
    console.log("server closed");
    process.exit(0);
  });
});

app.js에서는 'SIGINT' 시그널을 받을 경우에 대한 처리를 한다.

여기에도 예외사항이 있다.

만약 HTTP 1.1 Keep-Alive를 사용하고 있다면, 기존 요청이 처리된 후에도 계속 연결이 유지되기 때문에 위의 방법만으로는 해결되지 않는다.

const express = require("express");
const app = express();
const port = 3000;
let isDisableKeepAlive = false;
app.use((req, res, next) => {
  if (isDisableKeepAlive) {
    res.set("Connection", "close");
  }
  next();
});
app.get("/", (req, res) => {
  res.send("pm2 test");
});
app.listen(port, () => {
  process.send("ready");
  console.log("ready");
});
process.on("SIGINT", () => {
  isDisableKeepAlive = true;
  app.close(() => {
    console.log("server closed");
    process.exit(0);
  });
});

SIGINT 시그널을 받았을 때 특정 전역 플래그 값에 따라 응답 헤더에 'Connection: close'를 설정하여 클라이언트 요청을 종료하는 방법으로 서비스 중단 문제를 해결할 수 있다.

 

 

참고:

https://pm2.keymetrics.io/

 

PM2 - Home

Advanced process manager for production Node.js applications. Load balancer, logs facility, startup script, micro service management, at a glance.

pm2.keymetrics.io

https://engineering.linecorp.com/ko/blog/pm2-nodejs/

 

PM2를 활용한 Node.js 무중단 서비스하기 - LINE ENGINEERING

자바스크립트는 가장 널리 사용되는 클라이언트 측 프로그래밍 언어이자 프론트엔드 웹 개발 언어 중 하나입니다. 그리고 Node.js는 Chrome의 V8 자바스크립트 엔진으로 빌드된 자바스크립트 런타

engineering.linecorp.com

https://www.youtube.com/watch?v=p1Hp09Fr8HQ 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함