요즘 개발 포스트나 공고등을 보다보면 항상 눈에 보이는 단어가 있다.
바로 "대규모 시스템 개발 경험"!
비슷한 말로 대규모 트래픽 처리 경험, 대용량 데이터 처리 경험, 확장성있는 구조 등등이 있다.
대체 대규모 시스템이란 얼마나 다른 구조를 갖고 있기에 실무에서 강조 또 강조하는것일까?
현재 나는 "빼곡"이라는 서비스를 운영하고 있긴 하지만, 아직 많은 이용자와 트래픽이 있는것은 아니기에 막상 경험해보긴 힘들었다. 따라서 책과 포스팅을 통해 개인적으로 학습해보고, 이를 토대로 아키텍처를 설계해보며 기업들이 강조하는 <대규모 시스템 설계>대해 알아보고, 성장하고자 한다.
cf. 주 참고자료는 한 면접을 준비하며 알게된 <가상 면접 사례로 배우는 대규모 시스템 설계 기초>라는 책이다.
초기 서비스
단일서버(클라이언트, 서버, DB)
요즘 큰규모의 서비스를 가진 회사들은 으레 MSA 아키텍처를 활용해 복잡한 비즈니스를 구현한다. 그리고 이런 복잡한 아키텍처를 살펴보기 전에, 우선 한대의 서버가 어떤식으로 커지는지부터 설계해보려 한다.
이 그림은 현재 운영중인 <빼곡>의 서버구조를 좀더 단순화 한 것이다.
1. bbaegok.store라는 도메인을 활용하여 웹사이트에 접속한다. DNS에 먼저 질의하여 IP주소를 응답받아 접속하는 과정이 필요하다.
2. DNS질의 결과로 공인IP주소가 반환된다(실제주소는 다르다ㅎㅎ)
3. 공인IP주소인 서버로 HTTP 요청이 전달된다.
4. 요청을 전달받은 서버는 데이터베이스와 통신하여 조회/저장/수정/삭제 등의 로직을 처리한다.
5. HTML이나 JSON형태의 응답을 반환한다.
(CF) 어떤 데이터베이스를 사용해야 하는가?
대표적으로 관계형 데이터베이스와 비관계형 데이터베이스가 있을것이다.
1. RDB(관계형 데이터베이스) - MySQL, OracleDB, PostgreSQL 등
자료를 테이블과 열, 칼럼으로 표현하고, SQL을 사용하면 여러 테이블의 데이터를 관계에 따라 조인해 사용한다.
2. NoSQL(비관계형 데이터베이스) - MongoDB, Redis
비관계형 데이터베이스는 내부적으로 네가지 카테고리로 또 나뉠 수 있다.
키-값 저장소 / 그래프 저장소 / 칼럼 저장소 / 문서 저장소가 있다. 이름에서 알수있듯, 관계를 맺는 조인연산을 제공하지 않는다.
일반적으로 처음 개발할땐 관계형 데이터베이스를 많이 사용하지만, 다양한 요구사항의 서비스를 구현하다보면 관계형데이터베이스로는 적합하지 않은 상황이 발생할것이다. 관계형 데이터가 아니거나, 굉장히 빠른 응답시간을 요구하거나, 대용량 데이터를 저장해야하는 경우 등이 해당한다.
Scale up VS Scale out
다시 아까 서비스로 돌아가서, 만약 내 서비스에 엄청나게 많은 사용자가 생겼다고 가정해보자.
자연히 요청도 많아질것이고, 서버의 부담이 기하급수적으로 커질것이다. 이때 해결방법으로 두가지가 떠오를 수 있다.
Scale Up(수직적 규모확장)
서버 성능 자체를 높여 동시 처리량을 증가시키는 방식이다. 트래픽의 양이 적을때 좋은 선택이며, 단순하다는 큰 장점이 있다. 더 좋은 CPU와 더 많은 RAM을 사용하면 되니까!
하지만 심각한 단점이 있는데, 만약 최고의 CPU보다도 트래픽이 높아진다면? 당연히 문제는 똑같아지는것이다. 한대의 서버에 CPU와 메모리를 무한증설하는것은 불가능하니까. 뿐만아니라, 장애가 발생했을때에 대한 복구방안이나 다중화 방안을 제시하지 않는다. 즉, 서버에 장애가 발생하면 서비스 이용이 불가능해져 버린다.
Scale Out(수평적 규모 확장)
서버를 여러개 만들어 병렬처리를 함으로써 요청을 분산처리하는 방식이다. 즉, 더 많은 서버를 추가함으로써 성능을 개선하는 방법이며, Scale Up에서 발생했던 문제들을 해결할 수 있으므로 대규모 애플리케이션에 적합하다.
그러면 여기서 의문이 들수있다. "서버가 여러대면, 요청은 어떻게 이 서버들로 요청을 전달하는것인가?"
서버가 여러대여도, 수많은 요청이 특정 서버에만 집중된다면 또다시 응답속도가 느려지고, 종국엔 서비스 이용이 불가능해질것이다. 따라서 요청을 적절하게 분산해 서버에 전달하는 역할, <로드밸런서>가 필요한 시점인것이다.
로드밸런서
로드밸런서는 부하 분산 집합에 속한 서버들에게 트래픽 부하를 고르게 분산하는 역할을 한다.
사용자는 로드밸런서의 공인 IP주소를 DNS를 통해 획득해 접속한다. 서버가 직접 처리하지 않고, 로드밸런서가 새로 개입해서 어떤 서버로 갈지 알려주는 역할을 하게 된다.
결론적으로, 로드밸런서를 활용하면 앞서 발생했던 문제가 해결된다.
1. 서버1이 다운되었을 경우 모든 트래픽은 서버2로 전송된다
2. 두대의 서버로 트래픽이 감당되지 않으면, 더 많은 서버를 추가한다. 로드밸런서가 자동으로 트래픽을 분산할것이다.
(CF) 로드밸런싱 알고리즘은 무엇이 있는가?
단순히 생각해보면 온 순서대로 여러 서버에 뿌려주는 방식이 있을것이다. 그런데 이러면 특정 서버가 오래걸리는 요청을 집중적으로 받을수도 있다. 그럼 이런 문제들을 해결해주는 알고리즘이 있지않을까? 바로 다음과 같다.
1. Round Robin(라운드 로빈)
다수의 서버에 순서대로 요청을 할당하는 기본적인 방법이다(NginX의 디폴트값이기도 함).
서버에 균등하게 요청을 분배할수는 있지만, 각 서버의 처리량이나 서버 상태, 작업 부하가 달라지는 경우를 고려하지 못한다.
2. Least Connection(최소 연결)
TCP/IP 프로토콜로 생성되는 Connection 기반으로 부하를 분산한다.
사용자와 서버가 정상적 연결중이면 Connection을 생성하는것에 착안해, Connection이 적은 서버에 요청을 전달하는것.
작업마다 처리시간 변동폭이 클때 효과적이지만, 각 서버의 활성연결수를 계속해서 추적하는 복잡성이 있고, 여전히 서버 응답시간이나 상태는 고려하지 못한다.
3. Weight Ratio(가중치 비율)
각 서버의 처리능력을 고려해 서버가 가질 수 있는 처리량 또는 Connection 비율 가중치를 토대로 부하를 분산한다.
각 서버 성능에 따른 효율적인 처리가 가능하지만, 최적 서버 가중치를 유지하는데 어려움이 있다.
4. IP Hash(IP 해시)
위 방법들은 독립된 여러 서버마다 세션 불일치 문제가 발생할 수 있다. 사용자 세션 정보가 A서버에 있는데, 요청이 B서버로 가게되면 B서버도 A서버가 갖고있는 세션정보를 가져야한다. 즉, 중복데이터 문제가 생긴다. 따라서 IP Hash를 통해 패킷의 IP주소를 해싱하고, 해시값에 해당하는 서버가 처리하는것을 보장함으로써 불일치를 해결한다. 쉽게말하면 한 유저에 대한 요청은 한 서버로만 가는것이다. 그러나 적은수의 클라이언트가 많은 요청을 처리할경우 특정 서버에 부하가 몰릴 수 있다.
5. Least Response Time(최소 응답 시간)
응답시간이 가장 빠른 서버에 우선적으로 요청을 할당한다.
사용자경험이 향상되나, 서버응답시간을 파악하기 위한 모니터링 시스템을 구축해야하므로 복잡해지며, 서버상태나 처리량을 고려하지 못한다.
각 방법마다 장단점이 있으므로, 비즈니스 성격에 맞게 알고리즘을 선택해야한다...!!
이렇게 기본적인 흐름을 이해해봤다.
하지만 이번에 살펴본건 '서버계층'에서 문제가 발생했을때이다. 즉, 이렇게 만든 수많은 서버에서의 요청은 여전히 단 하나의 데이터베이스에 연결된다는 것이고.... 이상황에서 만약 '데이터계층'에서 문제가 발생한다면? 소중한 데이터들이 사라질수있다 ^.^....
따라서 다음 포스팅에서는 데이터베이스 다중화에 대해 알아 볼 것이다.
'CS > 대규모 시스템 설계' 카테고리의 다른 글
[대규모 시스템 설계] 1-2. 아키텍처 설계 (0) | 2025.05.09 |
---|