1장 - 사용자 수에 따른 규모 확장성
목표
여러 시스템을 설계해봄으로써 규모 확장성과 관계된 설계 문제에 관련한 지식 습득
단일 서버
모든 컴포넌트가 단 한 대의 서버에서 실행되는 간단한 시스템
웹, 앱, 데이터베이스, 캐시 등이 전부 서버 한 대에서 실행되는 시스템이다.

- DNS에 IP주소로 변환 요청
- DNS 조회 결과로 IP가 반환
- IP주소로 HTTP 요청 전달
- 요청받은 서버가 HTML 페이지, JSON형태 응답을 반환
데이터 베이스
사용자가 증가함에 따라 충분치 않아짖며 여러 서버를 두어야한다. 트래픽 처리 서버와 데이터베이스를 분리하면 그 각각을 독립적으로 확장해 나갈 수 있게 된다.

어떤데이터 베이스를 사용할 것인가?
- 관계형 데이터 베이스 RDBMS
- 비 관계형 데이터 베이스 NoSQL
- key-value store
- graph store
- column store
- document store
오랜 시간 시장에서 살아남았던 관계형 데이터베이스를 주로 사용하나 다음의 경우 NoSQL이 바람직한 선택 일 수 있다.
- 아주 낮은 응답 지연시간이 요구됨
- 다루는 데이터가 비정형이라 관계형 데이터가 아님
- 데이터(Json, TAML, XML등)를 직렬화하거나,역직렬화 할 수 있기만 하면 됨
- 아주 많은 양의 데이터를 저장할 필요가 있음
수직적 규모 확장 vs 수평적 규모 확장
스케일 업(수직적 규모 확장 프로세스)은 서버에 고사양 자원(CPU, RAM)을 추가하는 행위를 말한다. 스케일 아웃(수평적 규모 확장 프로세스)는 더 많은 서버를 추가하여 성능을 개선하는 행위를 말한다.
스케일 업은 단순함이 장점이나 심각한 단점이 존재한다.
- 한계가 존재한다. -> 한대의 서버에 cpu나 메모리를 무한대로 증설할 방법은 없다.
- 장애에 대한 방안이 없어 서버에 장애가 발생하면 완전히 중단된다.
이러한 단점때문에 대규모 어플리케이션은 수평적 규모 확장법이 적절하다.
로드밸런서
로드밸런서는 부하 분산 집합에 속한 웹 서버들에게 트래픽을 고르게 분산하는 역할을 한다.

사용자는 로드밸런서의 공개 IP 주소로 접속한다. 보안을 위해 서버 간 통신은 private IP가 이용되는데 로드밸런서와 웹서버는 private 주소를 이용하여 통신한다. 부하 분산 집합에 또 하나의 웹 서버를 추가하면 장애 복구, 웹 계층의 가용성이 향상된다. 자세히 보면 다음과 같다.
- 서버 1이 다운되면 모든 트래픽은 서버 2로 전송된다. ➡️ 전체가 다운되는 일 방지
- 트래픽 증가시 웹서버 계층에 서버를 추가하기만 하면 로드밸런스가 자동적으로 트래픽을 분산하기 때문에 웹 가용성이 향상된다.
데이터베이스 다중화
로드밸런서를 통해 웹계층은 괜찮아보임, 데이터 계층은 어떻게?
보통은 서버 사이에 주(master)-부(slave) 관계를 설정하고 데이터 원본은 주 서버에, 사본은 부 서버에 저장하는 방식이다.
- 쓰기 연산 (write operation - insert, delete, update 등)은 master에서만 지원
- 읽기 연산 (read opreration)은 slave에서 지원
- 읽기 연산 > 쓰기 연산 인것을 이용하여 부 서버를 많이 가져와 다중화 처리

다중화에 따른 장점
- 더 나은 성능 : 읽기 연산의 분산처리로 인하여 성능이 좋아진다.
- 안정성 : 물리적인 데이터베이스 서버 손상이어도 다중화로 데이터보존
- 가용성 : 데이터를 복제해둠으로써, 장애가 발생하더라도 다른서버의 데이터로 서비스 지속
다중화는 다음과 같은 여러 상황에 따라 대처가능하다
- 부서버가 한대인데 다운된 경우
➡️ 한시적으로 주 데이터베이스가 읽기 연산 처리, 즉시 새로운 부 데이터베이스로 대체 - 주 데이터베이스 서버가 다운된 경우
➡️ 부 데이터베이스 서버가 주 서버 역할을 대신하게 되며, 새로운 부 서버가 추가됨(실제로는 더욱 복잡)
로드밸런서와 데이터베이스 다중화
전체적인 그림은 다음과 같다.

다음은 캐시와 CDN을 이용하여 응답시간을 개선해 보자.
캐시
캐시는 여러 데이터를 메모리 안에 두고, 뒤이은 요청이 보다 빨리 처리 될 수 있도록 하는 저장소이다.
어플리케이션의 성능은 데이터베이스를 얼마나 자주 호출하느냐에 좌우되는데, 캐시는 이를 완화시켜 줌
캐시 계층
- 별도의 캐시 계층을 두면 성능개선, 데이터 부하를 줄일 수 있음
- 캐시 계층의 규모를 독립적으로 확장 가능
캐시 사용 시 유의할 점
- 캐시는 어떤 상황에 바람직한가?
➡️ 갱신은 자주 X, 참조가 많을 때 - 어떤 데이터를 캐시에 두어야 하는가?
➡️ 휘발성 메모리에 저장하므로 영속성 데이터 X - 캐시에 보관된 데이터는 어떻게 만료되는가?
➡️ 만료 정책 필요, 아래의 사항 고려
➡️ 없으면 계속 남아 있음 / 짧으면 효율 저하 / 길면 원본과 동기화 문제 - 일관성은 어떻게 유지되는가?
➡️ 데이터베이스 = 캐시인지의 여부, 원본 갱신+캐시 갱신이 하나의 트랜잭션으로 처리되어야 한다. - 장애에는 어떻게 대처할 것인가?
단일 장애지점(SPOF) : 특정 어느지점의 장애가 전체 시스템의 동작을 중단 시킨 경우 해당 지점
➡️ SPOF를 피하기 위해 캐시 서버를 분산 - 캐시 메모리는 얼마나 크게 잡을 것인가?
➡️ 메모리가 적으면 데이터가 자주 밀려나 효율 저하, 메모리를 과할당 시킴 - 데이터 방출 정책은 무엇인가?
➡️ 캐시가 꽉차면 어떤 데이터를 내보낼 것인지, LRU,LFU,FIFO등 상황에 맞게 적용
콘텐츠 전송 네트워크(CDN)
CDN은 정적 콘텐츠를 전송하는 데 쓰이는, 지리적으로 분산된 서버의 네트워크로 정적 파일들을 캐시 즉 지리적으로 가까운 위치에 CDN서버를 두어 로딩 시간을 개선한다.

- CDN 동작 과정

CDN 사용시 고려 사항
- 비용 : 해당 지역 제3 사업자에 의해 운영, 캐싱 안할 것을 미리 제외
- 적절한 만료 시한 설정 : 길면 신선도 저하, 짧으면 효율 저장하는
- CDN 장애 대처 방안 : CDN이 응답하지 않을 경우 원본서버에서 가져오도록 클라이언트 구성
- 콘텐츠 무효화 방법 : 오브젝트 버저닝(object versioning)
Cache + CDN
다음은 캐시와 CDN이 추가된 설계다.

무상태(stateless) 웹 계층
상태 정보 의존적인 아키텍처
- 사용자 A ➡️ 서버 1 : 사용자 A의 세션 데이터
- 사용자 B ➡️ 서버 2 : 사용자 B의 세션 데이터
상태 정보 의존적인 아키텍처에서 사용자 A가 서버 2에 요청을 보내면 인증이 실패된다.
이를 해결하기 위해 로드밸런서에서 A를 1로 B를 2로하는 고정 세션이 지원하나 로드밸런서에 부담
무상태 아키텍처

웹 계층을 수평적으로 확장하기 위해서는 상태 정보(세션 데이터)를 웹 계층에서 제거하여야 한다.
바람직한 전략은 상태 정보를 RDBMS나 NoSQL 같은 지속성 저장소에 보관하고, 필요할 때 가져오는
것으로 이렇게 구성된 웹 계층을 무 상태 웹 계층이라 부른다.

상태 정보가 웹서버에서 분리, 트래픽 양에 따라 웹 서버 규모 확장가능
데이터 센터
전세계 사용자를 대상으로 커질 경우 여러 데이터 센터 필요해진다. 사용자는 가까운 데이터 센터로 안내되는 것을 지리적 라우팅이라고 부른다.

다음과 같은 기술적 난제를 해결해야 한다.
- 트래픽 우회 : 올바른 데이터 센터로 트래픽을 보내야함, GeoDNS가 해결
- 데이터 동기화 : 데이터 센터마다 다른 데이터 베이스를 사용할 경우 찾는 데이터가 없을 수도 있다.
데이터를 여러 데이터센터에 걸처 다중화 하는 것으로 해결 - 테스트와 배포 : 여러 위치에서 테스트하는 것이 중요, 자동화된 배포도구를 통해 모든 데이터 센터에 동일한 서비스가 설치 되도록해야한다.
메시지 큐
시스템을 더큰 규모로 확장하기 위해서는 시스템의 컴포넌트를 분리해서, 독립적으로 확장 될 수 있도록 하여야 한다.
메시지느 큐는 보관된 메시지의 무손실, 비동기 통신을 지원하는 컴포넌트이다.
생산자 -> 메시지큐 -> 소비자 와 같은 구조를 가짐
- 서비스, 서버 간 결합이 느슨해져, 규모 확장성이 보장되어야하는 애플리케이션을 구성하기 좋음
- 큐의 상태에 따라 소비자 프로세스를 변화함으으로써 독립적으로 확장될 수 있음
로그, 메트릭 그리고 자동화
- 로그 : 서버단위로 모니터링 -> 단일 서비스로 모아주는 도구를 활용하여 조회
- 메트릭 : 시간이 지남에 따라 변화하는 데이터
- 호스트 단위 메트릭 : cpu, 메모, I/O
- 종합 메트릭 : 데이터베이스 계층, 캐시 계층의 성능
- 핵심 비즈니스 매트릭 : 일별 능동 사용자, 수익등
- 자동화 : 지속적 통합, 빌드, 테스트, 배포등 자동화 툴을 이용해 개발 생산성 향상
다음은 메시지 큐, 로그, 메트릭 그리고 자동화를 포함한 그림이다.

데이터베이스의 규모 확장
수직적 환장
기존서버에 고성능의 자원을 증성하는 방법으로 다음의 단점이 존재한다.
- 데이터 베이스 서버 하드웨어에는 한계가 있다.
- SPOF(Single Point of Failure)로 인한 위험성
- 고성능일 수록 고비용
수평적 확장
데이터베이스의 수평적 확장 = 샤딩(sharding)
샤딩은 베이터베이스를 샤드라고 부르는 작은 단위로 분할하는 기술
같은 스키마를 쓰지만 샤드간의 데이터에는 중복이 없다.
사용자 데이터를 어드 샤드에 넣을지는 사용자 ID에 따라 정한다.
즉 user_id % 4를 해서 결과를 바탕으로 샤드에 보관한다면 다음과 같다.
- 샤드 0 : user_id 0,4,8,12 인 column
- 샤드 1 : user_id 1,5,9,13 인 column
- 샤드 2 : user_id 2,6,10,14 인 column
- 샤드 3 : user_id 3,7,11,15 인 column
user_id와 같이 어떻게 분산될지 정하는 키 = 샤딩 키 = 파티션 키 샤딩키에는 다음과 같은 문제가 발생한다.
-
데이터의 재 샤딩(resharding)
- 데이터가 많아져 하나의 샤드로 X
- 일부 샤드에 할당된 공간 소모가 빨리 진행
위의 두가지 경우를 샤드 소진이라고 부르며 이때 샤드 키 함수를 변경, 데이터를 재배치해야함
-
유명인사(celebrity) 문제 = 핫스팟 키(hotspot key) 문제 특정 샤드에 질의 집중으로 서버에 과부하가 걸리는 문제가
-
조인과 비정규화 여러 사드 서버로 쪼개고 나면, 조인하기 힘들어짐
➡️ 데이터베이스를 비정규화하여 하나의 테이블로 질의 실행
다음은 샤드가 적용된 그림이다.

백만 사용자, 그리고 이상
시스템 규모를 확장하는 것은 지속적이고 반복적(iterative)한 과정
수백만 사용자를 지원하기 위해서는 더욱 새로운 전략 필요
- 웹 계층은 무상태 계층으로
- 모든 계층에 다중화 도입
- 가능한 한 많은 데이터를 캐싱할 것
- 여러 데이터 센터를 지원할 것
- 정적 콘텐츠는 CDN을 통해 서비스할 것
- 데이터 계층은 샤딩을 통해 그 규모를 확장할 것
- 각 계층은 독립적 서비스로 분할할 것
- 시스템을 지속적으로 모니터링하고, 자동화 도구들을 활용할 것