티스토리 뷰
이번에는 노드를 더 우아하게 사용할 수 있도록 돕는 PM2 에 대해 이야기를 해본다. PM2 는 Process Manager 의 약자로 이미 단어에서 모든게 설명 되는데 노드 프로세스를 관리해주는 역할을 한다. 아래에서 간단한 노드 프로그램을 기반으로 PM2 를 차근차근 이해하도록 하자.
보통 서버에 데몬 형태의 application 을 개발하게 되면 여러가지 것들을 신경써야 했다. 이를테면 application 에서 남기는 로그에 대한 처리( filesystem 을 이용해서 남기는 방법, 날짜 포함, rotate, etc ), 프로세스가 죽었을 때에 대한 처리( restart ), 부팅시에 자동 실행 등 무수히 많은 것들이 있다. 이런 귀찮은 것들을 몽땅 관리해주는 프로세스가 있다면 얼마나 편할까? (노드에만 국한된 이야기지만) 그래서 PM2 가 나왔다.
유사한 매니저 프로그램으로는 qmail 에서 사용하는 svscan 이 있겠지만 얘는 그냥 프로세스가 죽으면 살리는 역할만 할 수 있다. 반면 PM2 는 위에서 말한 모든 것+알파가 가능해진다. 자, 이제 그 PM2 를 본격적으로 살펴보자.
노드 프로젝트답게 npm 을 통해 아래와 같이 pm2 를 설치할 수 있다.
$ npm install pm2 -g
뒤에 -g 옵션을 붙이는 이유는 사용자가 pm2 명령어를 어떤 디렉터리에서건 사용할 수 있게 하기위함이다. 글로벌 세팅이라고 보면 된다. npm 이야기에서 만들어뒀던 API 서버 프로그램을 pm2 로 돌려보자.
$ pm2 start server.js
정말 이렇게 간단하게 pm2에 등록된다. PM2 에 등록된 관리 리스트를 볼 때는 list 옵션이 사용된다.
$ pm2 list
프로세스를 종료할 때는 pm2 list 에서 확인한 앱의 이름이나 애플리케이션의 ID (list 에서 확인 가능)를 stop 의 인자로 넘겨주면 된다.
$ pm2 stop 0
방금 위에서 애플리케이션의 ID 로 조정할 수 있다고 했는데 애초에 start 를 할 때 앱의 이름을 지정할 수 있다. 이렇게 하는 이유는 다양한 프로젝트를 pm2 에서 명확하게 관리하기 위해서다. 자, 그러면 기존에 등록되어 있던 server.js 를 우선 pm2 에서 delete 옵션을 통해 삭제하도록 하자.
$ pm2 delete 0
그리고 아래와 같이 ID 를 지정해서 다시 PM2 에 등록하도록 한다.
$ pm2 start server.js --name "API"
다시 pm2 list 를 살펴보면 ID 가 등록된 것을 볼 수 있다.
$ pm2 list
여기서 방금 올린 API 에 대한 상세 정보가 필요하면 show 옵션을 사용하도록 한다.
$ pm2 show API
server.js 내용이 수정되는 경우에는 pm2 에 API 이름을 갖는 대상에 대해 재시작( restart )만 날려주면 되지만 pm2 의 인자를 변경하는 경우에는 delete 하고 새로 등록해줘야한다는 점을 잊지말자. 위에 내용중에 자세히 보면 out log path (console.log 가 출력되는 위치) 가 기본적으로 .pm2 밑에 저장되고 있는 것을 볼 수 있는데 이 위치를 우리가 원하는 곳으로 옮겨보자. 앞선 설명처럼 API 를 delete 하고 옵션을 추가하도록 한다.
$ pm2 delete API && pm2 start server.js --name "API" -o ./api.log
뒤에 -o 옵션으로 api.log 를 지정해줬다. 이제 코드 내에서 console.log 로 출력한 모든 표준출력( stdout ) 이 api.log 로 기록 되게 된다. (짝짝) 노드가 아닌 다른 언어였면 로그를 남기기 위해 아래와 같은 함수를 등록해놓고 사용해야 했을 것이다.
// pseudo code
void systemLog(char *log)
{
fopen(api.log)
fwrite(log)
fclose(api.log)
}
때때로 open/write 에 대한 atomic 보장을 위해 함수 내부에 lock 이 필요한 언어도 있다. 아무튼, 중요한건 이런 불편을 pm2 가 어느정도 다 감수해준다는거다. 하물며 개발단계에서 확인하던 console.log 를 그대로 사용할 수 있다는 메리트가 크다. (사실 처음부터 pm2 로 관리하면 여러모로 편합니다)
하지만 아직 아쉬운 것이 있다. 위에서 언급한 것 처럼 -o 옵션은 stdout 을 처리하기 때문에 프로세스에서 발생하는 표준에러( stderr ) 에 대해서는 처리하지 못한다. 이를 처리하기 위한 옵션도 있으니 걱정하지마시라. 우선 API 를 삭제하고 -e 옵션으로 stderr 에 대한 처리도 추가해보자.
$ pm2 delete API && pm2 start server.js --name "API" -o ./api.log -e ./api.log --merge-logs
이제 노드 모듈의 예외발생 같은 크래시 메시지를 api.log 에서 확인할 수 있을 것이다. 자, 이렇게 잘 운영을 하고 있었는데 우리 서비스가 무척 잘 되서 트래픽이 급증하였다. 이런 일이 발생하면 단일 노드(js)에서 모든 것을 처리하기 버거워진다. 트래픽을 받아서 async 하게 처리한다고 해도 내부로직이 복잡해지면 어느순간 치솟는 CPU 를 어찌하지 못하는 상황이 온다. 이때 필요한 것이 클러스터이다. PM2 의 클러스터는 분산컴퓨팅의 그 용어와 비슷한 역할을 하는데 server.js 한 개 노드파일을 지정한 개수만큼 PM2 에 등록하고 Load Balancing (LB) 를 통해 부하를 분산시켜준다. PM2 의 LB 는 round robin (RR) 방식으로 동작하며 모든것은 옵션 하나면 충분하다.
$ pm2 delete API && pm2 start server.js --name "API" -i 3 -o ./test.log
위 예제에서는 -i 옵션을 통해 클러스터를 3 개 등록했다. 실제 패킷을 발생시켜보면 완벽하게 RR 으로 LB 되는 것을 확인할 수 있다. 아래처럼 패킷을 발생시켜보자.
$ curl localhost:6955
로그도 확인해보면 test.log 에 클러스터 번호가 붙어서 각각 test-0.log, test-1.log, test-2.log 로 나뉘어서 기록되게 된다. 저장되는 모든 로그를 아래처럼 확인해보자. RR 으로 LB 됨이 확인된다. (코드에 각 프로세스를 구분할 수 있도록 process.pid 를 추가해줬다)
$ tail -f test-*
==> test-0.log <== pid: 1736 > get request
==> test-1.log <== pid: 1737 > get request
==> test-2.log <== pid: 1750 > get request
==> test-0.log <== pid: 1736 > get request
==> test-1.log <== pid: 1737 > get request
==> test-2.log <== pid: 1750 > get request
운영도중에 클러스터 개수를 조정할 수도 있다. 즉 scale in/out 이 가능하다는 이야기다. scale in/out 은 아래와 같이 설정할 수 있다.
$ pm2 scale API 5
클러스터 3개로 돌고 있던 API 를 클러스터 5로 지정했다. 이에따라 2개의 클러스터가 추가된 것을 확인할 수 있겠다. 혹시 API 를 급하게 중단해야한다? all 을 인자로 넘겨서 아래와 같이 관리되고 있는 전체를 중단하거나 재시작할 수 있다.
$ pm2 stop all
monit 옵션으로 프로세스를 모니터링하는 것도 지원하는데 이것도 참 물건이다.
$ pm2 monit
아래와 같이 PM2 로 관리되는 프로세스별 상태를 볼 수 있고 실시간 로그까지 확인 가능하다. 빠져나올때는 키보드의 q 를 눌러주면 되겠다.
Node.js 로 작성된 프로그램을 서버에서 운영하기 위해 필요한 기초적인 몇 가지를 살펴봤다. 사실 이외에도 살펴보면 엄청나게 많은 옵션이 제공되고 있는데 공식홈페이지를 참고하면 되겠다. 여러가지 옵션을 잘 살펴보고 적절한 상황에서 활용해주면 된다.
'개발 > Node.js' 카테고리의 다른 글
노드를 더 우아하게. nvm 이야기 (0) | 2018.06.07 |
---|---|
비동기 교통정리 - async.waterfall (6) | 2018.06.04 |
콜백(callback) 개념 이해하기 (0) | 2018.03.26 |
pm2 모듈을 부트 스크립트로 등록하기 (0) | 2018.03.16 |
노드를 더 우아하게. npm 이야기 (0) | 2018.03.08 |
- Total
- Today
- Yesterday