[Spring] 부하 분산 & 서버 가용성을 위한 로드밸런싱

개요

 

[Spring] 테이블 조인 + 그룹 함수를 통한 정렬 쿼리 튜닝기 (with nGrinder 부하테스트)

개요 GitHub - sjiwon/study-with-me-be: 여기서 구해볼래? Backend Repository (Refactoring) 여기서 구해볼래? Backend Repository (Refactoring). Contribute to sjiwon/study-with-me-be development by creating an account on GitHub. github.com 본

sjiwon-dev.tistory.com

 

[Spring] ThreadPool & HikariCP 튜닝 (with nGrinder 부하테스트)

개요 [Spring] nGrinder 부하 테스트 1) 쿼리 튜닝 개요 GitHub - sjiwon/study-with-me-be: 여기서 구해볼래? Backend Repository (Refactoring) 여기서 구해볼래? Backend Repository (Refactoring). Contribute to sjiwon/study-with-me-be dev

sjiwon-dev.tistory.com

지금까지 Study With Me 서비스의 메인페이지 조회 쿼리 성능 향상을 위해서 튜닝을 진행하였고 프로젝트상에서 최적의 ThreadPool & HikariCP 옵션을 nGrinder 부하테스트를 통해서 찾아내었다

이제 또 다른 관점에서 프로젝트에 필요한 개선 포인트를 찾아보자

 

 

단일 인스턴스 API 서버의 문제점

현재 API 서버의 대략적인 요청 흐름이다

가장 심플한 아키텍처이긴 하지만 몇가지 문제점이 보인다

 

1. SPOF (Single Point Of Failure)

시스템의 단일 지점에 장애가 발생했을 경우 이 단일 지점으로 인해 시스템 전체가 마비되는 현상

현재 단일 EC2 인스턴스를 띄워서 Spring Application을 운영중이다

하지만 해당 인스턴스에 장애가 발생하면 어떻게 될까?

  • 과도한 부하로 인해 인스턴스가 다운
  • 하드웨어적, 네트워크, SW 버그로 인해 인스턴스가 마비
  • ...

 

결국 단일 EC2 인스턴스의 장애로 인해 전체 API 서버 시스템이 마비된다

이렇게 된다면 서버 장애로 인해 서비스를 일시적으로 제공할 수 없고 사용자들은 이탈할 것이다

 

2. 예측하기 어려운 트래픽 대응

트래픽은 사용자의 접속 패턴, 이벤트, 마케팅 활동, ...등에 의한 요인으로 변동성이 굉장히 크다

특히 대규모 프로모션, 특정 이벤트 등 예측하지 못한 상황에서 급증한 트래픽이 몰리게 된다면 단일 EC2 인스턴스로는 감당하기 어려울 수 있다

 

마찬가지로 이러한 트래픽 증가에 대비하지 못하면 서버가 과부하 상태에 빠져 서비스가 중단되거나 응답시간이 크게 느려질 수 있다

따라서 사용자의 경험을 저하시키고 서비스를 이탈할 수 있다

 

 

로드밸런싱

https://www.cloudns.net/blog/load-balancing/

위와 같이 예측할 수 없는 트래픽이 갑자기 몰려오거나 높은 수준의 서비스 가용성이 요구되는 상황에서는 서비스의 안전성을 위해서 부하를 분산시키기 위한 로드밸런싱의 도입이 필요하다

 

로드밸런싱이란 인바운드 성격의 네트워크 트래픽을 다수의 서버에 분산시키는 기술이다

이러한 로드밸런싱을 통해서 단일 서버에 집중되던 부하를 분산시키고 서버의 과부하 예방, 응답시간 개선, 높은 가용성을 제공할 수 있다

 

종류

(1) 라운드 로빈 & 가중 라운드 로빈

요청을 순차적으로 각 서버에 분산시키는 방식
  • 모든 서버가 동일한 용량을 가지고 있고 동일한 리소스를 소모한다는 가정하에 매우 균일한 방식이다

 

가중 라운드 로빈 방식은 기본적인 라운드 로빈 방식에 성능별로 가중치를 적용해서 더 높은 가중치를 가진 서버에 많은 요청을 보내는 방식이다

 

(2) 최소 연결 & 가중 최소 연결

현재 가장 적은 연결을 가진 서버에 우선적으로 분산시키는 방식
  • 서버간의 성능 차이가 존재하거나 요청마다 처리 시간이 크게 다른 경우 적합한 방식이다

 

(3) IP 해시

Client의 IP 주소를 해싱해서 특정 서버에 매핑해서 분산시키는 방식
  • 해싱 처리된 값을 기반으로 항상 동일한 서버로 매핑된다
  • 하지만 특정 서버에 부하가 집중될 수 있는 위험이 존재한다

 

(4) 최소 대기 시간

서버의 응답 시간 & 연결 수를 모두 고려해서 최적의 서버에 요청을 분산시키는 방식
  • 가장 빠른 응답 시간을 가지고 가장 적은 연결을 가진 서버가 우선순위로 고려된다

 

L4 vs L7

L4 로드밸런싱 → ex) NLB

IP 주소 + 포트번호를 기반으로 트래픽을 분산시키는 방식
  • 패킷의 헤더정보만을 보고 빠르게 어떤 서버로 요청을 분산시킬지 결정할 수 있기 때문에 높은 처리량 & 낮은 지연 시간을 제공한다
  • 다만 헤더정보만 살펴보고 패킷의 세부 내용은 살펴보지 않기 때문에 L7에 비해 세밀한 로드밸런싱 전략을 수립하기는 힘들다

 

L7 로드밸런싱 → ex) ALB

Application Layer의 정보(HTTP 헤더, 메소드, URL, 쿠키, ...)를 기반으로 트래픽을 분산시키는 방식
  • 트래픽의 세부적인 내용을 이해하고 이를 기반으로 세밀한 로드밸런싱 전략을 수립할 수 있다
  • 세밀하기 때문에 L4에 비해서는 성능이 약간 떨어진다

 

본 프로젝트에서는 EC2 API 서버 앞단에 ALB를 두고 EC2 인스턴스에 대한 Scale Out을 적용해서 로드밸런싱을 진행할 예정이다

  • ALB는 기본적으로 라운드 로빈 알고리즘을 통해서 부하를 분산시키고 최소 해결을 통해서도 분산시킬 수 있다

 

적용 전/후 비교

적용 전

 

적용 후

 

결과

  EC2 인스턴스 1대 EC2 인스턴스 2대
VUser 100 TPS = 309.9 (max 456.5)
응답 시간 = 321.65
테스트 횟수 = 92,046
TPS = 325.3 (max 430.0)
응답 시간 = 306.45
테스트 횟수 = 96,666
VUser 200 TPS = 360.9 (max 464.0)
응답 시간 = 553.38
테스트 횟수 = 106,645
TPS = 472.4 (max 683.5)
응답 시간 = 422.54
테스트 횟수 = 139,543
VUser 300 TPS = 351.1 (max 437.0)
응답 시간 = 851.69
테스트 횟수 = 103,692
TPS = 611.8 (max 779.5)
응답 시간 = 486.45
테스트 횟수 = 179,903
VUser 400 TPS = 350.4 (max 416.0)
응답 시간 = 1139.45
테스트 횟수 = 103,469
TPS = 615.7 (max 810.0)
응답 시간 = 640.47
테스트 횟수 = 180,409
VUser 500 TPS = 355.0 (max 428.5)
응답 시간 = 1403.72
테스트 횟수 = 104,180
TPS = 570.4 (max 833.0)
응답 시간 = 850.04
테스트 횟수 = 166,759
MTT는 비교적 만족스럽지 못하긴 하지만 그래도 EC2 1대만 추가해도 전체적인 TPS, 응답 시간, 테스트 횟수 지표가 꽤 좋아짐을 확인할 수 있다

 

이로써 로드 밸런싱을 통해 특정 서버에 집중되던 부하를 분산시키면 서버의 가용성과 확장성이 향상되며, 처리 속도와 처리량 등에서도 이점을 얻을 수 있다