•
한 명의 사용자를 지원하는 시스템에서 시작하여, 최종적으로는 몇백만 사용자를 지원하는 시스템을 설계해 볼 것이다.
•
이번 장을 통해 규모 확장성과 관계된 설계 문제를 푸는 데 사용할 수 있는 지식들을 알아갈 수 있을 것이다.
단일 서버
•
모든 컴포넌트가 단 한 대의 서버에서 실행되는 간단한 시스템이다.
◦
웹 어플리케이션, 데이터베이스, 캐시 등이 모두 서버 한 대에서 실행된다.
•
이때 하나의 웹 서버가 모든 클라이언트의 요청을 담당하게 되는데, 이때 사용자의 요청 처리 흐름은 어떻게 될까?
데이터베이스
•
사용자가 늘면 단일 서버로의 자원으로는 한계가 발생할 수 있으므로 데이터베이스를 가장 먼저 분리해볼 수 있다.
•
각각의 요소들은 분리되었으므로 서로에게 영향을 받지 않고 독립적으로 서비스를 만들어나갈수도 있다.
어떤 데이터베이스를 사용할 것인가
•
전통적으로는 관계형 데이터베이스와 비관계형 데이터베이스 사이에서 선택할 수 있다.
◦
관계형 데이터베이스는 MySQL, Oracle, PostgreSQL 등, 데이터를 테이블, 열, 컬럼으로 표현, SQL을 이용해 테이블의 관계에 따라 데이터를 조인할 수 있다.
◦
비관계형 데이터베이스는 흔히 NoSQL로 불리며 MongoDB, Amazon DynamoDB 등, NoSQL은 키-값 저장소, 그래프 저장소, 칼럼, 문서 저장소 등으로 분류된다.
•
일반적으로 도메인을 풀어내는 데는 관계형 데이터베이스를 사용하는 경우가 많으나 각 문제와 맥락을 고려해서 적절하게 데이터베이스를 선택하는 능력을 가져야 한다.
•
다음은 일반적으로 NoSQL을 선택하는 상황들이다.
◦
아주 낮은 응답 지연시간이 요구될 때
◦
다루는 데이터가 비정형이라서 관계형 데이터가 아닐 때
◦
데이터를 직렬화하거나 역직렬화만 하면 될 때
◦
아주 많은 양의 데이터를 저장할 필요가 있을 때
수직적 규모 확장 vs 수평적 규모 확장
•
수직적 규모 확장은 서버의 스펙을 향상시키는 것이다.
◦
트래픽이 적을 경우, 일반적으로 좋은 선택이 된다.
◦
다만 이론적으로 처리할 수 있는 트래픽에 한계가 있고 자동복구나 다중화 방안이 제시되지 않아 안정성 면에서 떨어지는 편인 것 같다.
•
수평적 규모 확장은 더 많은 서버를 추가하여 성능을 개선하는 행위다.
◦
장애에 대한 자동복구나 다중화 방안을 제시한다. 보통 대규모 애플리케이션에서 더 적절한 확장법이다.
•
이들은 확장의 방향이 서로 다르다. 위로 가느냐 퍼져 나가느냐다.
로드밸런서
•
수평적 규모 확장 방법을 선택한다고 했을 때, 사용할 수 있는 프로세스다.
•
로드밸런서는 부하 분산 집합에 속한 웹 서버들에게 트래픽 부하를 고르게 분산해주며, 사용자는 웹서버 대신 로드밸런서의 공개 IP 주소로 접속한다.
•
이렇게 부하 분산 집합에 웹 서버를 하나 더 추가하게 되면 자동 복구하지 못하는 문제가 해소되고, 웹 계층의 가용성은 향상된다. 하나의 서버가 죽어도 다른 서버가 그 역할을 대신하기 때문이다.
데이터베이스 다중화
•
이로써 웹 계층은 어느정도 개선됐는데, 데이터베이스 서버는 아직 하나이다. 자동 복구나 다중화를 지원하지 않는다.
•
단일 데이터베이스는 자동 복구나 다중화를 지원하지 않으나 다중 데이터베이스는 이를 해결하기에 적절한 방법이다.
•
Master - Slave 관계를 설정하고 데이터의 원본은 Master에 사본은 Slave에 저장, 쓰기 연산은 Master 서버에서만 일어나고 Slave 서버는 Master 서버로부터 사본을 전달받아 읽기 연산만을 담당한다.
•
통상적으로 CUD의 연산보다 R의 연산이 더 빈번하므로 Master보다 Slave의 수가 더 많은 편이다.
•
데이터베이스 다중화로 얻는 이점은 다음과 같다.
1.
더 나은 성능
•
모든 쓰기 연산은 Master가 읽기 연산은 다수의 Slave가 담당하므로 단순하게 생각해서 트래픽이 나눠진다.
2.
안정성
•
자연 재해등으로 서버의 일부가 파괴되어도 데이터가 보존됨, 지리적으로 떨어져 있는 곳으로 서버를 다중화시킬 수 있기 때문이다.
3.
가용성
•
데이터를 여러 지역에 복제하여 저장하므로 하나의 데이터베이스 서버가 뻗어도 다른 데이터베이스 서버가 그 역할을 대신하기 때문이다.
•
데이터베이스 다중화는 다음과 같은 상황에 대처가 가능
◦
하나뿐인 Slave 서버가 다운 → Master 서버가 일시적으로 읽기 연산까지 담당
◦
두 개 이상의 Slave 서버 중 하나의 서버가 다운 → 다른 Slave 서버가 담당
◦
Master 서버가 다운 → Slave 서버가 단일이든 다중이든 한 대의 Slave 서버가 Master 서버가 되어 쓰기 연산을 담당
캐시
•
캐시는 일반적으로 연산에 비용이 많이드는 결과나 자주 참조되는 데이터를 메모리 안에 두고 동일한 요청이 빠르게 처리될 수 있도록 하는 저장소다.
•
어플리케이션의 성능은 데이터베이스를 얼마나 자주 호출하느냐에 크게 좌우되기 때문에, 캐시는 그런 문제를 완화하는데 도움이 된다.
캐시 계층
•
캐시 계층은 데이터가 잠시 보관되는 곳으로 데이터베이스보다 훨씬 빠르다. 별도의 캐시 계층을 두면 성능이 개선될 뿐 아니라 데이터베이스의 부하를 줄일 수 있고, 캐시 계층의 규모를 독립적으로 확장할 수도 있다.
•
다양한 캐시 전략이 있는데, 여기선 읽기 주도형 캐시 전략만 소개해두었다. 이는 다음과 같다.
◦
데이터가 캐시에 있으면 캐시 계층에 있는 데이터를 반환한다.
◦
데이터가 없는 경우, 데이터베이스에서 데이터를 읽어온다.
▪
그리고 이를 반환한다.
캐시 사용 시 유의할 점
•
캐시의 특성을 고려해서 적합한 상황인지 확인해야 한다.
◦
캐시는 보통 데이터의 갱신이 잦지는 않지만 자주 참조되는 경우 고려해볼만 하다.
•
어떤 데이터를 캐시에 두겠는가?
◦
캐시는 데이터를 휘발성 메모리에 저장하므로 영송적으로 보관할 데이터를 두는 것은 일반적으론 바람직하지 않다.
•
캐시에 저장되는 데이터의 생명주기를 어떻게 할 것인가?
◦
만료된 데이터는 삭제되어야 한다. 만료 기한이 짧아도 데이터베이스를 자주 읽게 될 것이므로 바람직한 작업이 아니다.
◦
너무 길어도 원본 데이터와 차이가 날 가능성이 존재하기 때문에 이 역시 바람직하지 않다.
•
일관성은 어떻게 유지되는가?
◦
일관성은 데이터 저장소의 원본과 캐시 내의 사본이 같은지 여부다.
◦
즉, 일반적으로는 데이터베이스의 원본을 갱신하는 연산과 캐시의 데이터를 갱신하는 연산이 단일 트랜잭션으로 처리되는 것이 바람직하다.
◦
페이스북에서 내놓은 논문인 <Scaling Memcache at Facebook>을 참고하면 좋을 것이다.
•
단일 서버일 경우 단일 장애 지점이 될 확률이 있다.
◦
캐시 서버가 단일 서버인 경우, 다운되어버리면 캐시가 전혀 제기능을 하지 못하게 된다.
•
캐시 메모리를 얼마나 할당할 것인가?
◦
캐시 메모리가 너무 작으면 캐시 데이터 방출 정책에 의해 데이터가 빠르게 사라져버릴 가능성이 있다.
◦
이때, 캐시 메모리를 과할당하는 방법이 있다. 이는 캐시에 보관될 데이터가 갑자기 늘어나도 안정적으로 수행할 수 있게 한다.
•
데이터 방출 정책은 무엇인가?
◦
캐시가 꽉 차버리면 추가로 데이터를 입력하기 위해 기존의 데이터를 제거해야 한다.
◦
가장 널리 쓰이는 것은 LRU로 외에도 LFU, FIFO 같은 것도 있으며 데이터가 활용되는 특성을 확인하여 결정하는 것이 좋다.
콘텐츠 전송 네트워크
•
콘텐츠 전송 네트워크(Content Delivery Network)는 앞글자만 따서 CDN이라고 부르는 것이 일반적으로 정적 콘텐츠를 전송하는데 쓰이는 물리적으로 분산된 서버의 네트워크이다.
◦
이미지, 비디오, CSS 파일 등을 캐시해둘 수 있다.
•
동적 콘텐츠 캐싱은 요지에 적합하진 않지만 간단하게 request path, query string, cookie, request header 등의 정보에 기반하여 HTML 페이지를 캐시하는 것이다.
•
CDN이 어떻게 동작하는지를 개략적으로 살펴보면 다음과 같다. 어떤 사용자가 웹 사이트를 방문하면 그 사용자에게 가장 가까운 CDN 서버가 정적 콘텐츠를 전달하게 된다.
1.
사용자가 이미지 URL에 접근한다.
a.
CDN 서버의 캐시에 데이터가 있는 경우, 별다른 절차 없이 데이터를 가져온다.
2.
CDN 서버의 캐시에 데이터가 없는 경우, 원본 서버에 요청하여 파일을 가져온다.
⇒ 원본 서버는 이때 웹 어플리케이션 서버일수도 있고 스토리지일 수도 있다. 이건 중요하지 않다.
3.
원본 서버가 파일을 CDN 서버에 반환한다. 응답은 HTTP 헤더에는 해당 파일에 대한 TTL이 포함되어 있다.
4.
CDN 서버는 파일을 캐시하고 사용자에게 반환한다. 이미지는 TTL 동안 유지된다.
CDN 사용 시 고려해야 할 사항
•
비용
◦
CDN은 보통 제3 사업자에 의해 운영되고 보통 데이터 전송 양에 따라 비용이 발생한다.
◦
자주 사용되지 않는 콘텐츠를 캐싱하는 것은 이득이 크지 않으므로 비용을 절감하기 위해 빼는 것을 고려하자.
•
적절한 만료 시한 설정
◦
원본 데이터라는 보장이 없으므로 만료 시점을 잘 정해야 한다.
•
CDN 장애에 대한 대처 방안
◦
CDN이 다운되었을 경우를 대비해서 어플리케이션이 동작하는 방식에 대해서 고려해야 한다.
•
콘텐츠 무효화 방법
◦
아직 만료되지 않은 콘텐츠라고 하더라도 아래 방법 가운데 하나를 사용하여 CDN에서 제거할 수 있다.
▪
CDN 서비스 사업자가 제공하는 API를 이용
▪
콘텐츠의 다른 버전을 서비스하도록 오브젝트 버저닝을 이용
무상태 웹 계층
•
웹 계층을 수평적으로 확장하는 방법을 고민해볼 차례다. 이를 위해서는 상태 정보를 웹 계층에서 분리해내어야 한다.
•
바람직한 전략은 상태 정보를 데이터베이스에 보관하고 필요할 때, 가져오도록 하는 것이다. 이렇게 구성된 웹 계층을 무상태 웹 계층이라고 부른다.
상태 정보 의존적인 아키텍처
•
상태 정보를 보관하는 서버는 요청을 보내는 클라이언트의 정보, 즉 상태를 유지하여 요청들 간의 맥락이 존재하게 된다.
•
때문에 다른 상태를 가지고 있는 요청이 들어오는 경우, 인증이 실패하여 요청이 거부될 가능성이 존재한다.
•
즉, 같은 클라이언트로부터의 요청은 항상 같은 서버로 전송되어야 한다는 것이다.
◦
대부분의 로드 밸런서가 이를 지원하기 위해 고정 세션이라는 기능을 제공하고 있는데, 이는 로드 밸런서에게 부담을 준다.
무상태 아키텍처
•
웹 서버는 상태 정보가 필요할 경우, 공유 저장소로부터 데이터를 가져온다. 따라서 상태 정보는 물리적으로 웹 서버로부터 분리되어 있다.
•
이런 구조는 단순하고 안정적이며 확장이 쉽다.
데이터 센터
•
서비스의 가용성을 높이고 어디서도 쾌적하게 사용할 수 있게끔 하고자 할 때 고려해볼 수 있는 방법이다.
•
로드 밸런서의 라우팅이 지리적 거리를 기준으로 발생한다.
◦
만약, 데이터 센터 중 하나가 다운되면 나머지 데이터 센터들에게 전송이 분산된다.
•
이런 다중 데이터 센터 아키텍처를 만들려면 다음과 같은 기술적 난제를 해결해야 한다.
◦
트래픽 우회
▪
올바른 데이터 센터로 트래픽을 보내는 효과적인 방법을 찾아야 한다.
▪
이때 GeoDNS를 사용해볼 수 있다.
◦
데이터 동기화
▪
데이터 센터마다 별도의 데이터베이스를 사용하므로 데이터 간의 정합성 문제가 발생할 수 있다.
◦
테스트와 배포
▪
웹 사이트 또는 어플리케이션을 여러 위치에서 테스트해보는 것이 중요하다.
▪
또한 자동화된 배포 도구를 모든 데이터 센터에 동일한 서비스가 설치되도록 하는 데 중요한 역할을 한다.
메시지 큐
•
메시지 큐는 메시지의 무손실을 보장하는 비동기 통신을 지원하는 컴포넌트다.
•
메세지 큐의 기본 아키텍처는 간단하다.
◦
생산자 또는 발행자라고 불리는 입력 서비스가 메시지를 만들어 메시지 큐에 발행한다.
◦
큐에는 보통 소비자 혹은 구독자라 불리는 서비스 혹은 서버가 연결되어 있는데, 메시지를 받아 그에 맞는 동작을 수행하는 역할을 한다.
•
메시지 큐를 사용하면 서비스 또는 서버 간 결합이 느슨해져서 규모 확장성 면에서 안정적이어야 하는 어플리케이션을 구성하기 좋다.
◦
생산자는 소비자 프로세스가 처리를 할 수 있든 없든 발행할 수 있으며 소비자는 생산자 프로세스가 처리를 할 수 있든 없든 처리할 수 있다.
◦
생산자와 서비스 서비스의 규모는 각기 독립적으로 확장될 수 있으며 큐에 존재하는 메시지의 크기를 확인해 조절할 수 있을 것이다.
•
사용 예로는 다음과 같은 것들이 존재한다.
◦
이미지의 크로핑, 샤프링, 블러링 등을 지원해야 할 때, 이런 작업들은 시간이 오래 걸리므로 비동기적으로 처리하면 편하다.
로그, 메트릭 그리고 자동화
•
사업의 규모가 커지는 경우 피룡하다.
•
로그
◦
에러 로그를 모니터링하는 것은 중요하다. 시스템의 오류와 문제를 쉽게 찾고 해결할 수 있게 하기 위해서다.
◦
에러 로그는 서버 단위로 모니터링할 수 도 있지만 로그를 단일 서비스로 모아주는 도구를 활용하면 더 편리하게 검색하고 조회할 수 있다.
•
메트릭
◦
메트릭을 잘 수집하면 사업 현황에 관한 유용한 정보를 얻을 수도 있고 시스템의 현재 상태를 손쉽게 파악할 수도 있다.
◦
다음은 메트릭 가운데 특히 유용한 항목들이다.
▪
호스트 단위 메트릭: CPU, 메모리, 디스크 입출력에 관한 메트릭이 여기에 해당한다.
▪
종합 메트릭: 데이터베이스 계층의 성능, 캐시 계층의 성능같은 것이 여기 해당한다.
▪
핵심 비즈니스 메트릭: DAU, 수익, 재방문 같은 것이 여기에 해당한다.
데이터베이스의 규모 확장
•
저장한 데이터가 늘면 데이터베이스에 대한 부하도 당연히 증가한다. 이땐 데이터베이스를 증설할 방법을 찾아야 한다.
수직적 확장
•
고성능의 자원을 증설하는 방법이다.
•
하지만 이런 수직적 접근법에는 몇 가지 약점이 있다.
◦
데이터베이스 서버 하드웨어는 한계가 있으므로 무한히 증설할 수 없다.
◦
SPOF로 인한 위험성이 존재한다.
◦
비용이 많이 든다. 고성능일수록 기하급수적으로 증가하기 때문이다.
수평적 확장
•
샤딩이라고도 부르는 방법으로, 더 많은 서버를 추가함으로써 성능을 향상시킬 수 있도록 한다.
•
샤딩은 데이터베이스를 샤드라고 부르는 작은 단위로 분할하는 기술을 일컫는다. 모든 샤드는 같은 스키마를 사용하지만 데이터 사이에 중복이 없다.
•
샤딩 전략을 구현할 때 가장 중요한 부분은 샤딩 키를 어떻게 정하느냐로, 샤딩 키는 파티션 키라고도 부른다.
◦
샤딩 키를 통해 올바른 데이터베이스에 질의를 보내야 데이터를 획득할 수 있다. 또한 샤딩 키를 결정할 때는 데이터를 고르게 분포할 수 있도록 분할하는게 중요하다.
•
외에도 샤딩을 도입하면 다음과 같은 문제가 생길 가능성이 있다.
◦
데이터의 재 샤딩
▪
재샤딩은 다음과 같은 경우에 필요하다.
•
데이터가 너무 많아져서 하나의 샤드로는 감당하기 어려운 경우다.
•
어떤 샤드에 할당된 공간 소모가 다른 샤드에 비해 소모가 빠를 때, 샤드 소진이라고도 부른다.
▪
이런 현상이 발생하면 샤드 키를 계산하는 함수 변경 후, 데이터를 재배치하는 작업이 필요하다.
•
이때, 안정 해시 기법을 사용해볼 수 있다.
◦
유명인사 문제
▪
핫스팟 키 문제라고도 불리는데, 특정 샤드에 질의가 집중되는 경우를 의미한다.
▪
이 문제를 풀려면 유명인사 각각에 샤드 하나씩을 할당해야 할 수도 있고 심지어는 더 잘게 쪼개야 할 수도 있다.
◦
조인과 비정규화
▪
일단 하나의 데이터베이스를 여러 샤드로 쪼개고 나면여러 샤드에 걸친 데이터들을 조인하기가 힘들어 진다.
▪
이를 해결하는 한 가지 방법은 데이터베이스를 비정규화하여 하나의 테이블로 질의가 수행될 수 있도록하는 것이다.
백만 사용자, 그리고 그 이상
•
시스템의 규모를 확장하는 것은 지속적이고 반복적인 과정이다. 이번장에서 다룬 내용을 반복하다보면 결국 우리가 원하는 규모의 시스템을 달성할 수 있게 된다.
•
백만 이상의 사용자를 감당하려면 지속적으로 시스템을 가다듬어야 할 것이다. 예를 들어 시스템을 최적화하고 더 작은 단위의 서비스로 분할해야할 수도 있다.
⇒ MSA?
•
이번 장에서 알아본 시스템 규모 확장을 위해 살펴본 기법들을 정리해보면 다음과 같다.
◦
웹 계층은 무상태 계층으로 만들어라
◦
모든 계층에 다중화를 도입하라.
◦
가능한 한 많은 데이터를 캐시하라.
◦
여러 데이터 센터를 지원하라.
◦
정적 콘텐츠는 CDN을 통해 서비스하라.
◦
데이터 계층은 샤딩을 통해 그 규모를 확장하라.
◦
각 계층을 독립적 서비스로 분할하여 확장 가능성을 높여라.
◦
시스템을 지속적으로 모니터링하고 자동화 도구들을 활용하여 개선하라.