본문 바로가기
Project/SpringBoot

NginX를 적용한 로드밸런싱 적용 및 웹 소켓 로드 밸런싱

by 꽃요미 2025. 1. 31.

1️⃣ NginX의 특징

 

✅ 1. 비동기 이벤트 기반 아키텍처

 - NginX는 이동기 이벤트 기반(Asynchronous, Event-driven) 모델을 사용하여 높은 성능을 제공

 - Apache(쓰레드 기반)와 달리, 하나의 워커 프로세스가 비동기적으로 다수의 요청 처리 가능

 - 결과적으로 동시에 많은 요청을 처리할 수 있어 CPU와 메모리 사용량이 적음

 

✅ 2. 정적 콘텐츠(HTML, CSS, JS) 제공 최적화

 - NginX는 정적 파일(HTML, CSS, JS 이미지 등) 제공이 빠름

 - 캐싱 기능을 활용하면 서버 부하를 줄이고 응답 속도를 개선 가능

 

✅ 3. Reverse Proxy & Load Balancing 지원

 - 리버스 프록싱(Reverse Proxy) : 클라이언트의 요청을 받아서 백엔드 서버로 전달하고, 응답을 클라이언트에 반환

 - 로드 밸런싱(Load Balancing) : 여러 개의 백엔드 서버에 트래픽을 분산하여 부하를 줄이는 역할

 

✅ 4. 보안 및 HTTPS 지원

 - SSL/TLS를 사용하여 HTTPS 적용 가능.

 - DDos 공격 방어 및 IP 차단 설정 가능.

 

✅ 5. 마이크로서비스 & 컨테이너와의 호환성

 - Docker, Kubernetes 등의 환경에서 API Gateway 역할 수행

 - 마이크로서비스 아키텍처에서 서비스 간 트래픽을 관리하는 역할 수행

 

 

위 같은 기능중 로드 밸런싱을 적용해 보겠다.

 

✅ 로드 밸런싱이란?

 - 사용자의 요청을 여러 개의 서버( 백엔드 서버 ) 로 분산하는 기술.

 * 장점

  - 성능 향상 : 하나의 서버가 감당하는 부하를 줄여 성능 최적화

  - 가용성 증가 : 특정 서버가 다운되더라도 다르서버가 서비스 유지

  - 확장성 확보 : 트래픽 증가 시 서버 추가 가능

 

✅ NginX에서 지원하는 로드 밸런싱 방식

 1. Round Robin ( 기본 값 ) -> 서버에 순차적으로 요청 분배

 2. Least Connections -> 현재 연결이 가장 적은 서버에 요청 전달

 3. IP Hash -> 클라이언트의 ip를 해싱하여 해싱 값을 이용해 로드밸런싱하여 항상 같은 서버로 전달

 4. Weighted Load Balancing -> 성능이 다른 서버를 이용할 때 특정 서버에 가중치를 두어 더 많은 트래픽 할당 가능

 

📌 2. 기본적인 NginX 로드 밸런싱 설정

 - NginX는 기본적으로 Reverse Proxy 기능을 활용하여 로드 밸런싱을 수행한다.

 

🔹 1️⃣ NginX 설치

 

Ubuntu에서 NginX 설치

sudo apt update
sudo apt install nginx -y

 

설치 후 NginX 상태 확인

sudo systemctl status nginx

 

🔹 2️⃣ nginx.conf 설정 파일 수정

sudo nano /etc/nginx/nginx.conf

 

아래와 같이 설정을 추가

 

✅ 기본 Round Robin 방식 (순차적 요청 분배)

 

http {
    upstream backend_servers {
        server 192.168.1.101:8080; # 첫 번째 서버
        server 192.168.1.102:8080; # 두 번째 서버
        server 192.168.1.103:8080; # 세 번째 서버
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://backend_servers;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

 

- 배포할 EC2의 PrivateIP를 작성해주면 로드밸런싱이 적용된다.

 

📌 설명

- upstream backed_servers : 여러 개의 백엔드 서버를 그룹으로 정의.

- server IP:PORT : 특정 IP와 포트를 가진 서버 추가

- proxy_pass http://backed_servers; 클라이언트의 요청을 백엔드 서버 그룹으로 전달

 

📌 3. 로드 밸런싱 방식 변경

 

✅ 1) Least Connections (연결이 가장 적은 서버에 배분)

✅ 2) IP Hash (사용자의 IP에 따라 고정 서버 배정)

✅ 3) 가중치 기반 로드 밸런싱 (서버 성능에 따라 트래픽 차등 배분)

 

📌 4. 헬스 체크 (서버 상태 감지)

upstream backend_servers {
    server 192.168.1.101:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.102:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.103:8080 max_fails=3 fail_timeout=30s;
}

 

📌 설정 설명

max_fails=3 → 3번 연속 실패하면 해당 서버 제외

fail_timeout=30s → 30초 동안 서버 응답 확인 후 다시 추가

 

 

* 현재 프로젝트에서 적용중인 로드밸런싱 형태이다.

- nginx 인스턴스에 NginX를 적용하여 quiz, quiz2 인스턴스에 로드밸런싱을 적용한 이미지이다.

 

 

* 웹 소켓 로드 밸런싱 ( Trouble ... )

  문제 상황

- 사용자( user )는 예시 사진처럼 분산 접속이 되는 상황이다.

- roomlist 경로는 roundrobin 방식 로드 밸런싱 적용.

- room 경로는 roomid 값을 기준으로 iphash 로드 밸런싱을 적용.

- quiz( 인게임 ) 경도 room 경로와 마찬가지로 roomid 기준으로  iphash 로드 밸런싱 적용.

대기방까지 들어가는 과정 도식화

 

roomId로 해쉬 후 대기방에 접속 후 webSocket과 user 서버가 갈라진 시나리오

 

-> 문제의 상황은 사용자는 iphash 방식으로 로드 밸런싱이 잘 적용되지만, websocket 구독경로가 랜덤으로 로드 밸런싱 됨.

-> 위 시나리오는 WebSocket 로드 밸런싱이 사용자와 일치 하지 않았을때 시나리오다.

-> 즉 사용자의 접속 서버 != websocket 접속 서버.

-> websocket으로 구현된 모든기능 ( Ex. 준비, 시작, 참가자 갱신 ) 이 작동이 안된다.

 

* 위 상황을 해결하기 위해서 websocket의 연결 경로인 /game을 로드 밸런싱 하기로 했다.

const socket = new SockJS("/game");

 

- 첫 javascript가 실행될때 연결을 하는 경로이다. "/game".

 

location /game {
            proxy_pass http://roomId_based_servers;
            access_log /usr/local/nginx/logs/room_access.log upstream_log;
    }

 

- nginX의 /usr/local/nginx/conf/sites-available 위 경로에 있는 conf 파일에 roomId로 로드 밸런싱을 한번 더 해주었다.

- Controller에서 요청을 보내고 있는 url인 room으로 보내는것이 아닌 websocket 연결 경로인 /game 에 해주었다.

- 결론적으로 대기방은 적용이 되는데, 인게임은 Troble Shooting 중이다... ( 해결 완료. 아래 글 참조 )

 

#### websocket 로드 밸런싱 문제 해결 과정

* 각각의 js 마다 연결되어있는 websocket의 경로가 /game으로 같았다. ( 위 코드 const socket = new SockJS("/game")' 와 같이. )

* 게임방 프로젝트가 크게 3개의 화면으로 이루어져있다.

 1. 방 리스트를 나타내는 room ( js, html ) 의 2개의 파일이 존재한다.

 2. 대기방의 상태를 보여주는 game ( js, html ) 마찬가지로 2개의 파일이 존재한다.

 3. 인게임 화면을 보여주는 quiz ( js, html ) 2개의 파일이 존재한다.

* 원래는 각각의 소켓이 첫줄에 적혀있는데로 "/game"과 같았다.

* 방 리스트 -> 대기방 -> 인게임 이동할때마다 같은 "/game" 경로로 끊고 재연결해서 로드 밸런싱이 제대로 이루어지지 않은것 같다. 

* 현재는 방 리스트 ( "/updateOccupancy" ) , 대기방 ( "/game" ) , 인게임 ( "/ingame" ) + roomId 방 번호를 추가했다.

* roomId를 추가한 이유는 NginX가 roomId 기준으로 같은 EC2 인스턴스로 로드 밸런싱을 하기 때문이다.

* 현재 NginX 디렉터리의 /sistes-available/ 디렉터리 안에있는 dandee.dev 파일의 설정도 js 맞게 변경했다.

 1. 방 리스트는 Hash 해줄 필요없이 roundrobin 방식으로 처리하였다.

 2. 대기방은 "/game"으로 연결한 만큼 game 경로로 방 번호를 Hash 해서 로드 밸런싱 하였다.

 3. 인게임은 "/quiz"경로로 방번호를 Hash 해서 로드 밸런싱 하였다.

 

* 결과

 - 룸리스트, 대기방, 인게임 로드 밸런싱 적용되서 websocket 기능이 정상 작동한다!