[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 01. 사용자 수에 따른 규모 확장성

2023. 5. 19. 02:16Dev/System Design

반응형

※ 이 포스팅은 가상 면접 사례로 배우는 대규모 시스템 설계 기초의 1장. 사용자 수에 따른 규모 확장성 부분을 정리한 것입니다.

  1. 개요
  2. 웹계층 Scale Out
    1. 로드밸런서
    2. stateless
    3. 메시지 큐
    4. 로그, 메트릭, 자동화
  3. 데이터 계층 Scale Out
  4. 로드밸런서 && 데이터베이스 다중화
  5. 데이터베이스 규모 확장
    1. 샤딩
    2. 데이터베이스 종류에 대한 고민
  6. 응답시간 개선
    1. 캐시
    2. CDN
    3. Cache && CDN 적용 결과
  7. 데이터 센터

1. 개요

1.1. 단일서버?

모든 컴포넌트(웹앱, DB, 캐시서버 등)가 단 한대의 서버에서 실행되는 시스템입니다.

웹서비스를 이용하고자하는 사용자가 웹서버에 바로 접근하는 구조로 웹서버가 다운될 경우 웹사이트 접속이 불가능합니다.
또, 데이터베이스가 하나이기 때문에 장애가 발생했을 시, 장애 자동복구나 다중화를 지원하지 못합니다.

이러한 이유로 수백만 사용자를 지원하는 시스템을 설계하기 위해서는 여러대의 서버를 사용하는 것은 필수적입니다.


1.2. 서버 확장

서버를 확장하고자 할 때, 크게 2가지 계층에서 확장을 고려할 수 있습니다.

첫번째는 웹과 모바일의 트래픽을 처리하기 위한 웹계층 확장이 있고,
나머지 하나는 데이터 처리와 장애 복구를 위한 데이터 계층 확장이 있습니다.
이 두가지는 각각 독립적으로 확장할 수 있습니다.


1.3. Scale Up vs Scale Out

웹 계층과 데이터 계층을 확장하고자 할 때, 각 계층을 확장하는 방법에는 크게 2가지 방식이 있습니다.

흔히들 많이 들어봤을 Scale Up과 Scale Out이 이에 해당됩니다.

  • Scale Up
    • 수직적 규모 확장 프로세스
    • 고사양 자원(더 좋은 CPU, 더 많은 RAM)을 추가하여 성능을 개선합니다.
  • Scale Out
    • 수평적 규모 확장 프로세스
    • 더 많은 서버를 추가하여 성능을 개선합니다.

Scale Up은 서버의 스펙을 높이는 것을 말하는 것이고, Scale Out은 여러 서버를 묶어서 하나의 서비스를 처리하도록 하는 것입니다.

Scale Up 방식으로 규모를 확장하는 것은 비용도 비싼데다 확장하는데에 한계가 있습니다.
또, 서버에 장애가 발생했을시 모든 서비스가 다운되게 되는 SPOF 위험성이 있습니다.(Single Point Of Failure)

따라서, 서버 확장을 위해서는 Scale out 방식은 필수적입니다.

서버 장애 발생에 대한 대책 방안으로는 자동복구(failover), 다중화(redundancy) 가 있습니다.


그러면 이제, 웹서버를 Scale Out하는 방법과 데이터베이스를 Scale Out하는 방법을 알아봅시다.


2. 웹 계층 Scale Out

단일 서버에서는 웹 서비스를 위한 서버가 단 한대이며, 사용자가 직접적으로 서버에 접근합니다.
따라서, 그 서버에서 장애가 발생했을 때 웹사이트에 접속하지 못하는 문제점이 있습니다.

단순하게 웹서버가 다운되었거나
응답속도가 느려지는 경우 경우 (너무 많은 사용자가 접속하여 웹서버가 한계에 도달)

그렇기 때문에 수백만 사용자가 이용하는 시스템을 설계하는데에서 웹 계층에서의 부하 분산은 필수적입니다.


2.1. 로드밸런서

웹 계층을 Scale Out하기 위한 방법입니다.

로드밸런서는 웹서버들의 트래픽 부하를 분산하는 역할을 합니다.
웹 계층에서 발생되는 장애를 해결하기 위한 방법입니다.

01-1

사용자가 웹사이트에 접속하고자 할때, 웹서버로 직접 접속하지 않고 로드밸런서로 접속합니다.

로드밸런서에 여러 서버를 연결하여 웹/모바일 트래픽을 분산하여 처리하기 때문에, 서버 하나가 죽더라도 다른 서버들이 트래픽을 받아 사이트가 다운되는 일을 방지할 수 있습니다.(가용성 증가)

서버에 유입되는 트래픽이 일시적으로 높아지는 기간이 있다면 로드밸런서에 서버를 더 추가하여 장애를 줄일 수 있습니다.

또, 로드밸런서에 연결된 서버들을 같은 네트워크에 구성하여, private ip로 설정하면 외부에서 server로 직접 접속하지 못하게 설정할 수 있고
이를 통해 보안을 높일 수 있습니다.


3.2. stateless

웹 계층을 Scale Out 하기 위해서는, 하나의 요청이 어느 서버에서 실행되든 동일하게 동작되어야 합니다.

이전에 세션을 이용하여 사용자 정보를 서버에 저장하던 부분에서, 사용자 정보는 client가 가지고 있는 방식으로 변경이 되어야 하며
각 요청들은 무상태(stateless) 로 만들어야 합니다.

request에 session과 같은 상태정보가 있을 경우, 서버 하나에 의존적인 구조가 됩니다.
로드밸런서를 구성했을 때 세션정보를 활용하기 위해 고정세션 기능을 활용할수도 있으나, 이럴 경우 로드밸런서의 부담도 커지며, 서버 장애 발생시 다른 서버에서 요청을 처리할 수 없게 됩니다.

따라서, 웹 계층을 수평적으로 확장하기 위해서는 stateless 아키텍처로 구성하는 것은 필수적입니다.

웹서버를 stateless 아키텍처로 구성하면, 사용자가 로드밸런서에 설정된 서버 중 특정 서버와만 데이터를 송수신하지 않아도 됩니다.
http request는 로드밸런서 내의 어느 서버로든 전송되어도 되고, 만일 상태정보가 필요할 경우에는 웹서버가 아닌 shared storage(공유저장소) 로부터 가져옵니다.

즉, 상태정보는 웹서버로부터 물리적으로 분리된 상태입니다.

사용자 상태정보는 database, cache(memcached, redis) 등에 저장할 수 있습니다.


2.3. 메시지 큐

메시지 큐는 메시지의 무손실(durability)을 보장하는 비동기 통신 컴포넌트입니다.

01-4

생산자(Producer) 입력 서비스가 메시지를 만들어 메시지 큐에 발행합니다(publish)
메시지큐는 연결되어있는 소비자(Consumer) 서비스에게 메시지에 들어있는 동작을 수행합니다.

  1. 서비스(또는 서버) 간 결합이 느슨해져서(loosely coupled) 규모를 확장하는데에 도움이 됩니다.
  2. 생산자와 소비자는 각각 독립적이기 때문에 둘중 하나가 다운된 상태여도 각 기능에 영향을 주지 않습니다.
    1. 소비자가 다운되어도 생산자의 메시지 발행은 정상적으로 이뤄집니다.
    2. 생산자가 다운되어도 소비자는 메시지를 수신할 수 있습니다.

만약 어떤 작업이 완료되기 까지 오래 걸리지만, 다음 작업을 진행하는 데에 있어서 그 작업의 결과를 당장 받지 않아도 된다면, 메시지 큐를 사용해보는 것을 추천합니다.

관련 내용은 아래의 그림을 보면 이해하기가 쉬울 것입니다.


첫 번째 그림은 우리가 흔히 많이 이용하는 동기방식으로 API를 호출하는 A, B, C 서비스에서 API를 요청하고 응답받는 예시 입니다.
C에서 요청을 처리하던 중 에러가 발생될 경우, B와 A에도 에러가 전파된다는 단점이 있기 때문에 이 부분에 대해 고려하면서 개발을 진행해야 합니다.

01-5

두번 째 그림에서는 D, E, F 사이에 메시지 큐를 두고 있고,
D에서 E로 직접적으로 요청을 보내는 것이 아니라, 중간에 위치한 메시지 큐에 이벤트를 주는 방식으로 기능을 처리합니다.
D는 이벤트 큐에 어떤 행위를 적재하고, E는 이벤트 큐로부터 consume으로 이벤트를 받아서 처리합니다.

이렇게 처리할 경우, 만일 D가 문제가 발생하여 서버가 정상적으로 동작하지 않게 되더라도 E가 이벤트큐로부터 이벤트를 소비하는 것에 영향을 주지 않습니다.(반대의 경우도 마찬가지로 영향을 주지 않습니다.)

01-6

2.4. 로그, 메트릭, 자동화

웹 서버가 많아진 만큼, 장애가 발생된 서버를 모니터링 하는 기능이 필수적입니다.
또, 하나의 서비스를 여러 서버에 배포하는 CI/CD 부분도 반드시 고려해야할 사항입니다.

  • 로그
    • 에러 로그를 모니터링
    • 시스템 오류를 쉽게 찾을 수 있습니다.
    • 로그를 단일 서비스로 모아주는 도구를 활용하여 로그를 간편히 조회합니다
  • 메트릭
    • 호스트 단위 메트릭
      • CPU, Memory, 디스크 I/O
    • 종합 메트릭
      • 데이터베이스 계층의 성능, 캐시 계층의 성틍
    • 핵심 비즈니스 메트릭
      • daily active user, revenue(수익), retention(재방문)
  • 자동화 도구
    • 생산성을 높이기 위해 필수적으로 필요합니다.
    • CI(Continuous Integration, 지속적 통합)
      • 개발자가 만든 코드를 자동으로 어떤 검증 절차를 거치게 하여 문제점을 빠르게 감지하게 합니다.
    • 빌드, 테스트, 배포 절차를 자동화하여 개발 생산성을 향상합니다.

3. 데이터 계층 Scale Out - 데이터베이스 다중화

데이터베이스 다중화는 데이터베이스 서버를 Scale Out하는 방법입니다.

master-slave 관계를 설정하여 데이터 원본은 master 서버에, 복제본은 slave 서버에 저장하는 방식
데이터 계층에서 발생되는 장애를 해결하기 위한 방법입니다.

데이터 원본은 master 서버에 저장되고 사본을 slave에 저장하는 방식입니다.

읽기 연산 외의 기능(데이터를 변경하는 기능)은 master에서만 일어나고, slave 서버에서는 read 연산만 일어납니다.

데이터 변동을 일으키는 연산은 master로 전달됩니다.

애플리케이션 연산의 대부분은 읽기 연산(read)으로, 쓰기 연산(insert/update/delete)에 비해 부하가 더 큽니다.

이에 따라, slave 데이터베이스 수를 master 데이터 수보다 많이 설정하는 것이 데이터베이스 성능을 높이기에 유리합니다.

01-2

  • 성능을 높일 수 있습니다.
    • slave 서버를 여러개 두게 되면 읽기 연산을 분산 처리하게되고, 이에 따라 병렬적으로 처리되는 쿼리 수가 늘어나 성능이 높아집니다.
  • 안정성(reliability)
    • 데이터베이스 서버를 여러지역에 다중화할 경우, 재해발생으로 데이터가 손상되어도 데이터는 안전하게 보존할 수 있습니다.
  • 가용성(availability)
    • 데이터베이스를 여러 곳에 복제해둘 경우, 하나의 데이터베이스 서버가 장애가 발생되어도 다른 서버에 있는 데이터를 가져와 서비스를 지속할 수 있습니다.

IF 1

만일 slave 데이터베이스 서버에 장애가 발생되었는데 slave 서버가 한대 뿐일 경우에는 일시적으로 모든 데이터베이스 연산은 master가 수행하게 됩니다.

IF 2

만일, master 데이터베이스 서버에 장애가 발생될 경우, slave 데이터베이스중 하나를 master 데이터베이스 서버로 대체하여 연산합니다.
그 때, 만일 slave 데이터베이스 서버가 한대 뿐이었다면 새로운 slave 서버를 즉시 추가하는게 좋습니다.

production 환경에서라면, slave 데이터베이스 서버에 있는 데이터가 최신 데이터가 아닐 경우가 있기 때문에 별도의 처리를 고려해야 합니다.

이 경우에는, slave 데이터베이스를 master 데이터베이스로 교체하기 전에 없는 데이터를 복구하기 위한 복구 스크립트(recovery script)를 실행해야합니다.

※ 만일 multi-master, circular replication 등을 도입하여 master 데이터베이스를 1개 이상으로 설정할 경우 master 데이터베이스에 장애가 발생되더라도 장애복구가 더 유리할 수 있겠지만, 이 부분은 구성이 매우 복잡해서 시스템 설계에 난이도가 높습니다.


4. 로드밸런서 && 데이터베이스 다중화

01-3

읽기나 쓰기를 실행하는 과정은 아래와 같습니다.

  1. 사용자가 DNS 서버로부터 public ip를 전달받는다(public ip는 로드밸런서 주소)
  2. public ip를 이용하여 로드밸런서에 접속합니다.
  3. 로드밸런서는 Web Server들 중 하나에게 HTTP request를 전달합니다.
  4. 웹서버에서 비즈니스 로직을 실행합니다.
    1. 데이터베이스 쓰기연산의 경우 master DB를 사용하고
    2. 데이터베이스 읽기연산의 경우 slave DB를 사용합니다.

5. 데이터베이스 규모 확장

위에서 데이터베이스 서버를 Scale Out 방식으로 확장하는 것을 알아봤습니다.

그런데 만약 하나의 데이터베이스 서버 내에 데이터 규모가 너무 커진다면 어떻게 해야할까요?

단순하게 데이터베이스 서버의 용량을 증설하는 Scale Up 방식은 쉬워보이지만, 이는 위에서 말했었던 고비용, SPOF, 증설 용량 한계등의 문제가 있습니다.

이럴 때에는, 데이터베이스를 수평적으로 증설하는 것을 고려해봐야합니다.

5.1. 샤딩(sharding)

데이터베이스를 수평적으로 확장하는 것을 샤딩이라고 합니다.

샤드들은 모두 같은 스키마를 쓰지만 각 샤드에 들어있는 데이터는 중복되지 않습니다.
즉, 하나의 데이터베이스 서버를 여러대로 쪼갠 형태라고 볼 수 있습니다.

샤딩 전략을 구현할 때 고려할 중요한 것은 샤딩 키(= 파티션 키) 인데, 이 것은 데이터 분산에 활용된 하나 이상의 키입니다.

샤딩을 구성했을 경우 고려해야할 몇가지 사항이 있습니다.

  • 재샤딩
    • 데이터가 너무 많아 샤드가 더 필요할 때
    • 샤드간 데이터 분포가 균등하지 않을 경우
  • 유명인사 문제(= 핫스팟 조회)
    • 빈번히 조회되는 레코드가 하나의 샤드에 몰려있을 경우, 하나의 샤드에만 트래픽이 몰릴 수 있습니다.
    • 이 경우, 유명인사들을 여러 샤드에 분산하여 할당해야합니다.
  • 조인, 비정규화
    • 샤딩 후 데이터 조인이 힘들어지는 문제가 발생합니다
    • 이를 해결하기 위해 데이터베이스를 비정규화하여 하나의 테이블에서 쿼리가 수행될 수 있도록 할 수 있습니다.

5.2. 데이터베이스 종류에 대한 고민

하나의 데이터베이스를 쪼개서 여러개의 서버에 저장하는 것이 샤딩입니다. 이러한 특징 때문에 고려해야할 점이 있습니다.

RDBMS는 데이터무결성과 일관성이 중요하기 때문에 샤딩이 매우 까다롭습니다.

RDBMS의 가장 중요한 특징인 관계형이 바로 그에 대한 이유입니다.
만일, 특정 데이터베이스의 FK값을 이용하여 다른 테이블과 join을 하고자할 때, 그 연관테이블이 다른 DB서버에 저장되어있다면 어떻게 될까?
파티션 조각간의 join은 되지 않기 때문에 이 경우에는 RDBMS의 장점이 무색해집니다.

바로 이러한 점이 기존의 RDBMS에서 샤딩에 대한 어려운 점으로 남습니다.

때문에, 샤딩을 활용하고자 한다면, NoSQL을 사용해볼 것을 고려해보는 것도 좋습니다.

NoSQL은 그 밖에도 아래와 같은 상황에도 고려해보는 것을 권장합니다.

  • 응답 지연시간(latency)이 낮아야할 경우
  • 다루는 데이터가 비정형일 경우(=관계형 데이터가 아닐경우)
  • 데이터를 직렬화/역직렬화 하기만 하면 되는 경우
  • 아주 많은 데이터를 저장해야하는 경우
  • 트래픽 양에 따른 규모 확장을 빠르고 손쉽게 하고 싶은 경우

6. 응답시간 개선

응답시간(latency)을 개선하기 위한 방법을 알아봅시다.
캐시를 붙여서 정적 컨텐트를 CDN으로 옮기면 개선할 수 있습니다.


6.1. 캐시

값비싼 연산결과나 자주 참조되는 데이터를 메모리 안에 둬서 후에 이어지는 request를 보다 빨리 처리될 수 있도록 하는 저장소입니다.

request 하나를 처리할 때 최소 1번 이상의 Database 호출이 발생됩니다.
애플리케이션 성능 향상을 위해서는 DB 호출을 최소화하는 것이 중요합니다.

캐시 계층은 데이터가 잠시 보관되는 곳으로, DB보다 빠릅니다
별도의 캐시 계층을 두는 것은 성능개선과 DB 부하를 줄이는 장점이 있습니다.

캐시를 사용할때 몇가지 고려해야할 점이 있습니다.

  • 캐시를 적용하기 적절한 경우는?
    • 데이터 갱신이 잘 일어나지 않을 경우에는 캐시를 적용하기 적절합니다.
    • 데이터 갱신이 빈번한 경우에는 적절하지 않습니다.
  • 캐시에는 어떤 데이터를 둬야할까?
    • 캐시는 휘발성 메모리에 둡니다.
    • 서버 재시작시 모두 휘발되기 때문에 영속적으로 저장해야할 데이터는 반드시 database에 저장해야합니다.
  • 보관된 데이터의 만료기간은?
    • 캐시 만료기간에 대한 정책을 설정하여, 만료된 데이터는 캐시에서 삭제되도록 해야합니다.
      • 만료시간이 너무 짧을시, DB 호출을 빈번하게 하고
      • 만료시간이 너무 길 경우, 원본과 캐시에 저장된 값이 다를 경우가 있습니다.
  • 일관성(consistency) 유지 방법은?
    • 데이터를 저장하는 연산과 캐시를 갱신하는 연산은 하나의 Transaction 내에서 처리해야만 일관성을 유지할 수 있습니다.
  • 장애 대책
    • 캐시서버를 분산시켜야 합니다. (= 여러대 둬야 합니다.)
      • 캐시서버가 하나뿐이라면, 캐시서버에 장애가 발생했을시 데이터 조회 관련된 모든 서비스가 중단될 수 있습니다. (SPOF, Single Point Of Failure)
  • 데이터 방출(eviction) 정책
    • 캐시가 꽉찼을 때 기존 데이터 중 어떤 데이터를 방출할것인지에 대한 정책
    • LRU, Least Recently Used
      • 마지막으로 사용된 시점이 가장 오래된 것을 내보냄
    • LFU, Least Frequently Used
      • 사용 빈도가 가작 적은 것을 내보냄
    • FIFO, First In First Out
      • 가장 먼저 들어온 것을 가장 먼저 내보냄(= 선입선출)
  • 캐시 메모리
    • 너무 작을 경우, 저장된 데이터가 자주 밀려나가 캐시 성능이 떨어집니다.

6.2. CDN

Content Delivery Network
정적 컨텐츠를 전송할 때 쓰이는 지리적으로 분산된 서버의 네트워크로 이미지, 비디오, css, javascript 파일 등을 캐시할 수 있습니다.

request path + query string + request header + cookie 등의 정보를 이용하여 HTTP 페이지를 캐시합니다.

CDN 서버가 여러대일 경우, 사용자가 http request를 요청할 시, 가장 가까운 CDN 서버로부터 정적 컨텐츠를 전달받을 수 있습니다.
만일, CDN 서버에 캐시되지 않은 정적 컨텐츠를 요청했을 경우, 원본 서버로부터 요청하여 컨텐츠를 요청하여 적재한 후 반환합니다.

이때, 응답 헤더에는 TTL 값이 들어있고, TTL에 명시된 시간이 끝날때까지만 캐시합니다.
컨텐츠의 최신성 유지와 원본 서버에 요청보내는 수의 간의 중간점을 찾기 위해 적절한 TTL을 설정해야 합니다.


CDN 서버를 사용할 때 고려해두면 좋을 사항이 몇가지 있습니다.

  • 만일 CDN 서버에 장애가 발생했을 경우에 대한 대책 방안
    • 원본서버로부터 직접 컨텐츠를 가져오도록 처리
  • TTL이 만료되지 않은 컨텐츠를 무효화하는 방안
    • CDN 서비스 사업자가 제공하는 무효화 API
    • 정적 컨텐츠에 버전 관련 인자 설정
      • ex. like.png?v=2

6.3. Cache && CDN 적용 결과

  1. 정적 컨텐츠(images, js, css)는 웹서버가 아닌 CDN을 통해 제공하기 때문에 웹서버 성능을 향상시킵니다.
  2. 캐시가 database 서버의 부하를 줄입니다.

7. 데이터 센터(IDC)

지리적 라우팅(geoDNS-routing, geo-routing)을 이용하면 사용자의 위치에서 가장 가까운 데이터 센터로 ip를 변환할 수 있습니다.

예를들면 서울 IDC, 대전 IDC 같이 지역을 달리 구성하는 것


여러개의 데이터 센터를 지리적으로 다른 위치에 위치시키는 것이 큰 재난에 대한 대응을 할 수 있습니다.

다중 데이터 센터 아키텍처에서 데이터 센터 하나가 장애가 발생했을시, 관련 트래픽은 나머지 데이터 센터에서 대신하여 처리합니다.

다중 데이터 센터 아키텍처를 구성할 때엔 아래와 같은 사항을 고려해야 합니다.

  • 트래픽 우회
    • GeoDNS가 가장 가까운 데이터센터로 트래픽을 보내도록 해야합니다.
  • 데이터 동기화
    • 데이터 센터마다 별도의 데이터베이스를 가지고 있을 경우, 각 데이터 센터 내에 있는 데이터베이스를 다중화해야 합니다.
  • 테스트 및 배포
    • 자동화된 배포도구를 이용하여 각 데이터 센터에서 웹사이트/애플리케이션 구동이 잘되는지 테스트해야 합니다.

++

728x90
반응형