번개애비의 라이프스톼일

서비스구조개선을 통한 웹(앱)서비스의 대용량 트래픽처리 과정 본문

IT

서비스구조개선을 통한 웹(앱)서비스의 대용량 트래픽처리 과정

번개애비 2025. 3. 20. 14:41

이번 포스팅은 대용량 트래픽을 처리하기 위한 서버의 구성,

나아가 서버의 변경되는 구성에 따른 전체적인 시스템 구조개선에 대해 다뤄본다.

 

이 글은 아직 대용량 트래픽을 후두려맞기 전,

커피 한 잔의 여유를 갖고 있는 초심자를 위한 바이블이다. 

(내용 중간중간에 챗GPT는 알려주지 않는 현업꿀팁들이 숨어 있으니 한번쯤 읽어보길 추천)


 

1. 스케일업 (Scale-Up)

대용량 트래픽을 처리하는 가장 간단한 방법이면서, 가장 값비싸고, 한계가 뚜렷한 방법이다.

대용량 트래픽이 유입되는 양이 향후 지속적이지 않을것이 명백할 경우, 추천하는 방법이다.

(왜냐면 별도의 서비스 구조를 변경할 필요가 없고, 서버사양을 변경하기 위한 잠시의 다운타임만 감수하기 때문이다.)

네이버클라우드의 Standard-g3 Server의 사양별 가격표

엄청 쉽다.

현재 사용하고 있는 클라우드 서버의 사양을 더 비싼 사양으로 업그레이드 하면 된다.

단, 물리적인 Dedicated서버를 사용하고 있다면, 더 좋은 사양의 서버로 별도의 마이그레이션을 해줘야 하는 번거로움이 있다.  :(

본인이 대용량트래픽을 처음 겪어 본다면, 일단 서버를 Scale-Up부터 해보고 서버를 모니터링 해보자.

 

2. 웹서버와 데이터베이스서버의 분리를 통한 부하분산

서비스 또는 개발한 사람의 성향(?)에 따라 다르지만 보통 웹서버측의 부하보다 데이터베이스 부하가 통상적으로 더 높다.

때문에 하나의 단일서버에서 동작하는 웹과 데이터베이스를 별도의 서버로 분산해준다.

서비스의 형태와 개발방식이 유별나게 데이터베이스부하가 높다면 한번쯤(?) 생각해 볼 법한 부하분산이다.


1대의 서버가 해야되는 일을 2대의 서버가 해야함으로 Scale-Up을 통한 사양을 확장에 필요한 돈을 더 많이 쓸 수 있다(?) ㅋㅋ

(Ncloud기준으로 s64-g3서버를 2대쓰면 한달에 무려 5,620,480원!)

하지만, 이렇게 웹서버와 데이터베이스서버를 분리했음에도 불구하고 부하를 감당하지 못할경우, 다른 방식을 고려해야되며, 가끔 서버 모니터링에서는 부하가 낮게 감지 되지 않지만, 접속이 버벅인다면 웹서비스와 데이터베이스간 통신도 TCP를 통해 이뤄진다는 사실을 명심하고 한번 더 체크해보자

학부생때 배우는 네트워크의 TCP통신

 

3. L4나 DNS를 활용한 웹서버의 확장

여기서부터는 웹서버에서 관리되는 세션정보를 데이터베이스에서 통합관리되어야 한다.

보통은 클라이언트의 세션정보를 MySQL에 때려박거나 In-memory기반의 Redis으로 세션을 관리하면 된다.

 

웹서버를 확장하기 위해 보통 L7 Proxy방식의 HA-Proxy나 Nginx를 많이들 다루는데 NAT방식의 하드웨어 밸런서에 비해 일정수준이상에서는 버벅임으로 여기서는 논외로 하고 L4방식의 로드밸런서나 DNS를 활용한 웹서버 Scale-Out을 다룬다.

(가끔 도커나 EC2에 Nginx Proxy로 로드밸런서 흉내를 내는 경우가 있는데 AWS에서는 닥치고 Elastic Load Balancer를 쓰거나 로드밸런서 장비를 쓰자)

알테온 5224 L4로드밸런서

 

국내에서는 주로 알테온 라드웨어 제품군의 L4를 많이 사용하는 추세이고, 그 밖에 제품군에서는  F5의 BIG-IP, 시트릭사의 NetScaler등이 있다.

 

DNS자체만으로도 일부 Scale-Out이 가능한데, 하나의 도메인에 여러 웹서비스 아이피를 호스트A 지정하는 방법이다.
하나의 도메인에 여러 호스트A를 지정하게 될 경우, DNS에 질의가 들어올때 Round-Robin에 의해 여러 호스트A중 하나의 호스트만을 랜덤하게 제공하여 마치 부하분산과 같은 효과를 보여준다.

문제는 이 Round-Robin방식은 정확하게 랜덤한 방식이 아닐뿐더러 서버의 다운을 체크하는 Health check등의 기능이 부재하여 완벽한 Scale-Out의 방법은 아니나, 구글과 같이 대규모 트래픽을 대응해야하는 기업에서는 DNS Round-Robin을 적용하고 뒷단에 L4까지 둬서 처리하기도 한다.

DNS질의 때 마다 웹서비스의 아이피주소가 이렇게 계속 변경된다.

 

4. 데이터베이스의 리플리케이션을 통한 DB서버의 확장

2번에서 언급한것처럼 통상적인 서비스는 웹서비스보다 데이터베이스의 부하가 더 높다.

때문에 데이터베이스서버를 Scale-Out하여 리플리케이션 확장하는 방법이 있다.

 

이 방식을 적용하기 위해서는 제공되는 서비스의 코드에서 데이터베이스를 연계할때 입력되는 데이터와 읽는 데이터를 구분할 수 있도록 조치를 해줘야 적용이 가능하다. 입력 또는 수정 데이터는 Master노드에 읽기데이터는 Slave노드를 통해 데이터베이스 쿼리를 실행할 수 있도록 하면 되며, 통상적으로 MySQL(MariaDB)의 리플리케이션의 경우 1개의 Master노드에 n개의 Slave노드들을 두어 사용한다. 

MySQL Replication의 예

 

이와 같이 데이터베이스 리플리케이션을 구성했을때, Master노드에 데이터가 신규로 추가되거나 기존데이터가 수정되면 데이터베이스서버가 알아서 각 Slave노드들에게 추가되거나 변경된 데이터를 전파한다. 

문제는 쓰기와 수정의 부하가 많아져 Master노드의 부하가 증가하게 되면 Slave노드로의 데이터 동기화가 지연될수 있는 문제가 있음으로 실시간성 데이터쿼리에 대해 정책을 잘 고려해야한다.

 

5. 데이터베이스 샤딩을 통한 DB서버의 추가 확장

데이터베이스 마이그레이션을 통해 조회용 데이터베이스의 Scale-Out을 했음에도 불구하고 아직도 데이터베이스의 부하를 개선하지 못했다면, 코드기반의 논리적인 방법으로 데이터를 분산저장하는 형태로 Scale-Out을 고려해야한다.

테이블단위, 데이터범위, 해시값등의 샤딩할 데이터의 기준점을 잡고, 각각의 데이터의 추가와 수정을 특정 데이터베이스 서버가 담당하는 형식으로 분담하는 형태로 샤딩을 진행할 수 있다.

 

 

샤딩된 각 데이터베이스 마다 리플리케이션을 통해 Master-Slave node로 추가적인 Scale-Out을 진행할 수 있으며, 아예 Master node 앞단에 L4를 두어 Master 노드자체를 Scale-Out할 수 있다.

 

6. 나도 대기업처럼 MSA

기능단위로 인프라 자체를 쪼개기 때문에 (예를 들면, 구매페이지, 장바구니, 회원기능등)

각 서비스에 필요한 부하만큼 Scale-Up을 하던 Scale-Out을 하던 개떡같이 짜던 맘대로 하면 된다.

쿠버네티스를 활용한 컨테이너를 통합 관리하고 카프카를 통해 메시지큐를 여러 서비스간 고속으로 송수신하는...

이러한 행위들을 하게 된 이유가 Micro Service Architecture를 위해서이다.

MSA는 각 서비스별 특화된 Scale-Out을 위한 목적도 있지만, 서비스가 너무나 복잡해져서 이제는 과거의 똥코드들을 신입에게 알려주기 힘들어지는 시기가 올때 대부분 전환하는것 같다. 정확하게 표현하자면, 각 서비스가 갖고 있는 레거시를 인정하는 상태로 부분적 마이그레이션 형식으로 운영한다는 것을 의미한다.

엄청나게 세분화된 기능단위별로 독립적인 마이크로서비스를 운영할 수 있기 때문에 이러한 서비스구조를 운영하기 위해서는

기업의 규모가 일정이상 되어야 하고 담당업무별로 체계적인 매뉴얼과 커뮤니케이션 능력이 갖춰져 있어야 한다.

사실, 여기서부터는 개발업무보다 서비스를 운영/관리하는 업무가 더 많을 때가 대부분이고, 이것을 포장하기 위한 용어로는 DevOps가 있다. (그리고, 수백명의 개발자가 없는 대부분의 상황에서는 MSA를 운영하고 유지하는게 배보다 배꼽이 더 크다는 것을 명심하자)

 

많은 데브콘에서 MSA가 마치 모던한 것처럼 표현하지만, MSA로 전환되는 순간 모놀리식 서비스에서 경험하지 못했던 서비스간 네트워크 지연 또는 실패, 일관성과의 싸움이 시작된다는 것을 명심하자. 

 

7. 상남자의 Serverless

진정한 클라우드 네이티브다.

서버리스라고 해서 서버가 없는건 아니고, 너가 서버를 직접 관리하지 않아도 된다는 뜻이다.

AWS사용자라면 API Gateway와 AWS Lambda를 활용하면 된다.

 

서버리스 구성의 예 (출처 : https://devocean.sk.com/blog/techBoardDetail.do?ID=163934)

 

평소에 코드단위로 작성된 Lambda는 잠을 자고 있지만, Request가 발생되면 알아서 AWS가 Lambda의 코드를 실행하고 다시 자러 가는 원리이다. 덕분에 접속자 수에 비례하게 비용이 절감되는 강점이 있지만, Lamdba코드가 깨어나서 실행되기까지의 Cold Start문제가 있다. (근데 이 문제는 Provisioned Concurrency를 사용하거나 일정주기로 ping을 날려서 해결할 수 있음!)

 

코드만 신경쓰면되고 그 외 모든 것들을 클라우드서비스업체에 일임한 방식임으로 차후 Dedicate서버로 전환하거나 다른 클라우드서비스로 마이그레이션이 굉장히 힘들어지는 종속화되는 것이 단점이다.

 

그 밖에 잡기술들

- HTTP/1.1 → HTTP/2 H2 → HTTP/3 QUIC 와 같이 웹서비스의 프로토콜 변경

- Cache-Control 및 ETag를 활용한 정적리소스를 캐싱

- CSS Srpite를 활용하여 전송되는 정적데이터양을 최소화

- SSG데이터 자체를 CDN을 통해서 서비스

- WebP, AVIF등의 차세대 포맷을 통해 정적이미지의 파일 크기 최소화

- Lazy loading을 통해 실제 화면에 보여지는 이미지만 로드하도록 변경

- Preload, Prefetch를 통해 사전 로드

- 인덱싱 기반의 쿼리를 사용하고 N+1 문제를 해결

- 기존 데이터베이스 앞단에 데이터자체에 대한 데이터베이스 캐싱

- API뿐만 아니라 쿼리 Level에서 페이지네이션기반으로 설계

- 입장대기페이지 (정말 최악의 시나리오)

 


 

이번에 소개한 대용량 트래픽처리에 대한 방법들은 각자가 제공하는 서비스의 유형, 트래픽의 특성, 인프라 운영에 대한 경험등 여러가지를 고려해서 선택해야한다.

 

조직의 형태, 서비스 유형에 따라 조금씩 다르지만 대규모트래픽에 대해 점진적으로 대응하는 예시로는 아래와 같다.

(트래픽에 대응하기 위해 각 단계를 일부 생략하여 진행하기도 한다.)

 

 

 DB측 부하집중형 서비스에서는 

Scale-up → WAS + DB 분리하여 DB replicationDB 샤딩DB단 L4 로드밸런싱Kafka 도입하여 트랜젝션분리

 

WAS측 부하집중형 또는 파편화된 WAS 서비스에서는

Scale-up → WAS + DB 분리하여 WAS단 L4 로드밸런싱 Serverless 도입

 

 

 

어떤 방법이 정답이고, 어떤 방법이 잘못되었고를 따지지 않고

현재 처한 환경과 대응할 수 있는 범위의 가능한 방법을 종합적으로 판단하여

백만, 천만 유저를 처리할 수 있는 인프라를 구성해보자.

 

이 고민을 하는 시기가 오면 사실 행복한 고민이긴하다.

그만큼 유저가 많다는 방증이니

Comments