chap1. 사용자 수에 따른 규모 확장성
18 Dec 2021 | System Design
사용자 수에 따른 규모 확장성
- 수많은 사용자를 지원하는 시스템은 지속적인 계량과 끝없는 개선이 필요
- 1 user -> ~M users 를 지원하는 시스템 설계
1. 단일 서버
- 단일 서버는 하나의 서버에 웹 앱, 데이터베이스, 캐시 등을 모두 실행
fig1-1
fig1-2
1-1. 사용자 요청 처리 흐름
- 사용자는 도메인 이름 (ex: naver.com)을 이용하여 웹사이트에 접속
- 도메일 이름을 IP주소로 변환하는 과정 : DNS (Domain Name Service) 질의
- DNS는 third party 유료 서비스이므로 우리 시스템의 일부는 아님
- DNS 조회 결과로 IP주소 반환 (ex: 123.123.123.123)
- 해당 IP주소로 HTTP 요청 전달
- 요청을 받은 웹서버가 응답 반환 (HTML, JSON, …)
1-2. 실제 요청 유형
- 웹 애플리케이션 (Web Application)
- 비즈니스 로직, 데이터 저장 … : 서버 구현용 언어 (Python, Java …)
- 프레젠테이션 : 클라이언트 구현용 언어 (HTML(…언어?), javascript)
- 모바일 앱 (Mobile Application)
- 모바일 앱과 웹 서버간 통신 필요
- 프로토콜 : HTTP
- 데이터 포맷 : JSON이 널리 사용
2. 데이터베이스
- 사용자가 늘면 서버를 늘려야 함
- 웹/모바일 트래픽 처리 용도
- 데이터베이스 용도
- 분리 후 각각을 독립적으로 확장
fig1-3
2-1. 데이터베이스 선택
- 데이터베이스는 크게 2가지
- RDBMS(Relational Database Management System) : MySQL, Oracle Database, PostgreSQL, …
- 자료를 table, row, column으로 표현
- 여러 테이블에 있는 자료를 join 가능
- NoSQL : CouchDB, Neo4j, Cassandra, HBase, …
- NoSQL은 다시 4가지로 분류 가능
- key-value store
- graph store
- column store
- document store
- 보통 RDBMS를 많이 선택
- 하지만 다음과 같은 경우 NoSQL이 좋은 선택일 수 있음
- 아주 낮은 응답 지연시간 요구
- 다루는 데이터가 정형 데이터가 아닌 비정형 데이터 (unstructured data)인 경우
- 데이터 (JSON, YAML, XML 등)를 직렬화(serialize)하거나 역직렬화(deserialize)할 수 있기만 하면 됨
- 아주 많은 양의 데이터를 저장할 필요가 없음
3. Scale Up vs Scale Out
Scale Up
- 수직적 규모 확장 (vertical scaling) 프로세스
- 단일 서버에 고사양 자원 (더 좋은 CPU, 더 많은 RAM 등)을 추가하는 행위
Scale Out
- 수평적 규모 확장 프로세스
- 더 많은 서버를 추가하여 성능을 개선
선택 기준은?
- 서버로 유입되는 트래픽이 많지 않을때는 scale up이 좋은 선택 (단순하게 확장 가능)
- 하지만 scale up에는 심각한 단점이 존재
- 확장의 한계 존재 (단일 서버에 CPU, RAM을 무한정 증설할 수 없음)
- 자동복구(failover) 방안이나 다중화(redundancy) 방안을 제시하지 않음
- 단일 서버 운영시, 서버에 장애가 발생하면 웹사이트/앱은 완전히 중단됨
- 위 단점들로 인해, 대규모 애플리케이션의 경우 수평적 규모 확장법이 적절함
- 위 설계 (fig1-1)의 경우, 사용자가 많아져 웹서버가 한계 상황에 도달하게 되면 응답속도가 느려지거나, 서버 접속이 불가능해질 수도 있다.
- 문제 해결을 위해서 로드밸런서(Load Balancer)를 도입
3-1. 로드밸런서(Load Balancer)
- 로드밸런서는 load balancing set에 속한 웹 서버들에게 트래픽 부하를 고르게 분산함
fig1-4
- fig 1-4와 같이 user는 LB의 Public IP 주소로 접속
- 웹서버는 user의 요청 직접 처리 x
- 보안을 위해 서버간 통신은 private IP 주소 사용
- load balancing set에 또 하나의 웹 서버를 추가하고나면 failover 가능, availibility 향상
- 서버 1이 다운되면 모든 트래픽은 서버 2로 전송 (failover)
- 사이트 전체 다운 x
- 부하를 나누기 위해 새로운 서버를 추가 가능
- 웹사이트로 유입되는 트래픽이 가파르게 증가하면, 간단하게 LB에 붙어있는 LB set에 새로운 서버를 추가하여 트래픽 분산 가능 (avaliliblity 향상)
- 웹 계층은 OK, 하지만 데이터 계층은? 데이터 계층도 다중화가 필요하다.
3-2. 데이터베이스 다중화
fig1-5 (TODO:)
- 많은 DBMS가 다중화를 지원 : 서버 사이 master - slave 관계 설정, 원본은 master, 사본은 slave에 저장
- 쓰기 연산 (write operation - insert, delete, update 등)은 master에서만 지원
- 읽기 연산 (read opreration)은 slave에서 지원
- 대부분의 애플리케이션은 읽기 연산 » 쓰기 연산 이므로 통상 slave가 master보다 많음
DB 다중화로 인한 이득
- 더 나은 성능 : read operation이 부 데이터베이스 서버들로 분산되므로, 병렬 처리 가능한 query가 늘어남
- 안정성 (reliability) : 데이터베이스 서버 가운데 일부가 crash되어도 다른 서버 덕분에 데이터는 보존
- 가용성 (availibility) : 데이터를 여러 지역에 복제해 둠으로써, 하나의 데이터 서버에 장애가 발생해도 다른 서버에 있는 데이터를 가져와 계속 서비스 가능
DB 다중화시 장애 상황 시나리오
- 부 서버 다운 시나리오
- 부 서버가 한 대 뿐인데 다운된 경우
- 읽기 연산은 한시적으로 주 데이터베이스로 전달
- 새로운 부 데이터 서버가 장애 서버 대체
- 부 서버가 여러대인 경우
- 읽기 연산은 나머지 부 데이터 베이스로 전달
- 새로분 부 데이터베이스 서버 추가하여 대체
- 주 서버 다운 시나리오
- 부 데이터베이스 서버가 새로운 주 서버가 됨
- 모든 db연산은 일시적으로 새로운 서버상에서 수행
- 새로운 부 서버 추가
- production 환경에서 주 서버 다운 장애는 위 보다 훨씬 복잡함
- 부 서버에 보관된 데이터가 최신 상태가 아닐 수 있음
- 없는 데이터는 recovery script를 돌려서 추가해야함
- 다중 마스터 (multi-masters)나 원형 다중화(circular replication) 방식 도입 가능
- 위에 대한 자세한 논의는 참고문헌 (multi-master replication, NDB Cluster Replication)을 참고
3-3. 로드밸런서 + DB 다중화
fig1-6 (TODO:)
- user는 DNS로부터 LB의 Public IP주소를 받음
- 해당 IP주소를 사용해 LB에 접속
- HTTP요청은 서버 1 또는 서버 2로 전달
- 웹 서버는 사용자의 데이터를 부 데이터베이스 서버에서 읽음
- 웹 서버는 데이터 변경 연산은 주 데이터 베이스로 전달 (insert, delete, update)
3-4. 마무리
- 웹 계층과 데이터 계층에 대해서 정리해보았음
- 다음으로는 응답시간(latency) 개선
- Cache
- Contents Delivery Network, CDN
4. 캐시 (Cache)
- 값비싼 연산 결과 또는 자주 참조되는 데이터를 메모리에 저장 -> 같은 요청을 메모리에 있는 값으로 빨리 처리
- fig1-6에서는 웹 페이지를 새로고침 할 때 마다 표시되는 데이터를 가져오기 위해 데이터베이스 호출이 발생
- 어플리케이션의 성능은 데이터베이스를 얼마나 자주 호출하느냐에 좌우되는데, 캐시는 이를 완화시켜 줌
4-1. 캐시 계층 (Cache Tier)
fig1-8 (TODO:)
- 캐시 계층(cache tier)은 데이터가 잠시 보관되는 곳으로, DB보다 훨씬 빠름
- 별도의 캐시 계층을 두면 성능 개선 뿐만 아니라 DB의 부하를 줄일 수 있음
- 캐시 계층의 규모를 독립적으로 확장시키는 것도 가능
캐시계층의 작동 방식
- 읽기 주도형 캐시 전략(read-through caching strategy)
- 요청을 받은 웹서버는 캐시에 응답이 저장되어 있는지 확인
- 저장되어 있다면 해당 데이터를 client에 반환
- 없는 경우에는 db query를 통해 데이터를 찾아 캐시에 저장 -> client에 반환
- 이외에도 다양한 캐시 전략 존재
- 캐시할 데이터 종류, 크기, 액세스 패턴에 맞게 전략 선택
- 캐시 전략에 대한 비교 연구 결과
- 캐시 서버는 널리쓰이는 프로그래밍 언어로 API를 제공하여 사용하기 간단하다.
캐시 사용 시 유의할 점
- 캐시는 어떤 상황에 바람직한가?
- 데이터 갱신은 자주 일어나지 않지만, 참조는 빈번하게 일어날 때
- 어떤 데이터를 캐시에 두어야 하는가?
- 캐시는 데이터를 휘발성 메모리에 두므로, persistent data를 캐시에 두면 안된다.
- 데이터 만료(expire)는 어떻게 하는가?
- 만료 정책을 세워야한다. 만료 정책이 없으면 데이터는 캐시에 계속 남게 된다.
- 만료 기한이 너무 짧을 때 : 데이터베이스를 너무 자주 읽게 됨
- 만료 기한이 너무 길 때 : 원본과 차이가 날 가능성이 높아짐
- 일관성(consistency) 유지는?
- 일관성 : 데이터 저장소의 원본과 캐시 내의 사본이 같은지 여부
- 저장소의 원본을 갱신하는 연산과 캐시를 갱신하는 연산이 단일 트랜잭션으로 처리되지 않는 경우, 이 일관성은 깨질 수 있음
- 여러 지역에 걸쳐 시스템을 확장해 나가는 경우 캐시와 저장소 사이의 일관성을 유지하는 것을 어려운 문제, Scaling Memcache at Facebook참고
- 장애 대처는?
- 캐시 서버를 한 대만 두는 경우 해당 서버는 단일 장애 지점(Single Point of Failure, SPOF)이 되어버릴 가능성이 있다.
- 따라서 SPOF를 피하려면 여러 지여기에 걸쳐 캐시 서버를 분산해야 함
- 캐시 메모리는 얼마나 크게 잡을 것인가?
- 캐시 메모리가 너무 작을 때 : 데이터가 너무 빨리 캐시에서 밀려나버려(eviction), 캐시의 성능이 하락
- 캐시 메모리 과할당 (overprovision)으로 해결
- 데이터 방출(eviction) 정책은?
- 캐시가 꽉 차버리면 추가로 캐시에 데이터를 넣어야 할 경우 기존 데이터를 내보내야 함
- LRU(Least Recently Used) : 마지막으로 사용된 시점이 가장 오래된 데이터를 내보내는 정책
- LFU(Least Frequently Used) : 사용된 빈도가 가장 낮은 데이터를 내보내는 정책
- FIFO(First In Fisrt Out) : 가장 먼저 캐시에 들어온 데이터를 가장 먼저 내보내는 정책
5. 콘텐츠 전송 네트워크 (CDN)
- CDN : 정적 콘텐츠를 전송하는 데 쓰이는, 지리적으로 분산된 서버의 네트워크
- 이미지, 비디오, CSS, JavaScript 파일 등을 캐시 가능
- 동적 콘텐츠 캐싱은 상대적으로 새로운 개념으로써, 자세한 설명은 Amazon CloudFront Dynamic Content Delivery 참고
5-1. CDN 동작 방식
fig1-9 (TODO:)
- user가 웹사이트 방문 시, user와 가장 가까운 CDN 서버가 정적 콘텐츠 전달
- user가 CDN에서 멀면 멀수록 웹사이트는 천천히 로딩 됨
fig1-10 (TODO:)
- user A가 이미지 URL을 이용해 image.png에 접근. URL의 도메인은 CDN 서비스 사업자가 제공
- url ex: https://mysite.cloudfront.net/logo.net
- CDN 서버의 캐시에 해당 이미지가 없는 경우, 서버는 원본(origin) 서버에 요청하여 파일을 가져온다.
- 원본 서버가 파일을 CDN 서버에 반환, 응답의 HTTP 헤더에는 해당 파일이 얼마나 오래 캐시될 수 있는지를 설명하는 TTL(Time-To-Live)값이 들어있음.
- CDN 서버는 파일을 캐시하고 user A에게 반환, 이미지는 TTL에 명시된 시간이 끝날 때까지 캐시됨
- user B가 같은 이미지에 대한 요청을 CDN 서버에 전송
- 만료되지 않은 이미지에 대한 요청은 캐시를 통해 처리
5-2. CDN 사용 시 고려해야 할 사항
- 비용
- CDN은 보통 third-party provider에 의해 운영되며, CDN으로 들어가고 나가는 데이터 전송 양에 따라 요금을 내게 된다.
- 자주 사용되지 않는 콘텐츠를 캐싱하는 것은 이득이 크지 않으므로, CDN에서 빼는 것을 고려하자
- 적절한 만료 시한 설정
- time-sensitive 콘텐츠의 경우 만료시점을 잘 정해야 함
- 너무 길면 콘텐츠의 신선도가 떨어지고, 너무 짧으면 원본에 빈번하게 접속됨
- CDN 장애에 대한 대처 방안
- 일시적으로 CDN이 응답하지 않을 경우, 해당 문제를 감지하여 원본 서버로부터 직접 콘텐츠를 가져오도록 클라이언트를 구성하는 것이 필요
- 콘텐츠 무효화(invalidation) 방법
- 아직 만료되지 않은 콘텐츠여도 CDN에서 제거 가능
- CDN 서비스 사업자가 제공하는 API를 이용하여 콘텐츠 무효화
- 콘텐츠의 다른 버전을 서비스하도록 오브젝트 버저닝(object versioning) 이용. URL 마지막에 버전 번호를 인자로 주면 됨 (ex: image.png?v=2)
fig1-11 (TODO:) CDN과 캐시가 추가된 설계
- 정적 콘텐츠(JS, CSS, 이미지 등)는 더 이상 웹 서버를 통해 서비스하지 않으며, CDN을 통해 제공하여 더 나은 성능 보장
- 캐시가 데이터베이스 부하를 줄여줌
6. 무상태(stateless) 웹 계층
- 웹 계층을 수평적으로 확장하는 방법을 고민해보자
- 상태 정보(사용자 세션 데이터와 같은)를 웹 계층에서 제거해야 함
- 바람직한 전략은 상태 정보를 RDBMS나 NoSQL 같은 지속성 저장소에 보관하고, 필요할 때 가져오는 것
- 이렇게 구성된 웹 계층을 무상태 웹 계층이라 부름
6-1. 상태 정보 의존적인(stateful) 아키텍처
fig1-12 (TODO:)
- user A의 세션 정보나 프로파일 이미지 같은 상태 정보는 서버 1에 저장
- user A를 인증하기 위해 HTTP 요청은 반드시 버서1로 전송
- 요청이 서버 2로 전송되면 인증은 실패, 서버 2에 user A의 정보는 보관 x
- 위와 같이 문제는 같은 client로 부터 오는 요청은 항상 같은 서버로 전송 시켜야 함
- 대부분의 LB가 이를 지원하기 위해 sticky session이라는 기능 제공하지만, 이는 LB에 부담을 주고, LB에 서버를 추가하거나 제거하기도 까다로워짐
6-2. 무상태 (stateless) 아키텍처
fig1-13 (TODO:)
- 이 구조에서 사용자로부터의 HTTP요청은 어떤 웹 서버로도 전달 가능
- 상태 정보가 필요할 경우 공유 저장소(shared storage)로부터 데이터를 가져옴
- 상태 정보는 웹 서버로부터 물리적으로 분리되어 있음
- 이 구조는 단순하며, 안정적이며, 규모 확장이 쉬움
fig1-14 (TODO:) 기존 설계 -> stateless 아키텍처
- 세션 데이터를 웹 계층에서 분리하고 지속성 데이터를 보관소에 저장
- Relational DBMS
- Memcached/Redis와 같은 캐시 시스템
- NoSQL
- 자동 규모 확장(autoscaling) : 트래픽 양에 따라 웹 서버를 자동으로 추가하거나 삭제 기능
- 상태 정보가 웹 서버들로부터 제거되었으므로, 트래픽 양에 따라 웹 서버만 추가, 삭제 하면 됨
더 규모 확장?
- 전세계 서비스 제공을 위해서는 여러 데이터 센터를 지원하는 것이 필수
7. 데이터 센터
fig1-15 (TODO:)
- 지리적 라우팅 (geoDNS-routing or geo-routing) : user는 가장 가까운 데이터 센터로 안내
- 사용자의 위치에 따라 도메인 이름은 어떤 IP 주소로 변환할지 결정
fig1-16 (TODO:)
- 위 그림은 데이터센터2에 장애가 발생하여 모든 트래픽이 데이터센터 1로 전송되는 상황
7-1. 다중 데이터센터 아키텍처 고려사항
- 트래픽 우회
- 올바른 데이터 센터로 트래픽을 보내는 효과적인 방법을 찾아야 함
- GeoDNS는 사용자에게서 가장 가까운 데이터센터로 트래픽을 보낼 수 있도록 해줌
- 데이터 동기화(synchronization)
- 데이터 센터마다 별도의 데이터베이스를 사용한다면, 장애가 자동으로 복구되어(failover) 트래픽이 다른 데이터베이스로 우회된다해도, 해당 데이터센터에는 찾는 데이터가 없을 수 있음.
- 이런 상황을 막는 보편적 전략은 데이터를 여러 데이터센터에 걸쳐 다중화 하는 것
- Active-Active for Multi-Regional Resiliency 참고
- 테스트와 배포(deployment)
- 웹 사이트 또는 애플리케이션을 여러 위치에서 테스트 해보는 것이 중요
- 자동화된 배포 도구는 모든 데이터 센터에 동일한 서비스가 설치되도록 하는데 중요한 역할을 함
더 큰 규모?
- 시스템을 더 큰 규모로 확장하기 위해서는 컴포넌트들을 더 분리하여 각각이 독립적으로 확장할 수 있어야 함
- Message Queue는 많은 실제 분산 시스템이 이 문제를 풀기 위해 채용하고 있는 핵심적 전략 가운데 하나임
8. 메시지 큐 (Message Queue)
fig1-17 (TODO:)
8-1. 메시지 큐의 특성
- 메시지의 무손실(durability)을 보장하는 비동기 통신(asynchronous communication)을 지원하는 컴포넌트
- durability : 메시지 큐에 일단 보관된 메시지는 소비자가 꺼낼 때까지 안전히 보관되는 특성
- 메시지의 버퍼 역할을 하며, 비동기적으로 전송
8-2. 메시지 큐의 아키텍처
- 생산자 또는 발행자(producer/publisher)라고 불리는 입력 서비스가 메시지를 만들어 메시지 큐에 발행(publish)
- 큐에는 보통 소비자 혹은 구독자(consumer/subscriber)라 불리는 서비스 혹은 서버가 연결되어 있는데, 메시지를 받아 그에 맞는 동작을 수행
메시지 큐의 효과
- 서버 간 결합이 느슨해져서, 규모 확장성이 보장되어야 하는 안정적 애플리케이션 구성 가능
- 생산자는 소비자 프로세스가 다운되어 있어서 메시지 발행 가능
- 소비자는 생산자 서비스가 가용한 상태가 아니더라도 메시지를 수신 가능
9. 로그, 메트릭 그리고 자동화
- 몇 개 서버에서 실행되는 소규모 웹 사이트를 만들 때는 로그나 메트릭(metric), 자동화(automation) 같은 것은 하면 좋지만 꼭 할 필요는 x
- 하지만, 규모가 커지고 나면 그런 도구는 필수적
로그
- 에러 로그를 모니터링하는 것은 중요
- 시스템의 오류와 문제들을 보다 쉽게 찾아낼 수 있도록 함
- 서버 단위로도 모티너링 가능하지만, 로그를 단일 서비스로 모아주는 도구를 활용하면 더 편리
메트릭
- 메트릭을 잘 수집하면 사업 현황에 관한 유용한 정보 획득 가능
- 시스템의 현재 상태를 손쉽게 파악 가능
- 메트릭 중에 특별히 유용한 것들
- 호스트 단위 메트릭 : CPU, 메모리, 디스크I/O에 관한 메트릭
- 종합(aggregated) 메트릭 : 데이터베이스 계층의 성능, 캐시 계층의 성능 등
- 핵심 비즈니스 메트릭 : 일별 능동 사용자(daily active user), 수익(revenue), 재방문(retention) 등
자동화
- 생상성을 높이기 위해 자동화 도구 활용 필요
- CI/CD등
메시지 큐, 로그, 메트릭, 자동화 등을 반영하여 수정한 설계안
fig1-19 (TODO:)
- 메시지 큐는 각 컴포넌트가 보다 느슨히 결합(loosely coupled)될 수 있도록 하고, 결함에 대한 내성을 높인다.
- 로그, 모니터링, 메트릭, 자동화 등을 지원하기 위한 장치 추가
10. 데이터베이스의 규모 확장
10-1. 수직적 확장
- 기존서버에 더 많은, 더 고성능의 자원(CPU, RAM, 디스크 등)을 증설하는 방법
- 한계
- 데이터베이스 서버 하드웨어에는 한계가 있으므로 CPU, RAM등을 무한 증설 불가
- SPOF(Single Point of Failure)로 인한 위험성이 큼
- 비용이 많이 든다
10-2. 수평적 확장
fig1-20 (TODO:)
- 데이터베이스의 수평적 확장은 샤딩(sharding)이라고도 부름, 서버를 추가함으로써 성능을 향상 시킬 수 있도록 함
- 대규모 데이터베이스를 샤드(shard)라고 부르는 작은 단위로 분할
- 모든 샤드는 같은 스키마를 쓰지만 샤드에 보관되는 데이터 사이에는 중복이 없음
- 샤딩 전략 구현 시 고려해야 할 점
- 샤딩키 설정 : 데이터가 어떻게 분산될지 정하는 하나 이상의 칼럼으로 구성
샤딩 도입 시 고려해야 할 점
- 데이터의 재 샤딩(resharding)
- 데이터가 너무 많아져서 하나의 샤드로는 더 이상 감당하기 어려울 때
- 샤드 간 데이터 분포가 균등하지 못하여, 특정 샤드에 할당된 공간 소모가 다른 샤드에 비해 빨리 진행 될 떄 (샤드 소진, shard exhaustion)
- 샤드 소진 시 : 샤드 키를 계산하는 함수를 변경하고 데이터 재배치, 5장에서 다룰 안정 해시(consistent hashing) 기법을 활용하여 해결 가능
- 유명인사 문제
- hotspot key 라고도 하는데, 특정 샤드에 질의가 집중되어 서버에 과부하가 걸리는 문제
- SNS 앱에서 특정 셀럽들에게 read 연산 과부하가 걸릴 수 있음
- 조인과 비정규화 (join and denornalization)
- 일단 하나의 데이터베이스를 여러 샤드 서버로 쪼개고 나면, 여러 샤드에 걸친 데이터를 조인하기가 힘들어짐
- 데이터베이스를 비정규과하여 하나의 테이블에서 질의가 수행될 수 있도록 해야함(??)
fig1-23 (TODO:)
11. 백만 사용자, 그리고 그 이상
- 시스템 규모를 확장하는 것은 지속적이고 반복적(iterative)한 과정
- 수백만 사용자를 지원하기 위해서는 더욱 새로운 전략을 도입해야할 수도 있음
기법 정리
- 웹 계층은 무상태(stateless) 게층으로
- 모든 계층에 다중화 도입
- 가능한 한 많은 데이터를 캐싱할 것
- 여러 데이터 센터를 지원할 것
- 정적 콘텐츠는 CDN을 통해 서비스할 것
- 데이터 계층은 샤딩을 통해 그 규모를 확장할 것
- 각 계층은 독립적 서비스로 분할할 것
- 시스템을 지속적으로 모니터링하고, 자동화 도구들을 활용할 것
참고자료
- 알렉스 쉬, 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1장
사용자 수에 따른 규모 확장성
- 수많은 사용자를 지원하는 시스템은 지속적인 계량과 끝없는 개선이 필요
- 1 user -> ~M users 를 지원하는 시스템 설계
1. 단일 서버
- 단일 서버는 하나의 서버에 웹 앱, 데이터베이스, 캐시 등을 모두 실행 fig1-1 fig1-2
1-1. 사용자 요청 처리 흐름
- 사용자는 도메인 이름 (ex: naver.com)을 이용하여 웹사이트에 접속
- 도메일 이름을 IP주소로 변환하는 과정 : DNS (Domain Name Service) 질의
- DNS는 third party 유료 서비스이므로 우리 시스템의 일부는 아님
- DNS 조회 결과로 IP주소 반환 (ex: 123.123.123.123)
- 해당 IP주소로 HTTP 요청 전달
- 요청을 받은 웹서버가 응답 반환 (HTML, JSON, …)
1-2. 실제 요청 유형
- 웹 애플리케이션 (Web Application)
- 비즈니스 로직, 데이터 저장 … : 서버 구현용 언어 (Python, Java …)
- 프레젠테이션 : 클라이언트 구현용 언어 (HTML(…언어?), javascript)
- 모바일 앱 (Mobile Application)
- 모바일 앱과 웹 서버간 통신 필요
- 프로토콜 : HTTP
- 데이터 포맷 : JSON이 널리 사용
- 모바일 앱과 웹 서버간 통신 필요
2. 데이터베이스
- 사용자가 늘면 서버를 늘려야 함
- 웹/모바일 트래픽 처리 용도
- 데이터베이스 용도
- 분리 후 각각을 독립적으로 확장 fig1-3
2-1. 데이터베이스 선택
- 데이터베이스는 크게 2가지
- RDBMS(Relational Database Management System) : MySQL, Oracle Database, PostgreSQL, …
- 자료를 table, row, column으로 표현
- 여러 테이블에 있는 자료를 join 가능
- NoSQL : CouchDB, Neo4j, Cassandra, HBase, …
- NoSQL은 다시 4가지로 분류 가능
- key-value store
- graph store
- column store
- document store
- NoSQL은 다시 4가지로 분류 가능
- RDBMS(Relational Database Management System) : MySQL, Oracle Database, PostgreSQL, …
- 보통 RDBMS를 많이 선택
- 하지만 다음과 같은 경우 NoSQL이 좋은 선택일 수 있음
- 아주 낮은 응답 지연시간 요구
- 다루는 데이터가 정형 데이터가 아닌 비정형 데이터 (unstructured data)인 경우
- 데이터 (JSON, YAML, XML 등)를 직렬화(serialize)하거나 역직렬화(deserialize)할 수 있기만 하면 됨
- 아주 많은 양의 데이터를 저장할 필요가 없음
3. Scale Up vs Scale Out
Scale Up
- 수직적 규모 확장 (vertical scaling) 프로세스
- 단일 서버에 고사양 자원 (더 좋은 CPU, 더 많은 RAM 등)을 추가하는 행위
Scale Out
- 수평적 규모 확장 프로세스
- 더 많은 서버를 추가하여 성능을 개선
선택 기준은?
- 서버로 유입되는 트래픽이 많지 않을때는 scale up이 좋은 선택 (단순하게 확장 가능)
- 하지만 scale up에는 심각한 단점이 존재
- 확장의 한계 존재 (단일 서버에 CPU, RAM을 무한정 증설할 수 없음)
- 자동복구(failover) 방안이나 다중화(redundancy) 방안을 제시하지 않음
- 단일 서버 운영시, 서버에 장애가 발생하면 웹사이트/앱은 완전히 중단됨
- 위 단점들로 인해, 대규모 애플리케이션의 경우 수평적 규모 확장법이 적절함
- 위 설계 (fig1-1)의 경우, 사용자가 많아져 웹서버가 한계 상황에 도달하게 되면 응답속도가 느려지거나, 서버 접속이 불가능해질 수도 있다.
- 문제 해결을 위해서 로드밸런서(Load Balancer)를 도입
3-1. 로드밸런서(Load Balancer)
- 로드밸런서는 load balancing set에 속한 웹 서버들에게 트래픽 부하를 고르게 분산함
fig1-4
- fig 1-4와 같이 user는 LB의 Public IP 주소로 접속
- 웹서버는 user의 요청 직접 처리 x
- 보안을 위해 서버간 통신은 private IP 주소 사용
- load balancing set에 또 하나의 웹 서버를 추가하고나면 failover 가능, availibility 향상
- 서버 1이 다운되면 모든 트래픽은 서버 2로 전송 (failover)
- 사이트 전체 다운 x
- 부하를 나누기 위해 새로운 서버를 추가 가능
- 웹사이트로 유입되는 트래픽이 가파르게 증가하면, 간단하게 LB에 붙어있는 LB set에 새로운 서버를 추가하여 트래픽 분산 가능 (avaliliblity 향상)
- 서버 1이 다운되면 모든 트래픽은 서버 2로 전송 (failover)
- 웹 계층은 OK, 하지만 데이터 계층은? 데이터 계층도 다중화가 필요하다.
3-2. 데이터베이스 다중화
fig1-5 (TODO:)
- 많은 DBMS가 다중화를 지원 : 서버 사이 master - slave 관계 설정, 원본은 master, 사본은 slave에 저장
- 쓰기 연산 (write operation - insert, delete, update 등)은 master에서만 지원
- 읽기 연산 (read opreration)은 slave에서 지원
- 대부분의 애플리케이션은 읽기 연산 » 쓰기 연산 이므로 통상 slave가 master보다 많음
DB 다중화로 인한 이득
- 더 나은 성능 : read operation이 부 데이터베이스 서버들로 분산되므로, 병렬 처리 가능한 query가 늘어남
- 안정성 (reliability) : 데이터베이스 서버 가운데 일부가 crash되어도 다른 서버 덕분에 데이터는 보존
- 가용성 (availibility) : 데이터를 여러 지역에 복제해 둠으로써, 하나의 데이터 서버에 장애가 발생해도 다른 서버에 있는 데이터를 가져와 계속 서비스 가능
DB 다중화시 장애 상황 시나리오
- 부 서버 다운 시나리오
- 부 서버가 한 대 뿐인데 다운된 경우
- 읽기 연산은 한시적으로 주 데이터베이스로 전달
- 새로운 부 데이터 서버가 장애 서버 대체
- 부 서버가 여러대인 경우
- 읽기 연산은 나머지 부 데이터 베이스로 전달
- 새로분 부 데이터베이스 서버 추가하여 대체
- 부 서버가 한 대 뿐인데 다운된 경우
- 주 서버 다운 시나리오
- 부 데이터베이스 서버가 새로운 주 서버가 됨
- 모든 db연산은 일시적으로 새로운 서버상에서 수행
- 새로운 부 서버 추가
- production 환경에서 주 서버 다운 장애는 위 보다 훨씬 복잡함
- 부 서버에 보관된 데이터가 최신 상태가 아닐 수 있음
- 없는 데이터는 recovery script를 돌려서 추가해야함
- 다중 마스터 (multi-masters)나 원형 다중화(circular replication) 방식 도입 가능
- 위에 대한 자세한 논의는 참고문헌 (multi-master replication, NDB Cluster Replication)을 참고
3-3. 로드밸런서 + DB 다중화
fig1-6 (TODO:)
- user는 DNS로부터 LB의 Public IP주소를 받음
- 해당 IP주소를 사용해 LB에 접속
- HTTP요청은 서버 1 또는 서버 2로 전달
- 웹 서버는 사용자의 데이터를 부 데이터베이스 서버에서 읽음
- 웹 서버는 데이터 변경 연산은 주 데이터 베이스로 전달 (insert, delete, update)
3-4. 마무리
- 웹 계층과 데이터 계층에 대해서 정리해보았음
- 다음으로는 응답시간(latency) 개선
- Cache
- Contents Delivery Network, CDN
4. 캐시 (Cache)
- 값비싼 연산 결과 또는 자주 참조되는 데이터를 메모리에 저장 -> 같은 요청을 메모리에 있는 값으로 빨리 처리
- fig1-6에서는 웹 페이지를 새로고침 할 때 마다 표시되는 데이터를 가져오기 위해 데이터베이스 호출이 발생
- 어플리케이션의 성능은 데이터베이스를 얼마나 자주 호출하느냐에 좌우되는데, 캐시는 이를 완화시켜 줌
4-1. 캐시 계층 (Cache Tier)
fig1-8 (TODO:)
- 캐시 계층(cache tier)은 데이터가 잠시 보관되는 곳으로, DB보다 훨씬 빠름
- 별도의 캐시 계층을 두면 성능 개선 뿐만 아니라 DB의 부하를 줄일 수 있음
- 캐시 계층의 규모를 독립적으로 확장시키는 것도 가능
캐시계층의 작동 방식
- 읽기 주도형 캐시 전략(read-through caching strategy)
- 요청을 받은 웹서버는 캐시에 응답이 저장되어 있는지 확인
- 저장되어 있다면 해당 데이터를 client에 반환
- 없는 경우에는 db query를 통해 데이터를 찾아 캐시에 저장 -> client에 반환
- 요청을 받은 웹서버는 캐시에 응답이 저장되어 있는지 확인
- 이외에도 다양한 캐시 전략 존재
- 캐시할 데이터 종류, 크기, 액세스 패턴에 맞게 전략 선택
- 캐시 전략에 대한 비교 연구 결과
- 캐시 서버는 널리쓰이는 프로그래밍 언어로 API를 제공하여 사용하기 간단하다.
캐시 사용 시 유의할 점
- 캐시는 어떤 상황에 바람직한가?
- 데이터 갱신은 자주 일어나지 않지만, 참조는 빈번하게 일어날 때
- 어떤 데이터를 캐시에 두어야 하는가?
- 캐시는 데이터를 휘발성 메모리에 두므로, persistent data를 캐시에 두면 안된다.
- 데이터 만료(expire)는 어떻게 하는가?
- 만료 정책을 세워야한다. 만료 정책이 없으면 데이터는 캐시에 계속 남게 된다.
- 만료 기한이 너무 짧을 때 : 데이터베이스를 너무 자주 읽게 됨
- 만료 기한이 너무 길 때 : 원본과 차이가 날 가능성이 높아짐
- 일관성(consistency) 유지는?
- 일관성 : 데이터 저장소의 원본과 캐시 내의 사본이 같은지 여부
- 저장소의 원본을 갱신하는 연산과 캐시를 갱신하는 연산이 단일 트랜잭션으로 처리되지 않는 경우, 이 일관성은 깨질 수 있음
- 여러 지역에 걸쳐 시스템을 확장해 나가는 경우 캐시와 저장소 사이의 일관성을 유지하는 것을 어려운 문제, Scaling Memcache at Facebook참고
- 장애 대처는?
- 캐시 서버를 한 대만 두는 경우 해당 서버는 단일 장애 지점(Single Point of Failure, SPOF)이 되어버릴 가능성이 있다.
- 따라서 SPOF를 피하려면 여러 지여기에 걸쳐 캐시 서버를 분산해야 함
- 캐시 메모리는 얼마나 크게 잡을 것인가?
- 캐시 메모리가 너무 작을 때 : 데이터가 너무 빨리 캐시에서 밀려나버려(eviction), 캐시의 성능이 하락
- 캐시 메모리 과할당 (overprovision)으로 해결
- 캐시 메모리가 너무 작을 때 : 데이터가 너무 빨리 캐시에서 밀려나버려(eviction), 캐시의 성능이 하락
- 데이터 방출(eviction) 정책은?
- 캐시가 꽉 차버리면 추가로 캐시에 데이터를 넣어야 할 경우 기존 데이터를 내보내야 함
- LRU(Least Recently Used) : 마지막으로 사용된 시점이 가장 오래된 데이터를 내보내는 정책
- LFU(Least Frequently Used) : 사용된 빈도가 가장 낮은 데이터를 내보내는 정책
- FIFO(First In Fisrt Out) : 가장 먼저 캐시에 들어온 데이터를 가장 먼저 내보내는 정책
5. 콘텐츠 전송 네트워크 (CDN)
- CDN : 정적 콘텐츠를 전송하는 데 쓰이는, 지리적으로 분산된 서버의 네트워크
- 이미지, 비디오, CSS, JavaScript 파일 등을 캐시 가능
- 동적 콘텐츠 캐싱은 상대적으로 새로운 개념으로써, 자세한 설명은 Amazon CloudFront Dynamic Content Delivery 참고
5-1. CDN 동작 방식
fig1-9 (TODO:)
- user가 웹사이트 방문 시, user와 가장 가까운 CDN 서버가 정적 콘텐츠 전달
- user가 CDN에서 멀면 멀수록 웹사이트는 천천히 로딩 됨
fig1-10 (TODO:)
- user A가 이미지 URL을 이용해 image.png에 접근. URL의 도메인은 CDN 서비스 사업자가 제공
- url ex: https://mysite.cloudfront.net/logo.net
- CDN 서버의 캐시에 해당 이미지가 없는 경우, 서버는 원본(origin) 서버에 요청하여 파일을 가져온다.
- 원본 서버가 파일을 CDN 서버에 반환, 응답의 HTTP 헤더에는 해당 파일이 얼마나 오래 캐시될 수 있는지를 설명하는 TTL(Time-To-Live)값이 들어있음.
- CDN 서버는 파일을 캐시하고 user A에게 반환, 이미지는 TTL에 명시된 시간이 끝날 때까지 캐시됨
- user B가 같은 이미지에 대한 요청을 CDN 서버에 전송
- 만료되지 않은 이미지에 대한 요청은 캐시를 통해 처리
5-2. CDN 사용 시 고려해야 할 사항
- 비용
- CDN은 보통 third-party provider에 의해 운영되며, CDN으로 들어가고 나가는 데이터 전송 양에 따라 요금을 내게 된다.
- 자주 사용되지 않는 콘텐츠를 캐싱하는 것은 이득이 크지 않으므로, CDN에서 빼는 것을 고려하자
- 적절한 만료 시한 설정
- time-sensitive 콘텐츠의 경우 만료시점을 잘 정해야 함
- 너무 길면 콘텐츠의 신선도가 떨어지고, 너무 짧으면 원본에 빈번하게 접속됨
- CDN 장애에 대한 대처 방안
- 일시적으로 CDN이 응답하지 않을 경우, 해당 문제를 감지하여 원본 서버로부터 직접 콘텐츠를 가져오도록 클라이언트를 구성하는 것이 필요
- 콘텐츠 무효화(invalidation) 방법
- 아직 만료되지 않은 콘텐츠여도 CDN에서 제거 가능
- CDN 서비스 사업자가 제공하는 API를 이용하여 콘텐츠 무효화
- 콘텐츠의 다른 버전을 서비스하도록 오브젝트 버저닝(object versioning) 이용. URL 마지막에 버전 번호를 인자로 주면 됨 (ex: image.png?v=2)
fig1-11 (TODO:) CDN과 캐시가 추가된 설계
- 정적 콘텐츠(JS, CSS, 이미지 등)는 더 이상 웹 서버를 통해 서비스하지 않으며, CDN을 통해 제공하여 더 나은 성능 보장
- 캐시가 데이터베이스 부하를 줄여줌
6. 무상태(stateless) 웹 계층
- 웹 계층을 수평적으로 확장하는 방법을 고민해보자
- 상태 정보(사용자 세션 데이터와 같은)를 웹 계층에서 제거해야 함
- 바람직한 전략은 상태 정보를 RDBMS나 NoSQL 같은 지속성 저장소에 보관하고, 필요할 때 가져오는 것
- 이렇게 구성된 웹 계층을 무상태 웹 계층이라 부름
6-1. 상태 정보 의존적인(stateful) 아키텍처
fig1-12 (TODO:)
- user A의 세션 정보나 프로파일 이미지 같은 상태 정보는 서버 1에 저장
- user A를 인증하기 위해 HTTP 요청은 반드시 버서1로 전송
- 요청이 서버 2로 전송되면 인증은 실패, 서버 2에 user A의 정보는 보관 x
- 위와 같이 문제는 같은 client로 부터 오는 요청은 항상 같은 서버로 전송 시켜야 함
- 대부분의 LB가 이를 지원하기 위해 sticky session이라는 기능 제공하지만, 이는 LB에 부담을 주고, LB에 서버를 추가하거나 제거하기도 까다로워짐
6-2. 무상태 (stateless) 아키텍처
fig1-13 (TODO:)
- 이 구조에서 사용자로부터의 HTTP요청은 어떤 웹 서버로도 전달 가능
- 상태 정보가 필요할 경우 공유 저장소(shared storage)로부터 데이터를 가져옴
- 상태 정보는 웹 서버로부터 물리적으로 분리되어 있음
- 이 구조는 단순하며, 안정적이며, 규모 확장이 쉬움
fig1-14 (TODO:) 기존 설계 -> stateless 아키텍처
- 세션 데이터를 웹 계층에서 분리하고 지속성 데이터를 보관소에 저장
- Relational DBMS
- Memcached/Redis와 같은 캐시 시스템
- NoSQL
- 자동 규모 확장(autoscaling) : 트래픽 양에 따라 웹 서버를 자동으로 추가하거나 삭제 기능
- 상태 정보가 웹 서버들로부터 제거되었으므로, 트래픽 양에 따라 웹 서버만 추가, 삭제 하면 됨
더 규모 확장?
- 전세계 서비스 제공을 위해서는 여러 데이터 센터를 지원하는 것이 필수
7. 데이터 센터
fig1-15 (TODO:)
- 지리적 라우팅 (geoDNS-routing or geo-routing) : user는 가장 가까운 데이터 센터로 안내
- 사용자의 위치에 따라 도메인 이름은 어떤 IP 주소로 변환할지 결정
fig1-16 (TODO:)
- 위 그림은 데이터센터2에 장애가 발생하여 모든 트래픽이 데이터센터 1로 전송되는 상황
7-1. 다중 데이터센터 아키텍처 고려사항
- 트래픽 우회
- 올바른 데이터 센터로 트래픽을 보내는 효과적인 방법을 찾아야 함
- GeoDNS는 사용자에게서 가장 가까운 데이터센터로 트래픽을 보낼 수 있도록 해줌
- 데이터 동기화(synchronization)
- 데이터 센터마다 별도의 데이터베이스를 사용한다면, 장애가 자동으로 복구되어(failover) 트래픽이 다른 데이터베이스로 우회된다해도, 해당 데이터센터에는 찾는 데이터가 없을 수 있음.
- 이런 상황을 막는 보편적 전략은 데이터를 여러 데이터센터에 걸쳐 다중화 하는 것
- Active-Active for Multi-Regional Resiliency 참고
- 테스트와 배포(deployment)
- 웹 사이트 또는 애플리케이션을 여러 위치에서 테스트 해보는 것이 중요
- 자동화된 배포 도구는 모든 데이터 센터에 동일한 서비스가 설치되도록 하는데 중요한 역할을 함
더 큰 규모?
- 시스템을 더 큰 규모로 확장하기 위해서는 컴포넌트들을 더 분리하여 각각이 독립적으로 확장할 수 있어야 함
- Message Queue는 많은 실제 분산 시스템이 이 문제를 풀기 위해 채용하고 있는 핵심적 전략 가운데 하나임
8. 메시지 큐 (Message Queue)
fig1-17 (TODO:)
8-1. 메시지 큐의 특성
- 메시지의 무손실(durability)을 보장하는 비동기 통신(asynchronous communication)을 지원하는 컴포넌트
- durability : 메시지 큐에 일단 보관된 메시지는 소비자가 꺼낼 때까지 안전히 보관되는 특성
- 메시지의 버퍼 역할을 하며, 비동기적으로 전송
8-2. 메시지 큐의 아키텍처
- 생산자 또는 발행자(producer/publisher)라고 불리는 입력 서비스가 메시지를 만들어 메시지 큐에 발행(publish)
- 큐에는 보통 소비자 혹은 구독자(consumer/subscriber)라 불리는 서비스 혹은 서버가 연결되어 있는데, 메시지를 받아 그에 맞는 동작을 수행
메시지 큐의 효과
- 서버 간 결합이 느슨해져서, 규모 확장성이 보장되어야 하는 안정적 애플리케이션 구성 가능
- 생산자는 소비자 프로세스가 다운되어 있어서 메시지 발행 가능
- 소비자는 생산자 서비스가 가용한 상태가 아니더라도 메시지를 수신 가능
9. 로그, 메트릭 그리고 자동화
- 몇 개 서버에서 실행되는 소규모 웹 사이트를 만들 때는 로그나 메트릭(metric), 자동화(automation) 같은 것은 하면 좋지만 꼭 할 필요는 x
- 하지만, 규모가 커지고 나면 그런 도구는 필수적
로그
- 에러 로그를 모니터링하는 것은 중요
- 시스템의 오류와 문제들을 보다 쉽게 찾아낼 수 있도록 함
- 서버 단위로도 모티너링 가능하지만, 로그를 단일 서비스로 모아주는 도구를 활용하면 더 편리
메트릭
- 메트릭을 잘 수집하면 사업 현황에 관한 유용한 정보 획득 가능
- 시스템의 현재 상태를 손쉽게 파악 가능
- 메트릭 중에 특별히 유용한 것들
- 호스트 단위 메트릭 : CPU, 메모리, 디스크I/O에 관한 메트릭
- 종합(aggregated) 메트릭 : 데이터베이스 계층의 성능, 캐시 계층의 성능 등
- 핵심 비즈니스 메트릭 : 일별 능동 사용자(daily active user), 수익(revenue), 재방문(retention) 등
자동화
- 생상성을 높이기 위해 자동화 도구 활용 필요
- CI/CD등
메시지 큐, 로그, 메트릭, 자동화 등을 반영하여 수정한 설계안
fig1-19 (TODO:)
- 메시지 큐는 각 컴포넌트가 보다 느슨히 결합(loosely coupled)될 수 있도록 하고, 결함에 대한 내성을 높인다.
- 로그, 모니터링, 메트릭, 자동화 등을 지원하기 위한 장치 추가
10. 데이터베이스의 규모 확장
10-1. 수직적 확장
- 기존서버에 더 많은, 더 고성능의 자원(CPU, RAM, 디스크 등)을 증설하는 방법
- 한계
- 데이터베이스 서버 하드웨어에는 한계가 있으므로 CPU, RAM등을 무한 증설 불가
- SPOF(Single Point of Failure)로 인한 위험성이 큼
- 비용이 많이 든다
10-2. 수평적 확장
fig1-20 (TODO:)
- 데이터베이스의 수평적 확장은 샤딩(sharding)이라고도 부름, 서버를 추가함으로써 성능을 향상 시킬 수 있도록 함
- 대규모 데이터베이스를 샤드(shard)라고 부르는 작은 단위로 분할
- 모든 샤드는 같은 스키마를 쓰지만 샤드에 보관되는 데이터 사이에는 중복이 없음
- 샤딩 전략 구현 시 고려해야 할 점
- 샤딩키 설정 : 데이터가 어떻게 분산될지 정하는 하나 이상의 칼럼으로 구성
샤딩 도입 시 고려해야 할 점
- 데이터의 재 샤딩(resharding)
- 데이터가 너무 많아져서 하나의 샤드로는 더 이상 감당하기 어려울 때
- 샤드 간 데이터 분포가 균등하지 못하여, 특정 샤드에 할당된 공간 소모가 다른 샤드에 비해 빨리 진행 될 떄 (샤드 소진, shard exhaustion)
- 샤드 소진 시 : 샤드 키를 계산하는 함수를 변경하고 데이터 재배치, 5장에서 다룰 안정 해시(consistent hashing) 기법을 활용하여 해결 가능
- 유명인사 문제
- hotspot key 라고도 하는데, 특정 샤드에 질의가 집중되어 서버에 과부하가 걸리는 문제
- SNS 앱에서 특정 셀럽들에게 read 연산 과부하가 걸릴 수 있음
- 조인과 비정규화 (join and denornalization)
- 일단 하나의 데이터베이스를 여러 샤드 서버로 쪼개고 나면, 여러 샤드에 걸친 데이터를 조인하기가 힘들어짐
- 데이터베이스를 비정규과하여 하나의 테이블에서 질의가 수행될 수 있도록 해야함(??)
fig1-23 (TODO:)
11. 백만 사용자, 그리고 그 이상
- 시스템 규모를 확장하는 것은 지속적이고 반복적(iterative)한 과정
- 수백만 사용자를 지원하기 위해서는 더욱 새로운 전략을 도입해야할 수도 있음
기법 정리
- 웹 계층은 무상태(stateless) 게층으로
- 모든 계층에 다중화 도입
- 가능한 한 많은 데이터를 캐싱할 것
- 여러 데이터 센터를 지원할 것
- 정적 콘텐츠는 CDN을 통해 서비스할 것
- 데이터 계층은 샤딩을 통해 그 규모를 확장할 것
- 각 계층은 독립적 서비스로 분할할 것
- 시스템을 지속적으로 모니터링하고, 자동화 도구들을 활용할 것
참고자료
- 알렉스 쉬, 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1장
Comments