본문 바로가기

개발

Node js 웹서버 부하 분산 및 클러스터링

웹 서버는 클라이언트의 요청을 받아서 처리하고 응답하는 역할을 합니다. 하지만 웹 서버가 단일 프로세스로 동작한다면, 요청이 많아지거나 복잡해지면 성능이 저하되거나 다운될 수 있습니다. 이를 해결하기 위해 클러스터링과 부하 분산이라는 기법을 사용할 수 있습니다.

 

클러스터링이란?

클러스터링이란 여러 개의 프로세스나 컴퓨터를 하나의 그룹으로 묶어서 작업을 분산시키는 것입니다. Node.js에서는 cluster 모듈을 사용하여 클러스터링을 구현할 수 있습니다. cluster 모듈은 마스터 프로세스와 워커 프로세스로 구성되어 있습니다. 마스터 프로세스는 워커 프로세스를 생성하고 관리하는 역할을 하며, 워커 프로세스는 실제로 요청을 처리하는 역할을 합니다.

 

클러스터링의 장점은 다음과 같습니다.

- CPU 코어의 수만큼 워커 프로세스를 생성하여 멀티 코어 시스템의 성능을 향상시킬 수 있습니다.

- 워커 프로세스가 하나라도 다운되면 다른 워커 프로세스가 요청을 처리할 수 있으므로 서비스의 안정성을 높일 수 있습니다.

- 마스터 프로세스와 워커 프로세스간에 메시지를 주고받을 수 있으므로 통신이 가능합니다.

 

부하 분산이란?

부하 분산이란 여러 개의 서버에 요청을 균등하게 나눠주는 것입니다. 부하 분산을 위해서는 **로드 밸런서**라는 장치나 소프트웨어가 필요합니다. 로드 밸런서는 클라이언트의 요청을 받아서 여러 개의 서버 중에서 하나를 선택하여 전달하는 역할을 합니다. 로드 밸런서는 서버의 상태를 모니터링하고, 가장 적절한 서버를 선택하는 알고리즘을 사용합니다.

부하 분산의 장점은 다음과 같습니다.

- 단일 서버보다 여러 개의 서버가 요청을 처리하므로 성능이 향상됩니다.

- 한 대의 서버가 다운되어도 다른 서버가 요청을 처리할 수 있으므로 서비스의 안정성이 높아집니다.

- 서버의 수를 증가시키거나 감소시키는 것으로 확장성이 좋아집니다.

 

Node.js에서 클러스터링과 부하 분산 구현하기

Node.js에서 클러스터링과 부하 분산을 구현하는 방법은 여러 가지가 있습니다. 여기서는 간단한 예제를 통해 설명하겠습니다. 다음은 cluster 모듈을 사용하여 간단한 웹 서버를 클러스터링하는 예제입니다.

// cluster 모듈 불러오기
const cluster = require('cluster');
// CPU 코어의 수 구하기
const numCPUs = require('os').cpus().length;

// 마스터 프로세스일 경우
if (cluster.isMaster) {
  console.log(`마스터 프로세스 아이디: ${process.pid}`);
  // CPU 코어의 수만큼 워커 프로세스 생성
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
  // 워커 프로세스가 종료될 경우
  cluster.on('exit', (worker, code, signal) => {
    console.log(`${worker.process.pid}번 워커가 종료되었습니다.`);
    // 새로운 워커 프로세스 생성
    cluster.fork();
  });
} else {
  // 워커 프로세스일 경우
  // 웹 서버 생성
  const http = require('http');
  const server = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.write('<h1>Hello Node!</h1>');
    res.end('<p>Hello Cluster!</p>');
    // CPU 사용률을 높이기 위한 작업
    setTimeout(() => {
      process.exit(1);
    }, 1000);
  }).listen(8080);
  console.log(`${process.pid}번 워커 실행`);
}

 

이 예제에서는 CPU 코어의 수만큼 워커 프로세스를 생성하고, 각 워커 프로세스가 웹 서버를 실행합니다. 웹 서버는 요청을 받으면 "Hello Node!"와 "Hello Cluster!"라는 메시지를 응답하고, CPU 사용률을 높이기 위해 1초 후에 종료됩니다. 마스터 프로세스는 워커 프로세스가 종료되면 새로운 워커 프로세스를 생성합니다.

 

부하 분산 예제

다음은 Node.js에서 부하 분산을 구현하는 방법 중 하나인 nginx를 사용하는 예제입니다. nginx는 고성능의 웹 서버이자 로드 밸런서입니다. nginx를 설치하고 설정하는 방법은 다음과 같습니다.

1. nginx 공식 사이트에서 다운로드하고 설치합니다.

2. nginx.conf 파일을 열고 http 부분에 다음과 같은 내용을 추가합니다.

upstream node_cluster {
    server localhost:8081;
    server localhost:8082;
    server localhost:8083;
}

server {
    listen       80;
    server_name  localhost;

    location / {
        proxy_pass http://node_cluster;
    }
}

 

이 설정은 node_cluster라는 이름의 upstream을 정의하고, localhost의 8081, 8082, 8083 포트에 연결된 서버들을 등록합니다. 그리고 server 부분에서는 클라이언트의 요청을 받아서 node_cluster에게 전달하는 역할을 합니다.

3. nginx를 실행합니다.

4. 다음과 같은 코드로 간단한 웹 서버를 세 개 만들고, 각각 8081, 8082, 8083 포트에 연결합니다.

// http 모듈 불러오기
const http = require('http');
// 포트 번호 지정
const port = process.argv[2] || 8080;
// 웹 서버 생성
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.write(`<h1>${port}번 서버에 접속했습니다.</h1>`);
  res.end('<p>Bye!</p>');
}).listen(port);
console.log(`${port}번 서버 실행`);

 

이 예제에서는 nginx가 클라이언트의 요청을 받아서 8081, 8082, 8083 포트에 연결된 서버들 중에서 하나를 선택하여 전달하는 역할을 합니다. nginx는 기본적으로 라운드 로빈 방식으로 서버를 선택합니다. 즉, 순서대로 한 대씩 서버에 요청을 보내는 방식입니다. 이 외에도 가중치, IP 해시, 리스트 해시 등의 다른 방식도 사용할 수 있습니다.

 

이번 글에서는 Node.js를 활용한 웹 서버의 클러스터링과 부하 분산에 대해 알아보았습니다. 클러스터링은 여러 개의 프로세스를 생성하여 작업을 분산시키는 것이고, 부하 분산은 여러 개의 서버에 요청을 균등하게 나눠주는 것입니다. 이 두 가지 기법을 사용하면 웹 서버의 성능과 안정성을 향상시킬 수 있습니다. Node.js에서는 cluster 모듈과 nginx 등을 사용하여 간단하게 구현할 수 있습니다. 물론 실제로 운영하는 웹 서버에서는 더 많은 고려사항과 복잡성이 있을 수 있으므로, 자세한 내용은 공식 문서나 참고 자료를 통해 학습하시기 바랍니다.