사용자가 늘어나면서 서비스 구조는 어떻게 바뀌게 되는가? / 대규모 트래픽을 받기위해 서버를 어떻게 구성해야하는가? / feat. 가상 면접 사례로 배우는 대규모 시스템 설계 기초

     

목차

     

    들어가기 전에

    23년에 회사에서 다양한 IT책으로 스터디를 진행했다. 스터디한 책중에는 "가상 면접 사례로 배우는 대규모 시스템 설계 기초"라는 책이 있었다. 이 책에선 면접에 나올만한 주제로 다양한 IT 지식을 논의하는데, 그중 1장 "사용자 수에 따른 규모 확장성" 내용이 아주 재밌다.

     

    재밌다고 생각한 이유는 단일 서버에서 사용자가 늘어남에 따라 서버 구조를 어떻게 바꿔야 하는지, 바꾸면서 해결할 수 있는 문제는 무엇인지 단계적으로 설명해 준다. 결론적으로는 대규모 트래픽을 받을 수 있는 서비스 구조를 만들게 되는데, 아마 큰 회사에서 사용하는 서비스 구조가 아닐까 생각된다. (일단 우리 회사랑은 비슷함 ㅇㅇ)

     

    최근 채용페이지를 보면 개발자 지원자격에 항상 이런 말이 적혀있다.

     

    "대규모 트래픽을 경험 또는 운영해본 개발자"

     

    경력이 없는 개발자들은 도대채 어디서 대규모 트래픽을 경험한단 말인가?! 참 아이러니한 문구라고 생각된다. 그렇다고 면접에서 이렇게 말할 수는 없으니 우리는 간접경험이라도 해야 한다. 그 간접경험을 가장 기가 막히게 해 주는 게 이 책이었다.

     

     

    서비스의 진화과정

    1. 단일 서버

    자 이제 우리가 서비스를 하나 런칭한 스타트업이라고 생각해 보자. 아니 스타트업도 좀 커 보이니까 주변 지인들과 사이드프로젝트로 작은 서비스를 런칭했다. 프론트와 서버가 하나씩 존재하는 아주 간단한 서비스다.

     

    1단계 단일 서버 구조

     

    앱으로만 서비스한다면 AWS EC2의 public IP만으로 서비스가 가능하다. 하지만 웹 서비스도 사용한다면 도메인 서비스가 필요할 수 있다. AWS에서는 도메인 서비스를 위한 Route 53도 제공하니 이것도 추가해 보자.

     

    1단계 단일 서버 구조 (+ DNS를 위한 Route 53 추가 )

    이제 클라이언트에서 www.example.com과 같은 도메인을 입력해도 route53이 해당 도메인의 경로를 찾아 올바른 서버 주소를 알려준다.  

     

     

    2. 데이터베이스 추가

    단순히 정보를 보여주는 서비스의 경우 데이터를 저장할 필요가 없지만, 사용자가 이전에 한 행위나 사용자의 정보, 서비스의 정보 등을 저장하기 위해서는 데이터저장소가 필요하다. 작은 서비스의 경우 로컬 저장소를 이용해 구현할 수 있겠지만, 이것도 사용자가 많아지면 부족하게 된다. 이때 필요한 것이 바로 데이터베이스이다. AWS에서 제공하는 RDS를 이용해 데이터베이스를 연결한다.

     

    2단계 데이터베이스 추가

     

    이제 서비스에 필요한 데이터를 데이터베이스에 저장할 수 있다. 기존에 클라이언트에서 API를 요청하면 EC2에서 데이터를 처리해서 응답을 내려주었지만, 이제 특정 데이터가 필요하면 데이터베이스(RDS)를 조회해 필요한 데이터를 찾아 응답하게 된다. 

     

    데이터베이스는 다양한 종류가 있다. 우리가 많이 알고 있고 사용하고 있는 RDBMS의 mysql, oracle 등이 있고, 비관계형 데이터베이스인 NoSql의 mongo, dynamoDB 등이 있다. 

     

    어떤 데이터베이스를 사용하는지는 데이터의 성격에 따라 다르다. 데이터가 비정형인지, 응답속도가 빨라야 하는지, 데이터의 양이 큰지 등을 고려하여 알맞은 데이터베이스를 선택하면 된다. 일반적인 서비스에서는 관계형 데이터베이스를 선택하는 것이 무난하다.

     

    3. 스케일 아웃

    사용자가 늘어나서 API요청이 늘어나면 EC2의 성능을 올려 동시처리량을 증가시킬 수 있다. 이렇게 단일 서버의 성능을 올리는 방식을 스케일-업(scale-up)이라고 표현한다. 하지만 CPU나 메모리 등을 무한정으로 올릴 수는 없다. 이럴 경우 병렬로 서버를 늘려 API요청을 분산처리하는 방법이 있다. 이를 스케일-아웃(scale-out)이라고 표현한다. EC2앞에 로드밸런서(Load Balancer)를 추가하여 스케일 아웃을 적용할 수 있다.

     

    3단계 로드밸런서 추가

     

    AWS에서 제공하는 로드밸런서를 EC2 앞에 추가한다. 로드밸런서는 들어온 요청을 로드밸런서에 등록된 EC2에 고르게 분배한다. 기존에 하나의 EC2에서 서비스하는 것보다 EC2의 개수만큼 처리할 수 있는 양이 늘어나게 된다. AWS에서는 Auto Scale기능도 지원하기 때문에 특정지표(클라 요청수나 EC2의 CPU사용률 등)를 기준으로 EC2를 자동으로 늘리거나 줄일 수 있다. 

     

    이제 사용자의 요청이 늘어나면 EC2를 늘려서 대응할 수 있게 되었다. 하지만 데이터베이스는 아직도 하나다!! 요청이 몰리면 데이터베이스가 느려질 수 있다. 대부분의 데이터베이스 시스템은 다중화를 제공한다. AWS에서는 Read Replica를 제공해 주는데, 같은 데이터베이스를 1개의 Master DB와 여러 개의 Slave DB로 나눌 수 있다. Slave DB는 Read만 가능하며 Write 쿼리는 모두 Master DB로 보내야 한다. 

     

    3단계 데이터베이스 다중화

     

    물론 마스터 DB에서도 데이터조회가 가능하다. 마스터 DB에 쓰기 작업을 하게 되면 해당 작업은 read replica들에게 전달되며 자동으로 동기화된다. 따라서 데이터가 전달되는 시간 동안 약간의 데이터 정합성이 깨지는 경우(replica lag)가 발생하는데, AWS에서는 이 시간은 일반적으로 0.1초 이내라고 얘기하고 있다. 시간에 매우 민감한 데이터가 아니라면 0.1초는 무의미하기 때문에 read replica를 사용하면 되지만, 시간에 민감한 데이터라면 조회 역시 master DB에서 조회하면 delay를 줄일 수 있다.

     

    4. 캐시정책 적용

    데이터베이스를 좀 더 효율적으로 사용하기 위해서는 데이터베이스 앞에 캐시(Cache)를 두는 것을 고려할 수 있다. 데이터베이스는 기본적으로 Disk기반이라 읽고 쓰는 연산이 메모리에 비해 느리다. 데이터베이스 앞에 메모리 기반 캐시를 두게 되면 시스템 성능을 향상할 수 있다. AWS에서는 ElasticCache라는 서비스로 캐시서비스를 제공하고 있다.

     

    4단계 레디스 캐시 등록

     

    EC2는 사용자의 요청을 바로 RDS에서 조회하지 않고 캐시를 먼저 확인한다. 만약 원하는 데이터가 캐시에 있다면(cache hit) 클라이언트에 바로 데이터를 내려준다. 만약 찾는 데이터가 없다면 데이터베이스에서 조회하고 캐시에 해당 데이터를 업데이트한다. 이렇게 되면 다음에 같은 데이터를 조회할 때 캐시를 사용할 수 있게 되므로 데이터베이스의 부하를 줄일 수 있다.

     

    캐시를 사용할 때는 데이터의 성격을 잘 알아야 한다. 데이터가 매우 빠르게 변한다면 캐시 사용은 무용지물일 수 있다.(오히려 독일수도 있다.) 데이터가 빠르게 변한다면 캐싱된 데이터와 데이터베이스의 데이터가 틀어질 수 있기 때문이다. 따라서 데이터의 변화 주기를 고려해 캐시 할 데이터를 정해야 한다.

     

    5. CDN 적용

    정적인 데이터를 캐싱하기 위해서는 CDN서비스를 사용할 수 있다. (동적인 데이터 캐싱도 지원하긴 한다!)

    글로벌 서비스는 유튜브를 생각해 보면, 하나의 동영상이 한국에서도 재생되고 미국에서도 재생되고 유럽에서도 재생된다. 이렇게 물리적으로 떨어진 곳에 데이터를 빠르게 전달하기 위해서는 어떻게 해야 할까?

     

    5단계 CDN 적용

     

    AWS에서는 CloudFront라는 서비스를 통해 CDN서비스를 제공하고 있다. CloudFront를 클라이언트와 서버 사이에 두면 클라이언트에서 동영상이나 이미지를 조회할 때 CDN(CloudFront)를 먼저 조회한다. 만약 CDN에 데이터가 없다면 원본 저장소에서 데이터를 가져온다. CDN 사업자들은 CDN서버를 전국 곳곳에 관리하고 있기 때문에 사용자에게 최대한 가까운 CDN서버에서 응답이 내려가도록 보장하고 있다. 유튜브가 하나의 동영상을 끊김 없이 글로벌하게 제공할 수 있는 이유 중 하나이다.

     

     

    6. 데이터센터 다중화

    이제 물리적인 관점으로 다가가보자. 서버들이 모여있는 데이터센터에 불이 나면 어떻게 될까? 아마 모든 서버들이 작동을 중단하게 되고, 서비스가 중단될 것이다. 이를 위해 우리 서버들을 2개 이상의 데이터 센터에 분배할 필요가 있다. 다행히도, AWS를 사용하면 AvailableZone을 이용해 물리적 이중화를 구현할 수 있다. AWS에서 제공하는 각각의 서비스에 옵션으로 AZ(AvailbleZone)을 선택하면 되는데, 요즘에는 AZ를 2개 이상 선택해야 하는 게 디폴트라 아마 대부분 2개 이상으로 설정되어 있을 것이다. AWS 서울리전에는 a~d까지 총 4개의 AvailableZone을 운영하고 있기 때문에 원하는 AZ를 선택하면 된다.

     

    AZ 다중화로도 만족하지 못하는 경우 Cloud 다중화를 고려할 수 있다. 실제로 카카오에서 일할 때 AWS 전체 서비스가 무너지면 어떻게 하나?라는 고민을 했었다.(너무 오버하는 것 같기도 하지만..) 실제로 한국에서 AWS장애로 여러 서비스가 먹통이 되는 사건이 간간히 발생했다. 장애가 긴 시간 지속된 것은 아니었지만, 무중단 서비스를 위해서는 Cloud 자체 다중화를 고려해야 할 수도 있다. AWS에 구축한 서비스를 고대로 구글 클라우드나 네이버 클라우드에 구축하면 된다.

     

    6단계 클라우드 서비스 다중화

     

    물론 그냥 똑같이 복사 붙여 넣기 한다고 되는 것이 아니다. DB의 경우 각 클라우드에 저장되는 데이터가 다르기 때문에 어디든 메인으로 사용할 데이터베이스가 있어야 한다. 메인이 아닌 데이터베이스가 있는 클라우드의 경우 CDC와 같은 솔루션을 통해 데이터베이스를 지속적으로 동기화하다가, 메인 데이터베이스가 있는 클라우드가 서비스 중지될 경우 메인으로 승격하여 사용될 수 있도록 되어야 한다. 그 밖에도 양쪽 클라우드를 어떤 클라이언트가 어떤 조건일 때 호출해야 하는지도 결정해야 하고, 그 결정은 어떤 클라우드(혹은 on-premise) 서버에서 할지도 결정해야 하기 대문에 상당~~히 복잡한 구조가 만들어진다. 일반적인 회사에서 클라우드까지 다중화하는 경우는 거의 없을 것 같다. 🤔

     

    7. 데이터베이스 샤딩

    Read Replica와 Cache를 이용해 데이터베이스의 부하를 줄이기는 했지만, 쓰기 작업에 대해서는 DB 부하가 높을 수 있다. 이를 해결하기 위해 데이터베이스 샤딩을 하게 되는데, 데이터를 특정 키를 기준으로 나누어 저장하는 것이다. 이전 회사에서는 회원의 계좌정보를 홀짝으로 구분해, 홀수 계좌번호는 데이터베이스 1번, 짝수 계좌번호는 데이터베이스 2번을 사용했다.

     

    7단계 데이터베이스 샤딩

     

    홀수/짝수로 나뉘게 되면 2개의 마스터 DB가 생기게 되기 때문에 쓰기 작업을 2등분 할 수 있다. 더 분할하고 싶다면 샤닝수를 늘리면 된다. 샤딩을 하게 되면 데이터가 분할되어 저장되기 때문에 동시에 여러 개의 데이터를 조회해야 하는 비즈니스에는 부적합할 수 있다. 이런 비즈니스가 있다면 샤딩된 데이터를 한 번에 저장하는 데이터마트를 구성하거나, 분산된 데이터를 한 번에 조회할 수 있는 서비스를 만들어야 한다.

     

    8. 메시지 큐 활용

    서비스가 커지고 복잡해지면서 서버 간 의존성이 높아지고, 작업을 처리하는 시간이 많아지는 이슈가 생길 수 있다. 이때 적용할 수 있는 솔루션으로는 메시지 큐가 있다. 메시지큐를 사용하면 서비스, 서버 간 관계가 느슨해지면서 확장에 용이한 서비스 구조가 된다.

     

    8단계 메시지 큐 적용

     

    비동기적으로 처리해야 하는 비즈니스인 경우 서버에서 메시지 큐를 발행하여 비동기 요청을 보내고, 서버는 다른 일을 할 수 있다. 따라서 동시에 처리할 수 있는 처리량이 늘어나게 된다. 보통 사용자에게 Push를 보내거나 백그라운드로 동작해도 되는 비즈니스의 경우가 이에 해당된다. 동시에 여러 서버에 데이터를 보내야 할 때도 메시지 큐의 Pub/Sub 구조를 이용해 데이터를 전달할 수 있다.

     

     

    9. 서비스 모니터링

    구조가 복잡해지고 커질수록 모니터링 기능이 필수가 된다. 어떤 서버에 어떤 문제가 발생하는지 실시간으로 확인하는 것은 중요하다. AWS에서는 기본적으로 CloudWatch를 통해 각 서버들의 상태를 파악할 수 있게 되어있다. 어플리케이션 서버 레벨에서의 상태를 확인하기 위해 별도의 3rd party 모니터링 툴을 도입할 수 있다. 메시지큐를 이용해 각 서버에서 발생하는 로그를 수집해 grafana나 datadog 같은 모니터링 툴로 전송하여 대시보드 형태로 볼 수 있다. 

     

    9단계 모니터링 추가

     

    모니터링 지표를 통해 서버 상태를 확인할 수 있을 뿐만 아니라, 지표를 이용해 자동으로 서버를 늘리고 줄이는 자동화(auto scailing)를 할 수 있다. ReadReplica의 CPU가 증가하는 경우 ReadReplica DB의 수를 늘릴 수 있고, EC2의 CPU가 올라간다면 EC2를 여러 대 더 생성할 수 있다.

     

     

    끝없는 구조 개선

    단일 서버에서 시작한 서비스 구조가 큰 규모의 트래픽을 받을 수 있는 대규모 서비스로 진화했다. 더 많은 트래픽을 받기 위해서는 꾸준한 관리가 필요하다. 각각의 서비스를 쪼개서 단일 서버로 만드는 과정(MSA), 빈번히 조회되는 데이터 캐싱, 쓰기 작업이 많은 데이터 샤딩, 무거운 작업 비동기처리 등 지속적인 개선을 통해 서비스 구조는 계속 커질 것이다. 

     

    아마 대부분의 IT서비스 기업들은 위와 비슷한 구조로 서비스를 운영하고 있을 것이다. 아직 취업하지 않았거나 작은 회사에서 일하고 있다면 이 글로 조금이나마 서비스 구조에 대해 이해했으면 좋겠다. 앞서 설명한 것처럼 이 내용은 책 "가상 면접 사례로 배우는 대규모 시스템 설계 실습" 1장에 소개되어 있다. 이 책에는 이 내용 말고도 많은 실전 내용이 담겨 있기 때문에 면접을 보기 전이나 IT관련 지식을 공부할 때 한 번쯤 읽어볼 만한 책인 것 같다. 

     

    - 글을 쓰는 시점에는 책 2편이 나왔다.!!

     

     

     

     

    반응형

    댓글

    Designed by JB FACTORY