[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 15장. 구글 드라이브 설계

1단계. 문제 이해 및 설계 범위 확정
기능적 요구사항
- 파일 추가. 가장 쉬운 방법으로는 구글 드라이브 안으로 drag-and-drop 가능하도록
- 파일 다운로드
- 여러 단말에 파일 동기화. 한 단말에서 파일 추가 시 다른 단말에도 자동으로 동기화
- 파일 갱신 이력 조회(revision history)
- 파일 공유
- 파일이 편집되거나 삭제되거나 새롭게 공유되었을 때 알림 표시
비기능적 요구사항
- 안정성: 저장소 시스템에서 안정성은 매우 중요. 데이터 손실 발생하면 안됨
- 빠른 동기화 속도
- 네트워크 대역폭: 너무 많이 사용하면 사용자 불편 초래
- 규모 확장성: 아주 많은 양의 트래픽도 처리 가능해야 함
- 높은 가용성: 일부 서버에 장애가 발생하거나, 느려지거나, 네트워크 일부가 끊겨도 시스템은 계속 사용 가능해야 함
개략적 추정치
- 가입 사용자 오천만명. 천만명의 DAU 사용자.
- 모든 사용자에게 10GB의 무료 저장공간 할당
- 매일 각 사용자가 평균 2개의 파일을 업로드한다고 가정. 각 파일의 평균 크기는 약 500KB
- 읽기:쓰기 비율은 1:1
- 필요한 저장공간 총량 = 오천만 사용자 * 10GB = 500페타바이트(Petabyte)
- 업로드 API QPS = 천만 사용자 * 2회 업로드/24시간/3600초 = 약 240
- 최대 QPS = OPS * 2 = 480
2단계. 개략적 설계안 제시 및 동의 구하기
서버 구성
- 파일을 올리고 다운로드 하는 과정을 처리할 웹 서버
- 사용자 데이터, 로그인 정보, 파일 정보 등의 메타데이터를 보관할 데이터베이스
- 파일을 저장할 저장소 시스템. 파일 저장을 위해 1TB의 공간을 사용할 것
API
1. 파일 업로드 API
- 단순 업로드 : 파일 크기가 작을 때 사용
- 이어 올리기(resumable upload) : 파일 사이즈가 크고 네트워크 문제로 업로드가 중단될 가능성이 높다고 생각되면 사용
- 이어올리기의 경우 다음 세 단계의 절차로 이루어짐
- 이어올리기 URL을 받기 위한 최초 요청 전송
- 데이터를 업로드하고 업로드 상태 모니터링
- 업로드에 장애가 발생하면 장애 발생시점부터 업로드를 재시작
- 이어올리기의 경우 다음 세 단계의 절차로 이루어짐
2. 파일 다운로드 API
3. 파일 갱신 히스토리 API
지금까지 나열한 모든 API 는 사용자 인증을 필요로 하고 HTTPS 프로토콜을 사용해야 함.
SSL(Secure Socket Layer)를 지원하는 프로토콜을 이용하는 이유 : 클라이언트와 백엔드 서버가 주고받는 데이터 보호 목적
한 대 서버의 제약 극복
업로드 파일이 많아지면 파일 시스템 용량 부족하게 됨..
해결책1. 데이터를 샤딩(sharding) 하여 여러 서버에 나누어 저장

아마존S3
- 다중화 지원. 같은 지역 안에서 다중화도 가능하고, 여러 지역에 걸쳐 다중화도 가능 : AWS 서비스 지역(region)은 아마존 AWS가 데이터 센터를 운영하는 지리적 영역
- 여러 지역에 걸쳐 다중화하면 데이터 손실 막고, 가용성 최대 보장됨
로드밸런서 : 네트쿼으 트래픽 분산 목적
웹 서버 : 트래픽이 폭증해도 쉽게 대응 가능
메타데이터 데이터베이스 : 데이터베이스를 파일 저장 서버에서 분리하여 SPOF(Single Point of Failure) 을 회피
파일 저장소 : S3를 파일 저장소로 사용하고 가용성과 데이터 무손실을 보장하기 위해 두 개 이상의 지역에 데이터를 다중화
위의 사항들에 맞춰 수정한 설계안

동기화 충돌
먼저 처리되는 변경은 성공한 것으로 보고, 나중에 처리되는 변경은 충돌이 발생한 것으로 표시
→ 오류 발생 시점에 시스템 내에는 같은 파일이 2가지 버전으로 존재 : 사용자2 가 가지고 있는 로컬 사본(local copy) 과 서버에 있는 최신 버전
→ 이 상태에서 사용자는 두 파일을 하나로 합칠 지 아니면 둘 중 하나를 다른 파일로 대체할 지를 결정해야 한다.
Differential Synchronization
Differential Synchronization - Youtube Talk
일단은 메모해두고 추후에 읽어보기로 한다.
개략적 설계안

- 블록 저장소 서버(block server) : 파일 블록을 클라우드 저장소에 업로드하는 서버. 블록 수준 저장소(block-level storage). 클라우드 환경에서 데이터 파일을 저장하는 기술. 이 저장소는 파일을 여러 개의 블록으로 나누어 저장하며 각 블록에는 고유한 해시값 이 할당됨. 이 해시값은 메타데이터 데이터베이스에 저장. 예시 설계안의 경우 한 블록은 dropbox의 사례를 참고하여 최대 4MB 로 지정
- 클라우드 저장소
- 아카이빙 저장소(cold storage) : 오랫동안 사용되지 않은 비활성(inactive) 데이터를 저장하기 위한 시스템
- 로드밸런서
- API 서버 : 파일 업로드 외에 거의 모든 것을 담당하는 서버. 사용자 인증, 사용자 프로파일 관리, 파일 메타데이터 갱신 등에 사용
- 알림 서비스 : 특정 이벤트가 발생했음을 클라이언트에게 알리는데 쓰이는 발생/구독 프로토콜 기반 시스템
- 오프라인 사용자 백업 큐(offline backup queue) : 클라이언트가 접속 중이 아니라 파일의 최신 상태를 확인할 수 없을 때 - 해당 정보를 이 큐에 두어 나중에 클라이언트가 접속했을 때 동기화될 수 있도록...
3단계. 상세 설계
블록 저장소 서버
정기적으로 갱신되는 큰 파일들을 업데이트가 일어날 때마다 전체 파일을 서버로 보내면 네트워크 대역폭을 많이 잡아먹게 되는데.. 이를 최적화 하는 방법
- 델타 동기화(delta sync) : 파일이 수정되면 전체 파일 대신 수정이 일어난 블록만 동기화
- 압축(compression) : 블록 단위로 압축해 주면 데이터 크기를 많이 줄일 수 있음. 압축 알고리즘은 파일 유형에 따라 지정.
블록 저장소 서버는 클라이언트가 보낸 파일을 블록 단위로 나눠야 하고, 각 블록에 압축 알고리즘을 적용해야 하고, 암호화까지 해야 함. 아울러 전체 파일을 저장소 시스템으로 보내는 대신 수정된 블록만 전송해야 함.
높은 일관성 요구사항
같은 파일이 단말이나 사용자에 따라 다르게 보이면 안됨
메타데이터 캐시와 데이터베이스 계층에도 같은 원칙이 적용되어야 함
메모리 캐시는 보통 최종 일관성(eventual consistency) 모델 을 지원
강한 일관성을 달성하려면 ...?
- 캐시에 보관된 사본과 데이터베이스에 있는 원본(master)이 일치
- 데이터베이스에 보관된 원본에 변경이 발생하면 캐시에 있는 사본을 무효화
메타데이터 데이터베이스
데이터베이스의 스키마 설계안

업로드 절차
두 개 요청이 병렬적으로 전송된 상황
첫 번째 요청은 파일 메타데이터를 추가하기 위한 것.
두 번째 요청은 파일을 클라우드 저장소로 업로드하기 위한 것.
→ 전부 클라이언트1 이 보낸 것

- 파일 메타데이터 추가
- 클라이언트1 이 새 파일의 메타데이터를 추가하기 위한 요청 전송
- 새 파일의 메타데이터를 데이터베이스에 저장하고 업로드 상태를 대기중(pending)으로 변경
- 새 파일이 추가되었음을 알림 서비스에 통지
- 알림 서비스는 관련 클라이언트(클라이언트2) 에게 파일이 업로드되고 있음을 알림
- 파일을 클라우드 저장소에 업로드
- 클라이언트1 이 파일을 블록 저장소 서버에 업로드
- 블록 저장소 서버는 파일을 블록 단위로 쪼갠 다음 압축하고 암호화 한 다음 클라우드 저장소에 전송
- 업로드가 끝나면 클라우드 스토리지는 완료 콜백(callback)을 호출. 이 콜백 호출은 API 서버로 전송
- 메타데이터 DB에 기록된 해당 파일의 상태를 완료(uploaded)로 변경
- 알림 서비스에 파일 업로드가 끝났음을 통지
- 알림 서비스는 관련 클라이언트(클라이언트2) 에게 파일 업로드가 끝났음을 알림
다운로드 절차

알림 서비스
파일의 일관성을 유지하기 위해, 클라이언트는 로콜에서 파일이 수정되었음을 감지하는 순간
다른 클라이언트에 그 사실을 알려서 충돌 가능성을 줄여야 함.
알림서비스는 이벤트 데이터를 클라이언트들로 보내는 서비스
- 롱 폴링(long polling)
- 웹소켓(WebSocket) : 클라이언트와 서버 사이에 지속적인 통신 채널을 제공. 양방향 통신 가능. 채팅 같은 응용에 적합
본 설계안의 경우에는 롱 폴링 방식을 채택 : 양방향 통신 필요 없으므로
롱 폴링 방안을 쓰게 되면 각 클라이언트는 알림 서버와 롱 폴링용 연결을 유지하다
특정 파일에 대한 변경을 감지하면 해당 연결을 끊음
이때 클라이언트는 반드시 메타데이터 서버와 연결해 파일의 최신 내역을 다운로드 해야함.
→ 해당 다운로드 작업이 끝났거나 연결 타임아웃 시간에 도달한 경우, 즉시 새 요청을 보내서 롱 폴링 연결을 복원하고 유지해야 함
저장소 공간 절약
- 중복제거(de-dupe) : 중복된 파일 블록을 계정 차원에서 제거하는 방법. 두 블록 비교는 해시 값을 통하여 비교.
- 지능적 백업 전략 도입
2.1 한도 설정: 보관해야 하는 파일 버전 개수에 상한을 두는 것. 제일 오래된 것 버림
2.2 중요한 버전만 보관 - 자주 쓰이지 않는 데이터는 아카이빙 저장소(cold storage)로 옮김
장애 처리
- 로드밸런서 장애 : 로드 밸런서끼리 보통 박동(heartbeat) 신호를 보내서 상태를 모니터링 함
- 블록 저장소 서버 장애
- 클라우드 저장소 장애
- API 서버 장애 : API서버들은 무상태 서버. 로드밸런서는 API서버에 장애가 발생하면 트래픽을 해당 서버로 보내지 않음으로써 장애 서버를 격리할 것
- 메타데이터 캐시 장애
- 메타데이터 데이터베이스 장애
- 알림 서비스 장애 : 한 대 서버에 장애가 발생하면 수만 명 이상의 사용자가 롱 폴링 연결을 다시 만들어야 함 - 복구 시 시간 오래 걸림
- 오프라인 사용자 백업 큐 장애 : 큐에 장애가 발생하면 구독 중인 클라이언트 들은 백업 큐로 구독 관계를 재설정해야함.
코멘트 안 단 것들은 다중화가 되어있으니.. 다른 서버나 부 서버로 옮겨라는 코멘트..
4단계. 마무리
이번 장의 핵심 부분 1) 파일의 메타데이터를 관리하는 부분 2) 파일 동기화를 처리하는 부분
- 알림 서비스는 이 두 부분과 병존하는 또 하나의 중요 컴포넌트 : 롱 폴링을 사용하여 클라이언트로 하여금 파일의 상태를 최신으로 유지