From 01df223e8b23f15714176f1843c21dafefa6ed2f Mon Sep 17 00:00:00 2001 From: lsj8367 Date: Tue, 6 Jun 2023 21:58:16 +0900 Subject: [PATCH] =?UTF-8?q?:memo:=201=EC=9E=A5=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EA=B7=9C?= =?UTF-8?q?=EB=AA=A8=20=ED=99=95=EC=9E=A5=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0_\355\231\225\354\236\245\354\204\261.md" | 368 ++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 "1\354\236\245.\354\202\254\354\232\251\354\236\220_\354\210\230\354\227\220_\353\224\260\353\245\270_\352\267\234\353\252\250_\355\231\225\354\236\245\354\204\261.md" diff --git "a/1\354\236\245.\354\202\254\354\232\251\354\236\220_\354\210\230\354\227\220_\353\224\260\353\245\270_\352\267\234\353\252\250_\355\231\225\354\236\245\354\204\261.md" "b/1\354\236\245.\354\202\254\354\232\251\354\236\220_\354\210\230\354\227\220_\353\224\260\353\245\270_\352\267\234\353\252\250_\355\231\225\354\236\245\354\204\261.md" new file mode 100644 index 0000000..541084a --- /dev/null +++ "b/1\354\236\245.\354\202\254\354\232\251\354\236\220_\354\210\230\354\227\220_\353\224\260\353\245\270_\352\267\234\353\252\250_\355\231\225\354\236\245\354\204\261.md" @@ -0,0 +1,368 @@ +# 1장. 사용자 수에 따른 규모 확장성 + +수백만 사용자를 지원하는 시스템을 설계하는 것은 매번 도전적인 과제이며 개선에는 끝이 없을것. + +# 단일 서버 + +모든 컴포넌트가 단 한대의 서버에서 실행되는 간단한 시스템부터 설계하면 아래와 같다. + +![1](https://github.com/back-end-study/system-design-interview/assets/74235102/44f9a2e5-80ed-48c9-9012-267853107ca2) + +> 사용자 요청 처리 흐름 순서 +> +1. 도메인 이름으로 웹사이트에 접속한다. + 1. 여기서는 DNS 서버에 질의하여 본래의 ip주소로 변환하는 과정이 필요하다. +2. DNS 조회 결과로 IP 주소 반환 +3. 해당 IP주소로 http 요청 +4. 요청받게된 웹 서버에서는 찾는 html이나 JSON 데이터의 응답을 반환해준다. + +처음 요청이 들어오는 곳은 보통 웹이나 앱이 될 것이다. + +# 데이터베이스 + +사용자가 늘어나면 서버 하나로는 충분히 수용하기 어려워서 여러 서버를 두게 된다. + +한대는 클라이언트의 트래픽 처리 용도로 쓰이고, 다른 하나는 데이터베이스 용이다. + +쉽게 생각하면 프론트 + 백엔드 서버의 구조로 생각하면 될 것 같다. + +## 어떤 데이터베이스를 사용할 것인가? + +RDBMS, NOSQL 사이에서 데이터베이스를 선택할 수 있다. + +### 관계형 데이터베이스 (Relational Database Management System; RDBMS) + +MySQL, Oracle, PostgreSQL 등이 존재한다. + +데이터들을 테이블과 행, 칼럼으로 표현한다. SQL을 사용하게 되면 여러 테이블에 흩어져있는 데이터를 join을 사용하여 모아 볼 수 있다. + +### 비 관계형 데이터베이스 (NoSQL) + +HBase, MongoDB, DynamoDB 등이 있다. + +key-value, 그래프, 칼럼, 문서 저장소 4가지로 나눌 수 있다. 관계형과 다르게 **join을 지원하지 않는다.** + +> 비 관계형이 적합할 때 +> +- 아주 낮은 응답 지연시간이 요구되는 경우 +- 다루는 데이터가 비정형인 경우 +- 데이터를 직렬화 or 역직렬화 할 수 있기만 하면 되는 경우 +- 아주 많은 양의 데이터를 저장할 필요가 있는 경우 + +# 수직적 규모 확장 vs 수평적 규모 확장 + +스케일 업이라고 하는 수직적 규모 확장은 서버의 사양을 높여 성능을 개선하는 것을 말한다. + +스케일 아웃이라고 하는 수평적 규모 확장은 더 많은 서버를 추가하여 성능을 개선하는 것을 말한다. + +트래픽의 양이 적지만, 규모를 늘릴 땐 스케일업이 좋은 선택이다. + +그리고 스케일업의 장점은 단순함이다. + +## 스케일업의 단점 + +1. 수직적 확장엔 한계가 존재한다. + 1. 물리적인 장비를 좋게하는 방법은 끝이보인다. +2. 장애에 대한 자동복구나 다중화 방안이 없다. + 1. 서버가 죽으면 끝이라는 소리 + +위에서 봤던 그림에서 사용자들은 웹서버로 바로 연결하게 되는데, 이 때 웹 서버가 죽는다면? + +→ 웹 사이트의 자원을 이용할 수 없게 된다. + +→ 너무 많은 사용자가 접속하여 한계에 다다르면 이 역시 속도가 느려지거나, 접속이 불가능해진다. + +> 이럴 때 사용하는 것이 바로 로드밸런서 이다. +> + +## 로드 밸런서 + +자기가 다루는 집합 내의 웹 서버들에게 균등하게 트래픽을 분산해주는 역할을 수행한다. + +![2](https://github.com/back-end-study/system-design-interview/assets/74235102/c7f56517-05db-428c-a598-844d8c5767cd) + +이러한 구성에서 사용자들은 요청을 할 때 로드밸런서의 public IP로 요청하게 된다. + +그래서 웹 서버로 직접 요청을 보내는 것 처럼 느껴지지만, 사실 웹서버로 직접 붙는게 아닌 상태이다. + +리버스 프록시 기법을 사용하여 구성된 것을 볼 수 있다. + +그러면서 로드밸런서와 웹서버 사이에는 프라이빗한 내부 사설 IP주소를 통해 통신한다. + +→ 밖에서는 접속해볼 수 없다. + +이렇게 구성하게 되면, 장애 복구를 할 수 있는 구성이 되며 웹 계층 가용성이 향상된다. + +- 서버 1이 죽었다면 모든 요청을 서버2가 받아 수행해준다. + - 서버 1이 죽어서 모든 요청을 보내다가도 새로운 서버3을 붙여 2,3 으로 이중화 구성도 할 수 있다. +- 두대의 서버가 헐떡이는 상황이 온 경우도 끄떡없다. + - 웹 서버 계층을 더 추가해주면 그만. + - 이렇게 되면 로드밸런서가 자동으로 트래픽 분산을 진행해 줄 것이다. + - 트래픽이 헐떡이는 상황까지 온 시점에 서버를 증설하면 이미 늦었을 것 같다? + - 이때 서버를 하나씩 증가시키면 새 서버가 뜨는 동안 헐떡이는 서버가 죽게 될 수도 있을 것. + - 주워들은 얘기로는 대량의 트래픽이 예상되면 어느정도 미리 서버를 증설해놓기도 한다고 들음. + - **이럴 때는 어떻게 하는지 궁금합니다.** + +### 로드밸런싱 종류 + +- L2 + - Mac주소 기반 + - 브릿지, 허브 등이 존재함 + - 구조가 간단하고 값이 싸다 + - 캐싱된 MAC주소 테이블에 해당 주소가 존재하지 않으면 브로드 캐스트를 전송하기에 성능 저하가 발생함 +- L3 + - IP주소 기반 + - L2에 라우팅 기능이 추가된 형태 + - 라우터 + - 네트워크 주소를 확인하여 로드밸런싱 처리 +- L4 + - 4계층 사용 + - IP주소와 포트로 부하분산을 처리해준다. +- L7 + - 7계층 사용 + - URL을 보고 특정 path로 온경우 분산가능 + - HTTP 헤더에서도 분산이 가능 + +### 로드밸런싱 알고리즘 + +- **라운드 로빈 (Round Robin)** + - **클라이언트의 요청을 여러 대의 서버에 순차적으로 분배하는 방식이다.** 추가적인 계산작업이 없이, 들어온 요청을 빠르게 서버에 분산해 전송하는 1차원적인 주기능에 focus를 맞춘 방식이기 때문에, **가장 많이 사용되는 로드밸런싱 기법**이다. 모든 서버의 스펙이 동일하거나 비슷한 경우에 사용된다. +- **가중 라운드 로빈 (Weighted Round Robin)** + - 각 서버에 처리량(가중치)를 지정한 후, 가중치가 높은 서버에 클라이언트 요청을 우선적으로 전달하는 방식이다. 주로 서버의 트래픽 처리 용량이 다를 경우, 즉 특정 서버의 스펙(사양)이 더 좋을 경우 사용된다. 예를 들어, A 서버(가중치 3)와 B 서버(가중치 1)가 있고 로드 밸런서가 클라로부터 총 8개의 요청을 받았다면, A와 B 서버에 각 6개와 2개의 요청이 전달된다. +- **IP 해시 (IP Hash)** + - 클라이언트의 IP 주소가 어떤 서버로 클라의 요청이 전달될지를 결정하는 방식이다. 클라의 IP 주소가 바뀌지 않으면 동일한 서버로 요청이 보내지는 것을 보장한다. RR 방식과 달리, 서버에 Session clustering이 구성되어 있지 않은 경우에 주로 사용한다. +- **최소 연결 (Least Connection)** + - 요청이 들어온 시점에 가장 적게 연결돼있는 서버에 요청을 전송하는 방식이다. 서버에 분배된 트래픽이 일정하지 않을 경우에 주로 사용한다. +- **최소 응답 시간 (Least Response Time)** + - 서버의 현재 연결 상태와 응답 시간을 모두 고려하여 가장 적은 연결 수와 가장 짧은 응답 시간을 가지는 서버에 우선적으로 요청을 보내는 방식이다. + +## 데이터베이스 다중화 + +데이터베이스 서버 사이에는 master - slave 관계를 설정하여 쓰기연산 (Create, Update, Delete)의 경우에는 master에서 진행하고, 읽기연산(Read)는 slave에서 처리하는 방식을 많이 채택한다. + +그리고 읽기 연산의 비중이 쓰는 연산보다 훨씬 높기에 slave의 DB서버 수가 많고 성능도 더 좋게 구성한다. + +### 다중화를 하면 이득은? + +1. 더 나은 성능 + 1. 데이터 변경 연산은 master로만 전달되고, 읽기 연산은 slave 분산처리 된다. +2. 안정성 + 1. db 일부가 파괴되어도 데이터는 보존이 된다. + 2. 여러 장소에 다중화 시켜놓을 수 있기 때문 +3. 가용성 + 1. 데이터를 여러지역에 복사 + 2. 다른 서버에 있는 데이터를 가져와 계속 서비스할 수 있게 구성 + 3. master DB를 본떠놓은 무엇도 있는가? → 아니면 이부분도 이중 삼중으로 구성을 하는가? + +## 중간정리 + +지금까지의 내용을 합쳐 구성해보면 아래와 같은 그림처럼 구성이 된다. + +![3](https://github.com/back-end-study/system-design-interview/assets/74235102/5c92b3b1-6404-4dfb-b454-5cc7f32c19a7) + +# 캐시 + +캐시는 비용이 비싼 연산 결과나 자주 조회되는 무언가의 데이터를 메모리 안에 두고, 이전 요청보다 빠르게 처리할 수 있게 하는 저장소이다. + +당장 API 샘플로 하나를 개발하고 새로고침하며 계속 요청하면 그때마다 DB호출이 발생할 수도 있다. + +그렇기에 캐시를 도입한다. + +## 캐시 계층 + +이 계층은 데이터가 잠시 보관되는 곳이기에 DB보다 훨씬 빠르다. + +성능 개선 + DB부하 감소 라는 2가지 장점이 존재하기 때문에 이 계층 규모를 확장시키는 것도 가능해진다. + +> 읽기 주도형 캐시 전략 +> + +캐시가 있다면? → 바로 캐시 데이터를 클라이언트에 반환 + +캐시가 없다면? → 쿼리로 DB를 질의하여 데이터를 찾은 후 캐시에 선 저장 + 클라이언트 반환 수행 + +## 캐시 사용시 고려할 점 + +- 데이터 갱신이 빈번하지 않으며 참조만 빈번하게 많이 일어나는지 여부 +- 영속적으로 보관할 데이터가 아닌지? +- 캐시 데이터 만료시간을 어떻게 줄 것인지? +- 일관성은 어떻게 유지할 것인지? + - 캐시데이터와 DB원본 데이터가 같은지에 대한 여부 +- 장애에는 어떻게 대처할 것인지? + - 캐시 서버를 하나로 두는 경우 SPOF 가능성 +- 캐시 메모리의 크기는 얼마나 설정해줄 것인지? + - 메모리가 너무 적다면 캐시 데이터가 새롭게 갈아치워지는 경우가 빈번하여 성능저하 우려 +- 데이터 방출 정책 + - 캐시가 꽉찬 상태인데 추가 캐시로 저장해야 하는 경우 어떤 것을 내보낼 것인지? + - 기본적으로 LRU(Least Recently Used) 사용 + +# 콘텐츠 전송 네트워크 CDN + +CDN은 정적인 컨텐츠들을 전송하는데 쓰이는 분산된 서버 네트워크 이다. + +RequestPath + queryString + cookie + RequestHeader 정보에 기반하여 HTML 페이지를 캐싱하는 것. + +## CDN 사용 시 고려해야 할 사항 + +- 비용 + - CDN에 요청응답 하는 양에 따라 요금이 부과된다. + - 무조건 정말 자주 사용되는 컨텐츠를 CDN에 넣어주자. +- 적절한 만료 시한 설정 + - 시의성이 중요한 컨텐츠라면 만료 시점을 잘 정해주자. +- CDN 장애에 대한 대처 방안 + - CDN자체가 다운된 경우 어떻게 동작해야 하는지 +- 컨텐츠 무효화 + - CDN 서비스에서 제공하는 API를 사용하여 무효화 + - 다른 버전을 사용할 수 있도록 버저닝 이용. + +# 무상태 웹 계층 + +수평적으로 확장하는 방법을 사용하려면, 상태 정보들을 웹 계층에서 제거해주어야 한다. + +상태 정보를 RDB나 NoSQL같은 저장소에 보관하며 필요할 때 빼서 사용하도록 구성하자. + +이것을 무상태 웹 계층이라고 한다. + +## 상태 정보 의존적인 아키텍처 + +상태 정보를 보관하는 서버는 클라이언트 상태 정보들을 유지하여 요청 사이에 공유될 수 있도록 한다. + +무상태는 이런 것들이 존재하지 않는다. + +대표적으로 세션이 있는데 서로 다른 웹 서버에서 각자의 세션을 갖고 있는 경우 1번 서버에서 로그인한 유저가 로드밸런싱을 통해 2번서버로 왔는데 세션이 존재하지 않는 경우 다시 로그인을 해야될 수도 있다. + +이런 현상을 막아주기 위해 서버1번으로 요청이 들어왔다면 항상 1번으로 라우팅 되게 해줘야하는 문제가 존재한다. + +이를 로드밸런서에서 지원해주려고 sticky-session 기능을 제공한다. + +→ 간단하게 사용해줄 수 있지만, 나중되면 부담이 될 수 있다. + +## 무상태 아키텍처 + +![4](https://github.com/back-end-study/system-design-interview/assets/74235102/5a70a57e-910a-46d6-83ab-15ac1218a77b) + +이렇게 구성하면 어떤 웹서버로 요청이 오든 균일한 상태정보를 가질 수 있게 된다. + +해당 공유 저장소는 redis같은 인메모리 캐시 시스템이 많이 사용되지만, RDB가 사용될 수도, 또는 NoSQL일 수도 있다. + +여태까지 구성된 아키텍처를 그림으로 보면 아래와 같다. + +![5](https://github.com/back-end-study/system-design-interview/assets/74235102/3cc351a2-0e2c-436c-8c28-8992214df2cf) + +서버에 해당하는 부분을 트래픽에 따라 자동으로 관리해주는 autoscaling을 구성할 수도 있다. + +→ 이는 우리가 이전에 학습했던 docker + k8s 조합을 사용하면 훨씬 간편하게 구성할 수 있다. + +이 마저도 가용성을 높이고 어디서든 잘 사용할 수 있게 구성하려면 여러 데이터 센터를 지원하는 것이 필수가 되겠다. + +# 데이터 센터 + +두개 이상의 데이터 센터를 사용하게 되는 경우에는 장애가 없는 상황에서 사용자로부터 가장 가까운 데이터센터로 안내되는데 이것을 **지리적 라우팅** 이라고 부른다. + +위에서 학습했던 로드밸런싱 개념과 같이 한 데이터 센터가 불타 문제가 생겼다면, 다른 하나의 데이터센터로 모든 트래픽이 전송되게 구성된다. + +> 다중 데이터센터 아키텍처 구성 시 기술적 난제 +> +- 트래픽 우회 + - 올바른 데이터 센터로 트래픽을 보내는 방법 모색 +- 데이터 동기화 + - 데이터 센터마다의 DB 동기화 +- 테스트와 배포 + - 항상 똑같은 버전으로 두 데이터 센터에 배포가 되며, 여러 위치에서 사용해도 같은 응답을 받아볼 수 있는지를 꼼꼼하게 체크 + +# 메시지 큐 + +메시지의 무손실을 보장하는 비동기 통신을 지원하는 수단이다. + +메시지의 버퍼 역할을 하며 비동기적 전송을 수행한다. + +producer or publisher 서비스가 메시지를 만들어 발행하면, + +consumer or subscriber 서비스가 해당 메시지를 받아 그 메시지에 맞는 동작을 수행한다. + +메시지 큐를 사용하게 되면 서버간의 결합을 느슨하게 만들어 줄 수 있고, 규모 확장성이 보장되어야 하는 안정적인 애플리케이션을 구성하는데 좋다. + +producer와 consumer는 서로의 서버가 죽어도 메시지를 발행하거나 수신할 수 있다. + +# 로그, 메트릭 그리고 자동화 + +규모가 커지면 이 기능들이 중요해질 것이다. + +- 로그 + - 모니터링은 항상 중요 + - 잘게 쪼개진 서비스들의 로그를 한눈에 파악하게 구성하면 편리하게 조회해볼 수 있을것 +- 메트릭 + - 메트릭 수집이 잘 되면 유용한 정보들을 많이 얻어볼 수 있다. + - 호스트 단위 메트릭 + - 종합 메트릭 + - 핵심 비즈니스 메트릭 +- 자동화 + - 생산성을 높이기 위해 자동화 도구를 활용 + - CI 툴을 사용하면 코드 검증 절차를 자동으로 할 수 있다. + - 개발 생산성을 크게 증가시켜줄 수 있다. + +# 데이터베이스 규모 확장 + +저장할 데이터가 많아지면 데이터베이스 부하도 증가한다. + +데이터베이스의 규모 확장도 마찬가지로 수직, 수평 확장 2가지가 존재한다. + +## 수직적 확장 + +더 좋은 사양으로 업그레이드 해주는 방법이다. + +단점 + +- 무한대로 성능을 증설할 수 없음. +- SPOF 위험성 증대 +- 비용 + +## 수평적 확장 + +샤딩이라고도 부른다. + +더 많은 서버를 추가하여 성능을 향상시킬 수 있도록 한다. + +샤딩 - 대규모 DB를 shard라고 부르는 단위로 분할하는 기술 + +이 전략에서 가장 중요한 것은 아무래도 **샤딩 키를 어떻게 정하느냐** 이다. + +샤딩키 = 파티션키 어떻게 분산될지 정하는 하나 이상의 칼럼으로 구성된다. + +> 샤딩 키를 정할 때는 데이터를 고르게 분할 할 수 있도록 하는게 가장 중요 +> + +샤딩은 훌륭한 기술인 것은 맞지만, 완벽하지 않다. + +시스템이 복잡해지고 새로운 문제들을 마주할 수 있다. + +- 데이터의 재 샤딩 + - 데이터가 너무 많아져 하나의 샤드로는 더 이상 감당하기 어려워진 경우 + - 데이터 분포가 고르지 못하여 특정 샤드의 소모만 더 빨리 진행되는 경우 + - 안정 해시 기법을 활용하면 문제 해결이 가능 +- 유명인사 문제 + - 핫스팟 키 문제라고도 부른다. + - 특정 샤드에만 쿼리질의가 집중되어 과부하가 걸리는 문제 + - 인기있는 조회 데이터를 어떻게 고른 샤드에 분배할 수 있는지를 생각해야한다. +- 조인과 비정규화 + - 여러 샤드에 걸친 데이터를 조인하기 힘들어진다. + - 비정규화를 하여 한 테이블에서 질의할 수 있도록 하는 구성도 생각해보자. + +# 백만 사용자, 그리고 그 이상 + +시스템 규모를 확장하는 것은 지속적이고 반복적인 과정이다. + +이런 기능을 구현했는데에도 더 많은 트래픽이 발생하는 경우에 또 다른 숙제가 우리를 맞이할 것이다. + +그래서 또 지속적으로 시스템을 가다듬어주어야 한다. + +## 기법 정리 + +- 웹 계층을 무상태 계층 +- 모든 계층에 다중화 도입 +- 가능한 한 많은 데이터를 캐시하자 +- 여러 데이터 센터를 지원 +- 정적 컨텐츠는 CDN을.. +- 데이터 계층은 수평적 확장인 샤딩으로 확장 +- 각 계층은 독립적 서비스로 분할 +- 시스템을 지속적으로 모니터링하고, 자동화 도구 활용하기