1장. 사용자 수에 따른 규모 확장성
단일 서버
- 모든 컴포넌트가 단 한 대 서버에서 실행되는 간단한 시스템
- 웹, 앱, 데이터베이스, 캐시 등이 전부 한 대 서버에서 실행된다.
</img>
- 사용자 요청 처리 흐름
- 사용자는 도메인 주소를 이용하여 웹 사이트에 접속
- DNS 조회 결과로 IP 주소가 반환
- 해당 IP 주소로 HTTP 요청 전달
- 요청을 받은 웹 서버는 HTML 페이지나 JSON 형태의 응답을 반환
데이터베이스
- 사용자가 늘면 하나로 충분하지 않아 여러 서버를 두어야 한다.
- 하나는 웹/모바일 트리팩 처리용도, 하나는 데이터베이스용
</img>
어떤 데이터베이스를 사용할 것인가?
- 전통적인 관계형 데이터베이스 vs 비-관계형 데이터베이스
- 관계형 데이터베이스, RDBMS
- MySQL, 오라클, PostgreSQL 등
- 자료를 테이블과 열, 칼럼으로 표현
- SQL로 여러 테이블의 데이터를 관계에 따라 조인하여 합칠 수 있다.
- 비 관계형 데이터베이스, NoSQL
- CouchDB, Neo4j, Cassandra, HBase, Amazon DynamoDB 등
- NoSQL은 또 다음 네 부류로 나눌 수 있다.
- 키-값 저장소 (key-value store)
- 그래프 저장소 (graph store)
- 칼럼 저장소 (column store)
- 문서 저장소 (document store)
- 일반적으로 조인 연산은 지원하지 않는다.
- 대부분 관계형 데이터베이스가 최선이지만 비 관계형 데이터베이스가 필요할 수도 있다.
- 아주 낮은 응답 지연시간(latency)이 요구됨
- 다루는 데이터가 비정형이라 관계형 데이터가 아님
- 데이터(JSON, YAML, XML 등)를 직렬화, 역직렬화할 수 있기만 하면 됨
- 아주 많은 양의 데이터를 저장할 필요가 있음
수직적 규모 확장 vs 수평적 규모 확장
스케일 업(scale up), 수직적 규모 확장
- 서버에 고사양 자원을 추가하는 행위 (더 좋은 CPU, 더 많은 RAM)
- 트래픽 양이 적을 땐 수직적 확장이 좋은 선택
- 가장 큰 장점은 단순함
- 스케일 업 단점
- 한 서버에 CPU나 메모리를 무한으로 증설할 수 없기에 한계가 존재
- 자동 복구 방안이나 다중화 방안을 제시하지 않는다.
스케일 아웃(scale out), 수평적 규모 확장
- 더 많은 서버를 추가하여 성능을 개선하는 행위
- 대규모 애플리케이션을 지원할 때 적절
로드밸런서
- 로드밸런서는 부하 분산 집합에 속한 서버들에게 트래픽을 고르게 분산하는 역할을 한다.
-
로드밸런서 동작
</img>
- 사용자는 로드밸런서의 public IP로 접속
- 웹 서버는 클라이언트 접속을 직접 처리하지 않는다.
- 보안을 위해 서버 간 통신에는 private IP를 사용
- 로드밸런서를 사용하면 장애 자동복구를 통해 가용성이 향상된다.
- 서버 1이 다운되면 모든 트래픽은 서버 2로 전송될 수 있다.
- 트래픽이 가파르게 증가했을 때 웹 서버 계층에 더 많은 서버를 추가하면 로드밸런서가 자동으로 트래픽을 분산시킨다.
데이터베이스 다중화
- 많은 DBMS가 다중화를 지원한다.
- 보통 서버 사이에 master-slave 관계를 설정
- 원본은 master 서버에, 사본은 slave 서버에 저장하는 방식
- 쓰기 연산은 master에서만 지원
- slave DB는 master에서 사본을 전달 받으며 읽기 연산만 지원
- 대부분 애플리케이션은 읽기 연산이 훨씬 많기에 통상 master보다 slave 수가 많다.
</img>
- 데이터베이스 다중화의 장점
- 더 나은 성능 - 쓰기와 읽기 연산이 여러 DB 서버에 분산
- 안정성 - 재해 등으로 DB 서버 일부가 다운되어도 데이터는 보존된다. (지역적으로 떨어진 장소에 다중화)
- 가용성 - 하나의 DB 서버에 장애가 발생해도 다른 서버의 데이터로 서비스를 계속할 수 있다.
- 데이터베이스 다중화 장애 복구 시나리오
- slave 서버가 한 대 뿐인데 다운된 경우라면 읽기 연산은 한시적으로 master로 전달되고 새로운 slave 서버가 장애 서버를 대체
- slave 서버가 여러대인 경우에는 나머지 slave 서버에 읽기 연산이 분산되고 새로운 slave가 대체될 것이다.
- master 서버가 한 대 뿐인데 다운된다면 slave 서버가 새로운 master 서버가 되고 새로운 slave를 추가한다.
- 운영 상황에서는 slave 데이터가 최신 상태가 아아닐 수 있기에 복구 스크립트를 돌려서 추가해야 한다.
- 다중 마스터(multi-masters)나 원형 다중화(circular replication)으로 해결할 수 있지만 훨씬 복잡하다.
캐시
캐시란 값비싼 연산 결과 또는 자주 참조되는 데이터를 메모리 안에 두고 뒤이은 요청이 보다 빨리 처리될 수 있도록 하는 저장소
캐시 계층
캐시 사용 시 유의할 점
- 캐시는 데이터 갱신은 자주 일어나지 않지만 참조는 빈번하게 일어난다면 고려해볼 만하다.
- 캐시는 데이터를 휘발성 메모리에 두므로 영속적으로 보관할 데이터를 캐시에 두는 것은 바람직하지 않다.
- 캐시에 저장된 데이터의 만료 정책을 마련해 두어야 한다.
- 만료 정책이 없으면 캐시 데이터는 계속 커진다.
- 만료가 너무 짧으면 DB를 너무 자주 읽어야 한다.
- 캐시 데이터와 지속적 저장소의 데이터의 일관성을 유지해야 한다.
- 지속적 저장소와 캐시를 갱신하는 연산이 단일 트랜잭션으로 처리되지 않으면 일관성이 깨진다.
- 여러 지역에 걸쳐 캐시 서버를 분산시켜야 한다.
- 캐시 서버를 한 대만 두는 경우 해당 서버는 단일 장애 지점(SPOF)가 되어버린다.
- 캐시 메모리 과할당을 고려해봐야 한다.
- 캐시 메모리가 너무 작으면 엑세스 패턴에 따라 데이터가 자주 캐시에서 밀려나 성능이 떨어진다.
- 캐시 메모리를 과할당하면 갑자기 캐시 데이터가 늘어났을 때 문제를 방지할 수 있다.
- 데이터 방출 정책을 고려해야 한다.
- LRU(Least Recently Used): 마지막으로 사용된 시점이 가장 오래된 데이터를 내보내는 정책
- LFU(Least Frequently Used): 사용된 빈도가 가장 낮은 데이터를 내보내는 정책
- FIFO(First In First Out): 가장 먼저 캐시에 들어온 데이터를 내보내는 정책
콘텐츠 전송 네트워크 (CDN)
- CDN이란
- 정적 콘텐츠를 전송할 수 있는 지리적으로 분산된 서버의 네트워크
- 이미지, 비디오, CSS, JavaScript 파일 등을 캐시할 수 있다.
- 간단하게 말하면 요청 경로, 쿼리 스트링, 쿠키, 요청 헤더 등 정보에 기반하여 HTML 페이지를 캐시하는 것
- 웹 서버 대신 사용할 수 있다.
- 캐시가 DB 부하를 줄여준다.
- 사용자가 웹사이트를 방문하면 그 사용자에게 가장 가까운 CDN 서버가 콘텐츠를 전달하게 된다.
- CDN도 읽기 주도형 캐시 전략처럼 동작한다.
CDN 사용 시 고려해야 할 사항
- 비용
- CDN은 보통 제 3 사업자에 의해 운영되어 데이터 전송 양에 따라 요금을 내게 된다.
- 자주 사용되지 않는 콘텐츠 캐싱은 이득이 크지 않아 CDN을 사용하지 않는 것을 추천한다.
- 적절한 만료 시한 설정
- 시의성이 중요한 콘텐츠의 경우 만료 시점이 중요하다.
- 너무 길면 콘텐츠 신선도가 떨어지고, 너무 짧으면 원본 서버 접근 빈도가 늘어나 좋지 않다.
- CDN 장애 대처 방안
- CDN 자체가 죽었을 때 웹사이트/애플리케이션이 어떻게 동작해야 하는지 고려해야 한다.
- 콘텐츠 무효화 방법 - 만료되지 않은 콘텐츠라 하더라도 CDN에서 제거할 수 있다.
- CDN 서비스 사업자가 제공하는 API를 이용
- 콘텐츠의 다른 버전을 서비스하도록 오브젝트 버저닝 이용
- ex)
image.png?v=2
처럼 버전 정보를 지정
무상태(stateless) 웹 계층
- 웹 계층을 수평적으로 확장하기 위해선 서버에서 상태 정보(세션 등)를 제거해야 한다.
- 바람직한 전략은 별도의 저장소에 상태 저장소를 보관하고 필요할 때 가져오는 것이다.
상태 정보 의존적인 아키텍처
- 서버가 상태 정보를 저장하게 되면 상태 정보 의존적 아키텍처가 된다.
- 사용자 A의 상태 정보를 서버 A가 가지고 있다면 사용자 A는 서버 A에만 요청을 보내야만 한다.
- 이런 방식을 고정 세션(sticky session)이라 한다.
- 로드밸런서에 부담을 주는 방식
- 서버를 추가하거나 제거하기도 까다롭고 장애 처리도 복잡해진다.
무상태 아키텍처
- 무상태 아키텍처에선 어떤 사용자의 요청이 어떤 서버로도 전달될 수 있다.
- 상태 정보가 필요한 경우엔 공유 저장소로부터 데이터를 가져온다.
- 상태 정보는 웹 서버로부터 물리적으로 분리되어 있어 단순하고 안정적이며 규모 확장이 쉽다.
- 상태 정보 저장소는 관계형 DB일 수도 있고 NoSQL일 수도 있다.
데이터 센터
- 두 개 이상의 데이터 센터를 이용하는 경우 장애가 없다면 지리적으로 가까운 데이터 센터로 요청이 전달된다.
- 지리적 라우팅 (geoDNS-rouding or geo-routing)
- 데이터 센터 중 하나에 장애가 발생하면 모든 트래픽은 장애가 없는 데이터 센터로 전송된다.
- 다중 데이터 센터 아키텍처 구축을 위한기술적 난제
- 트래픽 우회 - 올바른 데이터 센터로 트래픽을 보내는 효과적인 방법 찾기
- 데이터 동기화 - 장애가 복구되어 트래픽이 우회되어도 데이터 정합성이 안맞을 수도 있다.
- 테스트와 배포 - 자동화된 배포 도구를 통해 모든 데이터 센터에 동일한 서비스가 설치되도록 해야한다.
메시지 큐
- 메시지 큐는 메시지의 무손실을 보장하는 비동기 통신을 지원하는 컴포넌트이다.
- 생산자(producer/publisher)가 메시지 큐에 메시지를 발생(publish)하고 소비자(consumer/subscriber)가 메시지를 받아 동작을 수행
- 메시지 큐를 통해 서비스, 서버 간 결합이 느슨해져 규모 확장성이 보장되어 안정적 애플리케이션을 구성하기 좋다.
- 생산자는 소비자가 다운되어 있어도 메시지 발행 가능
- 소비자는 생산자가 다운되어 있어도 메시지 수신 가능
로그, 메트릭 그리고 자동화
- 웹 사이트와 사업 규모가 커지면 로그나 메트릭, 자동화가 필수이다.
- 로그
- 에러 로그를 모니터링하는 것은 중요
- 서버 단위로 모니터링하기 보다 로그를 단일 서비스로 모아주는 도구를 활용하면 더 편리하다.
- 메트릭
- 메트릭을 잘 수집하면 사업 현황에 관한 유용한 정보를 얻을 수 있다.
- 시스템 현재 상태 파악에 용이
- 호스트 단위 메트릭: CPU, 메모리, 디스크 I/O에 관한 메트릭
- 종합 메트릭: DB 계층의 성능, 캐시 계층의 성능
- 핵심 비즈니스 메트릭: DAU, 수익, 재방문 등
- 자동화
- 지속적 통합(continuous integration) 도구 활용
- 빌드, 테스트 배포를 자동화하면 개발 생산성을 크게 향상시킬 수 있다.
데이터베이스의 규모 확장
- 저장할 데이터가 많아지면 데이터베이스 증설이 필요
- 수직적 확장과 수평적 확장이 있다.
수직적 확장
- 기존 서버에 고성능 자원(CPU, RAM, 디스크 등)을 증설하는 방법
- AWS RDS에는 24TB RAM을 갖춘 서버도 제공한다.
- 수직적 접근법의 한계
- 서버 하드웨어의 한계로 CPU, RAM을 무한 증설할 수는 없다.
- SPOF(Single Point of Failure)가 되어버린다.
- 고성능 서버로 갈수록 비용이 많이 든다.
수평적 확장
- DB의 수평적 확장을 샤딩(sharding)이라고 부른다.
- 샤딩은 DB를 샤드(shard)라는 작은 단위로 분할한다.
- 모든 샤드는 같은 스키마를 갖지만 데이터 사이에 중복은 없다.
- 어떤 샤드에 어떤 데이터를 넣을지는 사용자 ID에 따라 정한다.
ex. user_id % 4
를 해시 함수로 사용하여 샤드를 결정
- 샤딩 전략에서 가장 중요한 것은 샤딩 키(sharding key)
- 샤딩 키란 데이터가 어떻게 분산될지 정하는 하나 이상의 칼럼
- 사딩 키를 정할 때는 데이터를 고르게 분할 할 수 있도록 하는 게 가장 중요하다.
샤딩 도입 시 풀어야 할 문제
- 데이터의 재 샤딩(resharding)
- 데이터가 많아져서 하나의 샤드로는 더 이상 감당하기 어려울 때
- 샤드 간 데이터 분포가 균등하지 못해 어떤 샤드 공간 소모가 다른 샤드에 비해 빠를 때
- 위 두 샤드 소진이 발생할 때 재 샤딩이 필요하다.
- 샤드 키 계산 함수를 변경하고 데이터를 재 배치
- 안정 해시 기법을 활용하여 해결할 수 있다.
- 유명인사(celebrity) 문제 (핫스팟 키 문제)
- 특정 샤드에 트래픽이 집중되어 과부하가 걸리는 문제
- 이 문제를 풀려면 유명인사 각각에 샤드 하나씩 할당하거나 더 잘게 쪼개야할 수도 있다.
- 조인과 비정규화
- 여러 샤드에 걸친 데이터를 조인하기가 힘들어진다.
- 데이터베이스를 비정규화하여 하나의 테이블에서 질의가 수행될 수 있도록 하는 방법이 있다.
백만 사용자, 그리고 그 이상
- 시스템 규모 확장은 지속적이고 반복적인 과정이다.
- 그러나 수백만 사용자를 지원하려면 새로운 전략을 도입해야 한다.
- 시스템 규모 확장을 위한 기법들 정리
- 웹 계층은 무상태 계층으로
- 모든 계층에 다중화 도입
- 가능한 많은 데이터를 캐시할 것
- 여러 데이터 센터를 지원할 것
- 정적 콘텐츠는 CDN을 통해 서비스할 것
- 데이터 계층은 샤딩을 통해 규모를 확장할 것
- 각 계층은 독립적 서비스로 분할할 것
- 시스템을 지속적으로 모니터링하고, 자동화 도구를 활용할 것