pm2 도입이유
노드는 싱글 스레드이다. 즉, cpu 코어를 하나만 쓰는 셈이다. 서버 컴퓨터의 cpu 코어를 하나만 쓰는 경우는 없다. 굉장히 작은 서버도 최소 코어 4개는 사용해야 한다. 또, 우리가 사용하는 일반적인 노트북에서도 cpu 코어가 4개 이상은 된다. 이런 상황에서 노드를 사용한다면 코어 3개 이상이 놀게 된다.
자신이 가지고 있는 노트북, 데스크탑의 코어가 몇 개인지 확인하려면 os 모듈을 이용해 다음과 같이 입력해보자
import os from "os";
const numCPUS = os.cpus().length;
console.log(numCPUS);
이런 문제를 해결하는 방법이 cluster 모듈, pm2 모듈이다. 노드에서 멀티 프로세싱을 사용하는 셈이다.
라인 개발 블로그에서 이에 대한 글이 있으니 첨부하였다.
먼저 알아야 할 사실은 Node.js는 기본적으로 싱글 스레드(thread)라는 점입니다. Node.js 애플리케이션은 단일 CPU 코어에서 실행되기 때문에 CPU의 멀티코어 시스템은 사용할 수 없습니다. 만약 보유하고 있는 서버의 사양이 8코어이며 하이퍼스레딩을 지원한다면 최대 16개 코어를 사용 할 수 있는데요. 모든 코어를 사용해 최대 성능을 내지 못하고 오직 한 개의 코어만 사용해야 한다면 주어진 자원을 제대로 활용하지 못하는 꼴이 됩니다. Node.js는 이런 문제를 해결하기 위해 클러스터(Cluster) 모듈을 통해 단일 프로세스를 멀티 프로세스(Worker)로 늘릴 수 있는 방법을 제공합니다. 그렇다면 우리는 클러스터 모듈을 사용해서 마스터 프로세스에서 CPU 코어 수만큼 워커 프로세스를 생성해서 모든 코어를 사용하게끔 개발하면 됩니다.
애플리케이션을 실행하면 처음에는 마스터 프로세스만 생성되는데요. 이때 CPU 개수만큼 워커 프로세스를 생성하고 마스터 프로세스와 워커 프로세스가 각각 수행해야 할 일들을 정리해서 구현하면 됩니다. 예를 들어 워커 프로세스가 생성됐을 때 온라인 이벤트가 마스터 프로세스로 전달되면 어떻게 처리할지, 워커 프로세스가 메모리 제한선에 도달하거나 예상치 못한 오류로 종료되면서 종료(exit) 이벤트를 전달할 땐 어떻게 처리할지, 그리고 애플리케이션의 변경을 반영하기 위해 재시작해야 할 때 어떤 식으로 재시작을 처리할 지 등등 고민할 게 많습니다. 이런 것들은 직접 개발하기에 번거로운 작업입니다. 따라서 이런 문제를 간편하게 해결할 수 있는 무언가가 있으면 좋겠다고 생각할 수 있는데요. 다행히 이런 고민이 녹아있는 PM2 라는 Node.js의 프로세스 매니저가 존재합니다. 이 PM2를 간단히 살펴보고, 서비스에 PM2를 적용해 어떻게 Node.js 애플리케이션을 무중단으로 운영할 수 있는 지에 대해서 알아보겠습니다.
pakage.json의 script 부분 수정 + cross-env + pm2
React.js에서 배포할 때 npm run build를 입력해 본 경험이 있을 것이다. 그와 같이 배포용으로 script를 수정해야 한다.
// 전의 코드
"scripts": {
"start": "nodemon --exec babel-node app.js"
}
// 수정 후 코드
"scripts": {
"dev": "nodemon --exec babel-node app.js",
"start": "NODE_ENV=production PORT=80 node app.js"
// 정식 출시를 위해 PORT를 80으로 설정했으니 .env의 PORT는 지워주도록 합시다.
},
그런데 윈도우 환경에서 npm start를 입력해서 해당 스크립트를 실행해보면 다음과 같은 오류를 만날 수 있다. 'NODE_ENV'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다. 생각해보면 이건 Webpack 설정에서도 만난 오류이다. WEBPACK_ENV'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.
이러한 오류가 나는 이유는 동적으로 process.env를 변경하는 것은 리눅스나 맥 환경에서만 작동하기 때문입니다. 윈도우 환경에서 해당 코드가 작동하기 위해서는 cross-env를 설치해야 한다.
(하기 싫다면 직접 .env 를 수정하는 방법도 있다.)
npm i cross-env
"scripts": {
"dev": "nodemon --exec babel-node app.js",
"start": "cross-env NODE_ENV=production PORT=80 node app.js"
},
만약 웹팩을 돌린 후라면 해당 파일을 대상으로 node를 돌리면 된다.
다음은 pm2이다. 지금까지는 node를 통해 app.js를 돌렸다. node는 싱글 스레드라는 단점을 가지고 있다. 요즘 대부분의 CPU가 멀티 코어를 가지고 있으며 하이퍼 스레드 기술을 통해 더 많은 수의 스레드를 사용할 수 있음을 고려하면 이 단점을 꽤나 치명적이라고 할 수 있다. pm2를 사용하면 멀티 프로세싱이 가능해져서 노드 프로세스 갯수를 들릴 수 있습니다.
다만 멀티 스레딩은 아니므로 서버의 메모리 자원을 공유하는 것이 불가능합니다. 예를 들어, 유저 정보를 세션 메모리에 저장하는 경우, 로그인을 한 후 새로고침을 했다고 가정해봅시다. 새로고침을 통해 들어간 프로세스에는 로그인 정보가 공유되지 않으므로 로그인을 했음에도 로그인이 안되어 있을 수 도 있습니다.
이러한 문제를 해결하기 위해 세션에 store를 지정해줍니다. mongoDB, MySQL, Memchaced나 Redis 같은 서비스를 이용합니다. (Redis는 여기저기 자주 사용하니 언젠가 배우기를 권합니다) 무엇을 이용하는 지에 따라 이점이 다릅니다. 그러나 최대한 프로세스 간에 공유하는 것이 없도록 설계하는 것이 좋습니다.
또한 클라우드 환경에서는 pm2를 사용해야 서버가 유지되고, 서버가 죽었을 때 자동으로 재시작 해주기 때문에 사실상 pm2 사용은 필수라고 할 수 있다.
구체적으로 pm2를 활용하는 방법은 다음과 같다
npm i pm2
pm2 start [실행할 파일]
"scripts": {
"dev": "nodemon --exec babel-node app.js",
"start": "cross-env NODE_ENV=production PORT=80 pm2 start app.js"
}
실행 결과는 다음과 같다 (코드에 오류 있어서 status가 online이 아니라 stopped 되었다)
여러개의 CPU 코어를 사용하고 싶다면 뒤에 -i [코어 갯수]를 적어주면 된다.
코어 갯수를 모른다면 0을 입력한다. CPU 코어 갯수를 자동으로 알아보고 그 만큼 프로세스를 생성해준다.
-1을 입력하면 최대 코어 갯수에서 하나를 제외한 만큼 프로세스를 생성합니다.
"start": "cross-env NODE_ENV=production PORT=80 pm2 start app.js -i 6"
여러 개의 CPU 코어를 사용하면 mode가 cluster가 된다. (코드에 에러가 있어서 status가 stopped가 되었다)
pm2의 명령어를 간단하게 알아보자면 (리눅스, 맥 환경에서는 sudo 명령어를 붙여야 합니다. 1024 이하의 포트를 사용하기 때문입니다)
- pm2 restart all : pm2 재시작
- pm2 list : 현재 프로세스 정보가 표시됩니다.
- pm2 kill : pm2를 종료함. 나중에 다시 키고 싶으면 pm2 start app.js로 키던가 package.json의 script를 이용해 npm start로 켜도 됨.
- pm2
- pm2 monit
log, CPU 사용량 등을 조회할 수 있음
* pm2가 안 죽어요!
우선 npm i -g pm2를 통해 글로벌하게 pm2를 설치한 후 pm2 list를 통해 pm2가 무엇이 돌고 있는지 확인한 후 pm2 kill을 통해 죽여줍니다. pm2를 몇 개 돌렸는지 모르고 있다가 계속 돌아가 리소스를 낭비하는 경우가 많습니다.
pm2를 활용해 로그 기록 남기기
-e [로그 파일 명]
-o [로그 파일 명]
NODE_ENV=production pm2 start app.js -i 0 -e err.log -o out.log
아래와 같이 폴더를 만들고 내부에 정리하는 게 깔끔합니다.
물론 로그를 어디에 쌓아둘 것인지는 각자 판단해야 합니다. 정기적으로 clean up이 되는 폴더에 넣어준다던가, mnt한 외부 하드에 넣어준다던가 여러 방법이 있습니다.
저는 가벼운 테스트 로그는 소스 코드 폴더에 직접 저장하는 편입니다.
"pm2-dev": "pm2 start server.js -e testLog/err.log -o testLog/out.log --watch",
참고한 글)
'Node, Nest, Deno > 🚀 Node.js (+ Express)' 카테고리의 다른 글
Row level Node : js의 동작 방식부터 libuv와 event loop까지 (2) | 2020.11.18 |
---|---|
중앙 집중식 API 에러 핸들링 (탑레벨 fetching handler를 만들어라) (0) | 2020.09.17 |
노드 내장 모듈 util의 util.pomisify 사용하기 (0) | 2020.07.30 |
[koa] KOA 백엔드의 라우팅 폴더 구조 (0) | 2020.07.30 |
[koa] koa 살펴보기 (0) | 2020.07.29 |