Node.js 클러스터 모드로 멀티코어 활용하기
문제 상황
운영 중인 API 서버가 4코어 인스턴스에서 돌고 있는데, CPU 사용률이 한 코어에만 집중되는 현상을 발견했다. Node.js는 단일 스레드 기반이라 멀티코어를 제대로 활용하지 못하고 있었다.
클러스터 모드 적용
Node.js의 내장 cluster 모듈을 사용해 워커 프로세스를 생성하도록 수정했다.
const cluster = require('cluster');
const os = require('os');
const numCPUs = os.cpus().length;
if (cluster.isMaster) {
console.log(`마스터 프로세스 ${process.pid} 시작`);
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`워커 ${worker.process.pid} 종료`);
cluster.fork();
});
} else {
require('./app');
console.log(`워커 ${process.pid} 시작`);
}
무중단 재시작 구현
배포 시 SIGUSR2 시그널을 받으면 워커를 순차적으로 재시작하도록 처리했다.
if (cluster.isMaster) {
let isRestarting = false;
process.on('SIGUSR2', () => {
if (isRestarting) return;
isRestarting = true;
const workers = Object.values(cluster.workers);
const restartWorker = (index) => {
if (index >= workers.length) {
isRestarting = false;
return;
}
const worker = workers[index];
worker.disconnect();
cluster.once('listening', () => {
setTimeout(() => restartWorker(index + 1), 1000);
});
};
restartWorker(0);
});
}
결과
- CPU 사용률이 전체 코어에 고르게 분산됨
- 단일 워커 장애 시 자동으로 재시작되어 가용성 향상
- 배포 시 다운타임 없이 재시작 가능
주의사항
세션 스토어는 메모리가 아닌 Redis를 사용해야 워커 간 공유가 가능하다. 로컬 메모리 캐시도 워커마다 독립적이라는 점을 고려해야 한다.