대규모 설계 기초
[System Design Interview] 15장 구글 드라이브 설계
Wanderer Kim
2023. 11. 4. 22:39
728x90
구글 드라이브란?
파일 저장 및 동기화 서비스로, 문서, 사진, 비디오, 기타 파일을 클라우드에 보관할 수 있도록 한다. 아울러 보관된 파일은 친구, 가족, 동료 들과 손쉽게 공유할 수 있어야 한다.
1단계 문제 이해 및 설계 범위 확정
설계 범위
- 파일 업로드/다운로드, 파일 동기화, 알림 기능들을 지원해야 한다.
- 모바일 앱과 웹 모두 지원해야 한다.
- 파일 암호화 기능을 지원해야 한다.
- 파일 크기는 10GB로 제한해야 한다.
- 일간 능동 사용자 천만명을 지원해야 한다.
이번 장에서 집중할 기능들
- 파일 추가
- 가장 쉬운 방법은 파일을 구글 드라이브 안으로 떨구는 것이다.
- 파일 다운로드
- 여러 단말에 파일 동기화
- 한 단말에서 파일을 추가하면 다른 단말에도 자동으로 동기화되어야 한다.
- 파일 갱신 이력 조회
- 파일 공유
- 파일이 편집되거나 삭제되거나 새롭게 공유되었을 때 알림 표시
이번 장에서 논의하지 않을 기능
- 구글 문서 편집 및 협업 기능
비-기능적 요구사항들
- 안정성
- 빠른 동기화 속도
- 네트워크 대역폭
- 네트워크 대역폭을 불필요하게 많이 소모하면 안된다.개략
- 규모 확장성
- 높은 가용성
개략적 추정치
- 가입 사용자는 오천만명이고 천만 명의 DAU 사용자가 있다고 가정
- 모든 사용자에게 10GB의 무료 저장공간 할당
- 매일 각 사용자가 평균 2개의 파일을 업로드한다고 가정. 각 파일의 평균 크기는 500GB
- 읽기/쓰기 비율은 1:1
- 필요한 저장공간 총량 = 5천만 사용자 * 10GB = 500페타바이트
- 업로드 API QPS = 1천만 사용자 * 2회 업로드 / 24시간 / 3600초 = 약 240
- 최대 QPS = QPS * 2 = 480
2단계 개략적 설계안 제시 및 동의 구하기
- 이번 장에서는 모든 것을 한 대 서버에서 출바해 점진적으로 천만 사용자 지원이 가능한 시스템으로 발전시켜 나갈것이다.
- 우선 아래와 같은 구성의 서버 한 대로 시작한다.
- 파일을 올리고 다운로드 하는 과정을 처리할 웹 서버
- 사용자 데이터, 로그인 장보, 파일 정보 등의 메타데이터를 보관할 데이터베이스
- 파일을 저장할 저장소 시스템, 파일 저장을 위해 1TB의 공간을 사용함
- figure 15-3은 driver/ 디렉터리에 실제 파일이 보관된 사례다.
API
1. 파일 업로드 API
이 시스템은 두 가지 종류의 업로드를 지원한다.
- 단순 업로드 : 파일 크기가 작을 때 사용한다.
- 이어 올리기 : 파일 사이즈가 크고 네트워크 문제로 업로드가 중단될 가능성이 높다고 생각되면 사용한다.
이어 올리기 API예
- 이어 올리기 단계
- 이어 올리기 URI을 받기 위한 최초 요청 전송
- 데이터를 업로드하고 업로드 상태 모니터링
- 업로드에 장애가 발생하면 장애 발생시점부터 업로드 재시작
2. 파일 다운로드 API
3. 파일 갱신 히스토리 API
한 대 서버의 제약 극복
- 업로드되는 파일이 많아 지다 보면 결국에는 figure 15-4와 같이 파일 시스템이 가득차게 된다.
- 가장 먼저 떠오르는 해결책은 데이터를 샤딩하여 여러 서버에 나누어 저장하는 것이다.
- figure 15-5는 user_id를 기준으로 샤딩한 예제이다.
- 서비스의 파일은 아마존 S3저장소에 저장하기로 한다.
- 데이터 손실을 방지하기 위해 S3를 다중화 하기로 한다. figure 15-6은 다중화가 어떻게 이루어지는지 나타낸다.
- 미래에 비슷한 문제가 벌어지는 것을 방지하기 위해 아래와 같은 점들을 개선하기로 한다.
- 로드밸런서 : 네트워크 트래픽을 분산하기 위해 로드밸런서를 사용한다.
- 웹 서버 : 로드밸런서를 추가하고 나면 더 많은 웹 서버를 손쉽게 추가할 수 있다. 따라서 트래픽이 폭증해도 쉽게 대응이 가능하다.
- 메타데이터 데이터베이스 : 데이터베이스를 파일 저장 서버에서 분리하여 SPOF를 회피한다.
- 파일 저장소 : S3를 파일 저장소로 사용하고 가용성과 데이터 무손실을 보장하기 위해 두 개 이상의 지역에 데이터를 다중화한다.
- figure 15-7은 위 수정사항들을 반영한 후 설계안이다.
동기화 충돌
- 구글 드라이브와 같은 대형 저장소 시스템의 경우 두 명 이상의 사용자가 같은 파일이나 폴더를 동시에 업데이트하려고 하면, 때때로 동기화 충돌이 발생할 수 있다.
- 이런 충돌을 해소하기 위해 다음의 전략을 사용할 것이다.
- 먼저 처리되는 변경은 성곤한 것으로 보고, 나중에 처리되는 변경은 충돌이 발생한 것으로 표시하는 것이다.
- figure 15-8에서 사용자1과 2는 같은 파일을 동시에 갱신하려 한다. 하지만 이 시스템은 사용자1의 파일을 먼저 처리했다.
- 사용자 1의 파일 갱신 시도는 정상적으로 처리되지만 사용자 2에 대해서는 동기화 충돌 오류가 발생할것이다.
- 이 오류에 대한 해결책은 아래와 같다.
- 오류가 발생한 시점에 이 시스템에는 같은 파일의 두 가지 버전이 존재하게 된다.
- 즉, 사용자 1가 가지고 있는 로컬 사본과 서버에 있는 최신 버전이 그것이다. 이 상태에서 사용자는 두 파일을 하나로 합칠지 아니면 둘 중 하나를 다른 파일로 대체할지를 결정해야 한다.
개략적 설계안
- figure 15-10은 이번 면접 문에에 대한 개략적 설계안이다.
- 사용자 단말 : 사용자가 이용하는 웹크라우저나 모바일 앱 등의 클라이언트.
- 블록 저장소 서버 : 파일 블록을 클라우드 저장소에 업로드하는 서버다. 블록 저장소는 블록 수준 저장소라고도 하며, 클라우드 환경에서 데이터 파일을 저장하는 기술이다.
- 클라우드 저장소 : 파일은 블록 단위로 나눠져 클라우드 저장소에 보관된다.
- 아카이빙 저장소 : 오랫동안 사용되지 않은 비활성 데이터를 저장하기 위한 컴퓨터 시스템이다.
- 로드밸런서 : 요청은 모든 API 서버에 고르게 분산하는 구심을 한다.
- API 서버 : 파일 업로드 외에 거의 모든 것을 담당하는 서버다. 사용자 인증, 사용자 프로파일 관리, 파일 메타데이터 갱신 등에 사용된다.
- 메타데이터 데이터베이스 : 사용자, 파일, 블록, 버전 등의 메타데이터 정보를 관리한다. 실제 파일은 클라우드에 보관하며, 이 데이터베이스에는 오직 메타데이터만 둔다.
- 메타데이터 캐시 : 성능을 높이기 위해 자주 쓰이는 메타데이터는 캐싱한다.
- 알림 서비스 : 특정 이벤트가 발생했음을 클라이언트에게 알리는데 쓰이는 발생/구독 프로토콜 기반 시스템이다.
- 오프라인 사용자 백업 큐 : 클라이언트가 접속 중이 아니라서 파일의 최신 상태를 확인할 수 없을 때는 해당 정보를 이 큐에 두어 나중에 클라이언트가 접속했을 때 동기화될 수 있도록 한다.
3단계 상세 설계
블록 저장소 서버
- 정기적으로 갱신되는 큰 파일들은 업데이트가 일어날 때마다 전체 파일을 서버로 보내면 네트워크 대역폭을 많이 잡아먹게 된다.
- 이를 최적화하는 방법으로는 두 가지 정도를 생각해 볼 수 있다.
- 델타 동기화 : 파일이 수정되면 전체 파일 대신 수정이 일어난 블록만 동기화한다.
- 압축 : 블록 단위로 압축해 두면 데이터 크기를 많이 줄일 수 있다. 이때 압축 알고리즘은 파일 유형에 따라 정한다.
- 새 파일이 추가되었을 때 블록 저장소 서버가 어떻게 동작하는지 figure 15-11에 나와 있다.
- 주어진 파일을 작은 블록들로 분할한다.
- 각 블록을 압축한다.
- 클라우드 저장소로 보내기 전에 암호화한다.
- 클라우드 저장소로 보낸다.
- figure 15-12는 델타 동기화 전략이 어떻게 동작하는지를 보여준다.
- 검정색으로 표시된 블록 2와 5는 수정된 블록이다.
- 갱신된 부분만 동기화해야 하므로 이 두 블록만 클라우드 저장소에 업로드하면 된다.
높은 일관성 요구사항
- 이 시스템은 강한 일관성 모델을 기본으로 지원해야 한다.
- 메타데이터 캐시와 데이터베이스 계층에도 같은 원칙이 적용되어야 한다.
- 메모리 캐시는 보통 최종 일관성 모델을 지원한다. 따라서 강한 일관성을 달성하려면 다음 사항을 보장해야 한다.
- 캐시에 보관된 사본과 데이터베이스에 있는 원본이 일치한다.
- 데이터베이스에 보관된 원본에 변경이 발생하면 캐시에 있는 사본을 무효화한다.
- 관계형 데이터베이스는 ACID(Atomicity, Consistency, Isolation, Durability)를 보장하므로 강한 일관성을 보장하기 쉽다.
- NoSQL 데이터베이스는 ACID를 지원하지 않으므로, 동기화 로직 안에 프로그램해 넣어야 한다.
- 본 설계에서는 ACID를 지원하는 관계형 데이터베이스를 채택할 것이다.
메타데이터 데이터베이스
- figure 15-13은 이 데이터베이스의 스키마 설계안이다.
- user : 이름, 이메일, 프로파일 사진 등 사용자에 관계된 기본적 정보들이 보관된다.
- device : 단말 정보가 보관된다.
- namespace : 사용자의 루트 디렉터리 정보가 보관된다.
- file : 파일의 최신 정보가 보관된다.
- file_version : 파일의 갱신 이력이 보관되는 테이블이다.
- block : 파일 블록에 대한 정보를 보관하는 테이블이다.
업로드 절차
- figure 15-14는 사용자가 파일을 업로드하면 무슨 일이 벌어지는지 설명하는 시퀀스 다이어그램이다.
- 위 다이어그램은 두 개 요청이 병렬적으로 전송된 상황을 보여준다.
- 첫 번째 요청은 파일 메타데이터를 추가하기 위한 것이고, 두 번째 요청은 파일을 클라우드 저장소로 업로드하기 위한 것이다.
- 파일 메타데이터 추가
- 클라이언트 1이 새 파일의 메타데이터를 추가하기 위한 요청 전송
- 새 파일의 메타데이터를 데이터베이스에 저장하고 업로드 상태를 대기중으로 변경
- 새 파일이 추가되었음을 알림 서비스에 통지
- 알림 서비스는 관련된 클라이언트에게 파일이 업로드되고 있음을 알림
- 파일을 클라우드 저장소에 업로드
- 클라이언트 1이 파일을 블록 저장소 서버에 업로드
- 블록 저장소 서버는 파일을 블록 단위로 쪼갠 다음 압축하고 암호화 한 다음에 클라우드 저장소에 전송
- 업로드가 끝나면 클라우드 스토리지는 완료 콜백을 호출, 이 콜백 호출은 API 서버로 전송됨
- 메타데이터 DB에 기록된 해당 파일의 상태를 완료로 변경
- 알림 서비스에 파일 업로드가 끝났음을 통지
- 알림 서비스는 관련된 클라이언트에게 파일 업로드가 끝났음을 알림
- 파일 수정하는 경우도 위의 흐름과 비슷하다.
- 파일 메타데이터 추가
다운로드 절차
- 클라이언트는 다른 클라이언트가 파일을 편집하거나 추가했다는 사실을 아래 두 가지 방법들을 사용해서 감지한다.
- 클라이언트 A가 접속 중이고 다른 클라이언트가 파일을 변경하면 알림 서비스가 클라이언트 A에게 변경이 발생했으니 새 버전을 끌어가야 한다고 알린다.
- 클라이언트 A가 네트워크에 연결된 상태가 아닐 경우에는 데이터는 캐시에 보관될 것이다.
- figure 15-15는 어떤 파일이 변경되었음을 감지한 클라이언트는 우선 API 서버를 통해 메타데이터를 새로 가져가야 하고, 그 다음에 블록들을 다운받아 파일을 재구성하는 흐름을 보여준다.
- 알림 서비스가 클라이언트 2에게 누군가 파일을 변경했음을 알림
- 알림을 확인한 클라이언트 2는 새로운 메타데이터를 요청
- API 서버는 메타데이터 데이터베이스에게 새 메타데이터 요청
- API 서버에게 새 메타데이터가 반환됨
- 클라이언트 2에게 새 메타데이터가 반환됨
- 클라이언트 2는 새 메타데이터를 받는 즉시 블록 다운로드 요청 전송
- 블록 저장소 서버는 클라우드 저장소에서 블록 다운로드
- 클라우드 저장소는 블록 서버에 요청된 블록 반환
- 블록 저장소 서버는 클라이언트에게 요청된 블록 반환, 클라이언트 2는 전송된 블록을 사용하여 파일 재구성
알림 서비스
- 파일의 일관성을 유지하기 위해, 클라이언트는 로컬에서 파일이 수정되었음을 감지하는 순간 다른 클라이언트에 그 사실을 알려서 충돌 가능성을 줄여야 한다. 알림 서비스는 그 목적으로 이용된다.
- 알림 서비스는 이벤트 데이터를 클라이언트에게 보내는 서비스가. 따라서 아래 두 가지 정도의 선택지가 있다.
- 롱 폴링
- 웹소켓
- 본 설계안의 경우에는 롱 폴링을 사용할 것인데 이유는 아래와 같다.
- 채팅 서비스와 달리, 본 시스템의 경우 알림 서비스와 양방향 통신이 필요하지 않다.
- 웹소켓은 실시간 양방향 통신이 요구되는 채팅 같은 응용에 적합하다. 구글 드라이브의 경우 알림을 보낼 일은 그렇게 자주 발생하지 않으며, 알림을 보내야 하는 경우에도 단시간에 많의 양의 데이터를 보낼 일은 없다.
- 롱 폴링 방안을 쓰게 되면 각 클라이언트는 알림 서버와 롱 폴링용 연결을 유지하다가 특정 파일에 대한 변경을 감지하면 해당 연결을 끊는다.
- 이때 클라이언트는 반드시 메타데이터 서버와 연결해 파일의 최신 내역을 다운로드 해야한다.
저장소 공간 절약
- 저장용량을 너무 빨리 소진하지 않기 위해 아래와 같은 방법을 사용한다.
- 중복 제거 : 중복된 파일 블록을 계정 차원에서 제거하는 방법이다. 두 블록이 같은 블록인지는 해시 값을 비교하여 판단한다.
- 지능적 백업 전략을 도입한다.
- 한도 설정
- 중요한 버전만 보관
- 자주 쓰이지 않는 데이터는 아카이빙 저장소로 옮긴다. 몇달 혹은 수년간 이용되지 않은 데이터가 이에 해당한다. 아마존 S3 글래시어같은 아카이빙 저장소 이용료는 S3보다 훨씬 저렴하다.
장애 처리
- 로드밸런서 장애 : 로드밸런서에 장애가 발생할 경우 부 로드밸런서가 활성화되어 트래픽을 이어받아야 한다.
- 블록 저장소 서버 장애 : 블록 저장소 서버에 장애가 발생하였다면 다른 서버가 미완료 상태 또는 대기 상태인 작업을 이어받아야 한다.
- 클라우드 저장소 장애 : S3 버킷은 여러 지역에 다중화할 수 있으므로, 한 지역에서 장애가 발생하였다면 다른 지역에서 파일을 가져오면 된다.
- API 서버 장애 : 로드밸런서는 API 서버에 장애가 발생하면 트래픽을 해당 서버로 보내지 않음으로써 장애 서버를 격리할 것이다.
- 메타데이터 캐시 장애 : 메타데이터 캐시 서버도 다중화한다.
- 메타데이터 데이터베이스 장애
- 주 데이터베이스 서버 장애 : 부 데이터베이스 서버 가운데 하나를 주 데이터베이스 서버로 바꾸고, 부 데이터베이스 서버를 새로 하나 추가한다.
- 부 데이터베이스 서버 장애 : 다른 부 데이터베이스 서버가 읽기 연산을 처리하도록 하고 그동안 장애 서버는 새 것으로 교체한다.
- 알림 서비스 장애 : 한 대 서버에 장애가 발생하면 사용자들의 롱 폴링 연결을 다시 만들어야 한다. 주의할 것은 한 대 서버로 백만 개 이상의 접속을 유지하는 것은 가능하지만, 동시에 여러 개 접속을 시작하는 것은 불가능하다는 것이다. 따라서 롱 폴링 연결을 복구하는 것은 상대적으로 느릴 수 있다.
- 오프라인 사용자 백업 큐 장애 : 이 큐 또한 다중화해 두어야 한다.
반응형