pm2 클러스터링 with redis

Jaymee·2021년 10월 1일
0

yeomanda 프로젝트를 진행중에 socketio를 통해 채팅 기능을 구현하고 있었다. 1대1 대화는 딱히 상관없지만 추후에 많은 이용자들이 있을 경우에는 서버가 터지는 경우가 생길 수도 있겠다는 생각에 scale out을 구현하고자 하였다.

scale out을 구현하는데 있어 방법은 두가지이다.

  • clustering
  • load balancing

로드밸런싱

서버과부하를 막기 위해 가상 ip를 통해 여러 서버에 접속하도록 하는 분배 기술

여러 서버들이 많은 트래픽을 분산처리하여 효율을 높여주는 기술이다. 이러한 기술을 구현하는 소프트웨어 혹은 하드웨어 장치를 로드밸런서라고 한다.
로드밸런서를 구현할 때 가장 어려운 점은 바로 세션데이터를 관리하는 것이다.

클라이언트의 요청을 여러 서버에서 담당하여 관리하다 보면 세션을 공유해야 하는 일이 생긴다. 그렇지 않으면, 로그인을 하고 다른 요청을 보낼 시 또 다시 로그인을 해야 하는 경우도 생길 수 있기 때문이다.

이러한 문제를 해결하는 방법으로는 대표적으로 세션고정(session sticky), 세션 클러스터링(session clustering) 이 있다.

session sticky

처음 요청을 보낸 서버한테만 계속 요청을 보내는 방법
특정 서버에게만 과부하가 생길 수 있다.

session clustering

하나의 세션을 모든 서버에서 클러스터링하여 관리하는 방법
하나의 서버에서 오류가 생기면 해당 세션을 다른 서버에서 관리할 수 있다.


클러스터링

여러개의 컴퓨터를 병렬로 연결하여 마치 하나의 컴퓨터처럼 사용하는 것

nodejs에서 클러스터링을 구현할 수 있다. nodejs의 cluster 내장 모듈을 사용할 수 있다.

var cluster = require('cluster')
var numCPUs = require('os').cpus().length;

if(cluster.isMaster){
   for (var i = 0; i < numCPUs; i++) {
     cluster.fork();
   }
   cluster.on('exit', function(worker, code, signal) { // 워커가 종료되었을 때
     console.log('worker ' + worker.process.pid + ' died');
     console.log('code' , code , 'signal', signal)
     cluster.fork(); // 워커가 죽어도 다시 생성
   });
 }
 else{
   var server = http.createServer(app);
   app.io.attach(server)
   server.listen(port);
   setTimeout(() => { //워커가 존재하는지 확인하기 위해 1초마다 강제 종료
   		process.exit(1);
	}, 1000);
   server.on('error', onError);
   server.on('listening', onListening);
   console.log(`${process.pid} 번 워커 실행중...`)
 }

하지만 실무에서는 강력한 pm2를 사용한다고 한다.
pm2를 사용하여 클러스터링을 구현하려면

module.exports = {
  apps: [{
    name: 'www',
    script: './bin/www', // 서버 시작 스트립트
    instances: 0,
    exec_mode: 'cluster'
  }]
}

instance는 구동 가능한 프로세스의 갯수를 말하는 것이고, 0으로 세팅하면 사용 가능한 모든 프로세스를 이용한다는 것이다.
exec_mode: 'cluster'는 클러스터 모드로 pm2를 돌린다는 것.


redis

이렇게 클러스터링 된 서버를 이용하면 위에서 말한 것 처럼 문제가 생길 수 있다.
여러 이용자가 서버에 접속을 하는데, 같은 이용자임을 확인 못 하는 불상사가 발생할 수 있다.
이러한 문제를 해결하기 위해 nodejs에서는 redis server를 사용한다. 이는 여러 곳으로 나눠진 클러스터링을 하나로 묶어서 관리해주는 도구이다.
이렇게 되면 서버를 분산시켜도 하나의 서버를 사용하는 것처럼 할 수 있다.

const io = require( "socket.io" )();
const socketapi = {
    io: io
};

const redis = require('socket.io-redis');
io.adapter(redis({
    host : 'localhost',
    port : 6379
}))

대신 서버 구동 환경에서 미리 redis-server를 설치하고, 직접 redis server를 돌린다음에 nodejs서버를 돌려야 한다.
우분투에서 redis server 돌리기


결과

2021년 7월 postman의 최고의 기술인 socketio를 통해 테스트를 해보았다. 여러 계정으로 로그인하여 메세지를 보내는데 여러 프로세스를 통해서 메세지를 보내는 것을 알 수 있다.


참고

https://hayden-archive.tistory.com/429
https://tableplus.com/blog/2018/10/how-to-start-stop-restart-redis.html
https://socket.io/docs/v4/redis-adapter/

profile
backend developer

0개의 댓글