15.구글 드라이브 설계

대규모 시스템 설계 기초 15장을 요약한 내용입니다.

1단계: 문제 이해 및 설계 범위 확정

설계 범위

  • 파일 추가

    • 가장 쉬운 방법은 파일을 구글 드라이브 안으로 drag-and-grop 하기

  • 파일 다운로드

  • 여러 단말에 파일 동기화

    • 한 단말에서 파일을 추갛면 다른 단말에도 자동으로 동기화

  • 파일 갱신 이력 조회

  • 파일 공유

  • 파일 편집/삭제 혹은 공유 시 알림 표시

논의하지 않을 내용

  • 구글 문서 편집 및 협업 기능

비-기능적 요구사항

  • 안정성

    • 데이터 손실이 발생하면 안 된다.

  • 빠른 동기화 속도

    • 파일 동기화에 시간이 너무 많이 걸리면 사용자는 인내심을 잃고 해당 제품을 더 이상 사용하지 않을 수있다.

  • 네트워크 대역폭

    • 네트워크 대역폭을 불필요하게 많이 소모하면 사용자가 좋아하지 않을 것이다.

  • 규모 확장성

    • 아주많은 양의 트래픽도 처리 가능해야 한다.

  • 높은 가용성

    • 일부 서버에 장애가 발생하거나, 느려지거나, 네트워크 일부가 끊겨도 시스템은 계속 사용 가능해야 한다.

개략적 추정치

  • 가입 사용자는 오천만(50million) 명이고 천만 명의 DAU 사용자가 있다고 가정

  • 모든 사용자에게 10GB의 무료 저장공간 할당

  • 매일 각 사용자가 평균 2개의 파일을 업로드한다고 가정

    • 각 파일의 평균 크기는 500KB

  • 읽기:쓰기 비율은 1:1

  • 필요한 저장 공간 총량 : 5천만 사용자 x 10GB = 500PB

  • 업로드 API QPS = 1천만 사용자 x 2회 업로드 / 24시간 / 3600초 = 약 240

  • 최대 QPS = QPS x 2 = 480

2단계: 개략적 설계안

모든 것을 담은 한 대 서버에서 출발해 점진적으로 천만 사용자 지원이 가능한 시스템으로 발전시켜 나가 보자.

  • 파일을 올리고 다운로드 하는 과정을 처리할 웹 서버

  • 사용자 데이터, 로그인 정보, 파일 정보 등의 메타데이터를 보관할 데이터베이스

  • 파일을 저장할 저장소 시스템, 파일 저장을 위해 1TB의 공간을 사용

API

이 시스템은 기본적으로 세 가지의 API가 필요하다.

파일 업로드 API

  • 단순 업로드: 파일 크기가 작을 때 사용

  • 이어 올리기: 파일 사이즈가 크고 네트워크 문제로 업로드가 중단될 가능성이 높다고 생각될 때 사용

https://api.example.com/files/upload?uploadType=resumable

인자

  • uploadType=resumable

  • data : 업로드할 로컬 파일

이어 올리기의 3단계 절차

  • (1) 이어 올리기 URL을 받기 위한 최초 요청 전송

  • (2) 데이터를 업로드하고 업로드 상태 모니터링

  • (3) 업로드에 장애가 발생하면 장애 발생 시점부터 업로드를 재시작

.

파일 다운로드 API

https://api.example.com/files/download

인자

  • path : 다운로드할 파일의 경로

    {
        "path" : "/recipes/soup/best_soup.txt"
    }

.

파일 갱신 히스토리 API

https://api.example.com/files/list revisions

인자

  • path : 갱신 히스토리를 가져올 파일의 경로

  • limit : 히스토리 길이의 최대치

    {
        "path" : "/recipes/soup/best_soup.txt",
        "limit" : 20
    }

모든 API는 사용자 인증을 필요로 하고 HTTPS 프로토콜을 사용해야 한다.

SSL(Secure Socket Layer)를 지원하는 프로토콜을 이용하는 이유는 클아이언트와 백엔드 서버가 주고 받는 데이터를 보호하기 위함이다.

한 대 서버 제약 극복

업로드되는 파일이 많아지다 보면 결국 파일 시스템은 가득 차게 된다.

가장 먼저 떠오르는 해결책은 데이터를 샤딩하여 여러 서버에 나누어 저장하는 것이다.

  • user_id 기준으로 샤딩한 예제

시장 주도 기업(넷플릭스, 에어비엔비)들은 저장소로 아마존 S3를 사용한다.

  • AWS S3(Simple Storage Service)는 업계 최고 수준의 규모 확장성, 가용성, 보안, 성능을 제공하는 객체 저장소 서비스이다.

데이터 다중화는 같은 지역 안에서만 할 수도, 여러 지역에 걸쳐 할 수도 있다.

  • 여러 지역에 걸쳐 다중화하면 데이터 손실을 막고 가용성을 최대한 보장할 수 있다.

추가로 연구해볼 수 있는 개선점

  • 로드밸런서: 네트워크 트래픽을 분산하기 위해 로드밸런서를 사용

    • 로드밸런서는 트래픽을 고르게 분산할 수 있을 뿐 아니라, 특정 웹 서버에 장애가 발생하면 자동으로 해당 서버를 우회

  • 웹 서버: 로드밸런서를 추가하고 나면 더 많은 웹 서버를 손쉽게 추가할 수 있다.

    • 트래픽이 폭중해도 쉽게 대응 가능

  • 메타데이터 데이터베이스: 데이터베이스를 파일 저장 서버에서 분리하여 SPOF(Single Point of Failuer)를 회피

    • 다중화 및 샤딩 정책을 적용하여 가용성과 규모 확장성 요구사항에 대응

  • 파일 저장소: S3를 파일 저장소로 사용하고 가용성과 데이터 무손실을 보장하기 위해 두 개 이상의 지역에 데이터를 다중화

동기화 충돌

대형 저장소 시스템의 두 명 이상의 사용자가 같은 파일이나 폴더를 동시에 업데이트하려고 할 경우 때때로 동기화 충돌이 발생할 수 있다.

이 경우, 먼저 처리되는 변경은 성공, 나중에 처리되는 변경을 충돌이 발생한 것으로 표시하여 해소할 수 있다.

충돌로 발생한 오류를 해결하는 방법

  • 오류가 발생한 시점에 이 시스템에는 같은 파일의 두 가지 버전이 존재하게 된다.

    • 사용자 2가 가지고 있는 로컬 사본과 서버에 있는 최신 버전

  • 이 상태에서 사용자는 두 파일을 하나로 합칠지, 둘 중 하나를 다른 파일로 대체할지 결정해야 한다.

개략적 설계안

구글 드라이브의 개략적 설계안

사용자 단말: 사용자가 이용하는 웹브라우저나 모바일 앱 등의 클라이언트

블록 저장소 서버(block server): 파일 블록을 클라우드 저장소에 업로드하는 서버

  • 블록 수준 저장소라고도 하며, 클라우드 환경에서 데이터 파일을 저장하는 기술

  • 파일을 여러개의 블록으로 나눠 저장하며, 각 블록에는 고유한 해시값이 할당

  • 해시값은 메타데이터 데이터베이스에 저장

  • 각 블록(4MB)은 독립적인 객체로 취급되며 클라우드 저장소 시스템에 보관

  • 파일을 재구성하려면 블록들을 원래 순서대로 합쳐야 한다.

클라우드 저장소: 파일은 블록 단위로 나눠져 클라우드 저장소에 보관

아카이빙 저장소: 오랫동안 사용되지 않은 비활성 데이터를 저장하기 위한 컴퓨터 시스템

로드밸런서: 요청을 모든 API 서버에 고르게 분산하는 구실

API 서버: 파일 업로드 외에 거의 모든 것을 담당하는 서버

  • 사용자 인증, 사용자 프로파일 관리, 파일 메타데이터 갱신 등에 사용

메타데이터 데이터베이스: 사용자, 파일, 블록, 버전 등의 메타데이터 정보를 관리

  • 실제 파일은 클라우드에 보관하며, 이 데이터베이스에는 오직 메타데이터만 둔다.

메타데이터 캐시: 성능을 높이기 위해 자주 쓰이는 메타데이터는 캐시

알림 서비스: 특정 이벤트가 발생했음을 클라이언트에게 알리는데 쓰이는 발생/구독 프로토콜 기반 시스템

  • 파일 추가/편집/삭제 알림 등

오프라인 사용자 백업 큐: 클라이언트가 접속 중이 아니라서 파일의 최신 상태를 확인할 수 없을 때는 해당 정보를 이 큐에 두어 나중에 클라이언트가 접속했을 때 동기화될 수 있도록 한다.

3단계: 상세 설계

블록 저장소 서버

정기적으로 갱신되는 큰 파일들은 업데이트가 일어날 때마다 전체 파일을 서버로 보내면 네트워크 대역폭을 많이 잡아먹게 된다.

  • 이를 최적화하는 방법은 두 가지 정도가 있다.

델타 동기화(delta sync)

  • 파일이 수정되면 전체 파일 대신 수정이 일어난 블록만 동기화

압축(compression)

  • 블록 단위로 압축해 두면 데이터 크기를 많이 줄일 수 있다.

  • 압축 알고리즘은 파일 유형에 따라 정한다.

블록 저장소 서버는 파일 업로드에 관계된 힘든 일을 처리하는 컴포넌트

  • 클라이언트가 보낸 파일을 블록 단위로 나누고

  • 각 블록에 압축 알고리즘을 적용하고

  • 암호화까지 진행

  • 전체 파일을 저장소 시스템으로 보내는 대신 수정된 블록만 전송

새 파일이 추가되었을 때 블록 젖아소 서버의 동작

  • 주어진 파일을 작은 블록들로 분할

  • 각 블록을 압축

  • 클라우드 저장소로 보내기 전에 암호호

  • 클라우드 저장소로 전송

델타 동기화 전략의 동작

델타 동기화 전략과 압축 알고리즘으로 네트워크 대역폭 사용량을 절감할 수 있다.

높은 일관성 요구사항

이 시스템은 강한 일관성 모델을 기본으로 지원

  • 같은 파일이 단말이나 사용자에 따라 다르게 보이는 것은 허용할 수 없음

  • 메타데이터 캐시와 데이터베이스 계층에도 같은 원칙 적용

메모리 캐시는 보통 결과적 일관성 모델을 지원, 따라서 강한 일관성을 달성하려면 아래 사항을 보장

  • 캐시에 보관된 사본과 데이터베이스에 있는 원본이 일치

  • 데이터베이스에 보관된 원본에 변경이 발생하면 캐시에 있는 사본을 무효화

관계형 데이터베이스는 ACID(Atomicity, Consistency, Isolation, Durability)를 보장하므로 강한 일관성을 보장하기 쉽다.

  • 하지만, NoSQL은 이를 기본으로 지원하지 않으므로, 동기화 로직 안에 프로그램해 넣어야 한다.

메타데이터 데이터베이스

(중요한 것만 간추린 단순화된 형태의) 데이터베이스 스키마 설계안

user

  • 이름, 이메일 프로파일 사진 등 사용자에 관계된 기본적 정보들이 보관

device

  • 단말 정보가 보관

  • push_id는 모바일 푸시 알림을 보내고 받기 위한 것

  • 한 사용자가 여러 대의 단말을 가질 수 있음에 유의

namespace

  • 사용자의 루트 디렉터리 정보가 보관

file

  • 파일의 최신 정보가 보관

file_version

  • 파일의 갱신 이력이 보관되는 테이블(읽기 전용)

  • 갱신 이력이 훼손되는 것을 막기 위한 조치

block

  • 파일 블록에 대한 정보를 보관하는 테이블

  • 특정 버전의 파일은 파일 블록을 올바른 순서로 조합하기만 하면 복원 가능

업로드 절차

사용자가 파일을 업로드하면 무슨 일이 벌어질까?

병렬적으로 전송되는 두 개의 요청

  • 파일 메타데이터를 추가하기 위한 요청

  • 파일을 클라우드 저장소로 업로드하기 위한 요청

파일 메타데이터 추가

  • (1) 클라이언트 1이 새 파일의 메타데이터를 추가하기 위한 요청 전송

  • (2) 새 파일의 메타데이터를 데이터베이스에 저장하고 업로드 상태를 대기중으로 변경

  • (3) 새 파일이 추가되었음을 알림 서비스에 통지

  • (4) 알림 서비스는 관련된 클라이언트(2)에게 파일이 업로드되고 있음을 알림

파일을 클라우드 저장소에 업로드

  • (2.1) 클라이언트 1이 파일을 블록 저장소 서버에 업로드

  • (2.2) 블록 저장소 서버는 파일을 블록 단위로 쪼갠 다음 압축하고 암호화 한 다음 클라우드 저장소에 전송

  • (2.3) 업로드가 끝나면 클라우드 스토리지는 완료 콜백을 호출. 이 호출은 API 서버로 전송

  • (2.4) 메타데이터 DB에 기록된 해당 파일의 상태를 완료로 변경

  • (2.5) 알림 서비스에 파일 업로드가 끝났음을 통지

  • (2.6) 알림 서비스는 관련된 클라이언트(2)에게 파일 업로드가 끝났음을 알림

파일을 수정하는 경우에도 흐름은 비슷하다.

다운로드 절차

파일이 새로 추가되거나 편집되면 자동으로 시작

다른 클라이언트가 파일을 편집하거나 추가했다는 사실을 감지하기 위해 두 가지 방법을 사용한다.

  • 클라이언트 A가 접속 중이고 다른 클라이언트가 파일을 변경하면, 알림 서비스가 클라이언트 A에게 변경이 발생했으니 새 버전을 끌어가야 한다고 알린다.

  • 클라이언트 A가 네트워크에 연결된 상태가 아닐 경우, 데이터는 캐시에 보관될 것이다. 해당 클라이언트의 상태가 접속 중으로 바뀌면 그때 해당 클라이언트는 새 버전을 가지고 간다.

특정 파일이 변경되었음을 감지한 클라이언트는 우선 API 서버를 통해 메타데이터를 새로 가져가야 하고, 그 다음에 블록들을 다운받아 파일을 재구성해야 한다.

  • (1) 알림 서비스가 클라이언트 2에게 누군가 파일을 변경했음을 알림

  • (2) 알림을 확인한 클라이언트 2는 새로운 메타데이터를 요청

  • (3) API 서버는 메타데이터 DB에게 새 메타데이터 요청

  • (4) API 서버에게 새 메타데이터가 반환

  • (5) 클라이언트 2에게 새 메타데이터가 반환

  • (6) 클라이언트 2는 새 메타데이터를 받는 즉시 블록 다운로드 요청 전송

  • (7) 블록 저장소 서버는 클라우드 저장소에서 블록 다운로드

  • (8) 클라우드 저장소는 블록 서버에 요청된 블록 반환

  • (9) 블록 저장소 서버는 클라이언트에게 요청된 블록 반환

    • 클라이언트 2는 전송된 블록을 사용하여 파일 재구성

알림 서비스

알림 서비스는 파일의 일관성을 유지하기 위해, 클라이언트가 로컬에서 파일이 수정되었음을 감지하는 순간 다른 클라이언트에게 그 사실을 알리는 목적으로 이용된다.

  • 단순하게 이벤트 데이터를 클라이언트들로 보내는 서비스

두 가지 정도의 선택지가 있다.

  • long polling: 드롭박스가 채택한 방식

  • WebSocket: 클라이언트와 서버 사이에 지속적인 통신 채널을 제공

    • 양방향 통신 가능

본 설계안에서는 롱 폴링을 선택

  • 채팅 서비스와 달리 알림 서비스와 양방향 통신이 불필요

    • 서버는 파일이 변경된 사실을 클라이언트에게 알려주어야 하지만 반대 방향의 통신은 요구되지 않음

  • 웹소켓은 실시간 양방향 통신이 요구되는 채팅 같은 응용에 적합

    • 구글 드라이브의 경우 알림을 보낼 일은 그렇게 자주 발생하지 않으며, 알림을 보내야 하는 경우에도 단시간에 많은 양의 데이터를 보낼 일이 없다.

롱 폴링 방안을 쓰면 각 클라이언트는 알림 서버와 롱 폴링용 연결을 유지하다가 특정 파일에 대한 변경을 감지하면 해당 연결을 끊는다.

  • 이때 클라이언트는 반드시 메타데이터 서버와 연결해 파일의 최신 내역을 다운로드 해야 한다.

  • 해당 다운로드 작업이 끝났거나 연결 타임아웃 시간에 도달한 경우 즉시 새 요청을 보내어 롱 폴링 연결을 복원하고 유지해야 한다.

파일 저장소 공간

파일 갱신 이력을 보존하고 안정성을 보장하기 위해서는 파일의 여러 버전을 여러 데이터센터에 보관할 필요가 있다.

  • 이 상황에서 모든 버전을 자주 백업하게 되면 저장용량이 너무 빨리 소진될 가능성이 있다.

  • 이 문제를 피하고 비용을 절감하기 위해 보통 세 가지 방법을 사용한다.

(1) 중복 제거(de-dupe)

  • 중복된 파일 블록을 계정 차원에서 제거하는 방법

  • 두 블록이 같은 블록인지는 해시 값을 비교하여 판단

(2) 지능적 백업 전략을 도입

  • 한도 설정: 보관해야 하는 파일 버전 개수에 상한 두기

    • 상한에 도달하면 제일 오래된 버전을 버린다.

  • 중요한 버전만 보관: 어떤 파일은 자주 번경된다.

    • 불필요한 버전과 사본이 만들어지는 것을 피하려면 그 가운데 중요한 것만 골라내야 한다.

(3) 자주 쓰이지 않는 데이터는 아카이빙 저장소로 옮긴다.

  • 몇달 혹은 수년간 이용되지 않은 데이터가 이에 해당

  • 아마존 S3 glacier 같은 아카이빙 저장소는 S3보다 저렴

장애 처리 흐름

다루면 좋을 부류의 장애

로드밸런서 장애

  • 부(secondary) 로드밸런서가 활성화되어 트래픽을 이어받아야 한다.

  • 로드 밸런서끼리는 보통 박동 신호를 주기적으로 보내서 상태를 모니터링한다.

  • 일정 시간 동안 박동 신호에 응답하지 않는 로드밸런서는 장애가 발생한 것으로 간주

블록 저장소 서버 장애

  • 다른 서버가 미완료 상태 또는 대기 상태인 작업을 이어받아야 한다.

클라우드 저장소 장애

  • S3버킷은 여러 지역에 다중화가 가능하므로, 한 지역에서 장애가 발생하였다면 다른 지역에서 파일을 가져오면 된다.

API 서버 장애

  • API 서버들은 무상태 서버이다.

  • 따라서 로드밸런서는 API 서버에 장애가 발생하면 트래픽을 해당 서버로 보내지 않음으로써 장애 서버를 격리

메타데이터 캐시 장애

  • 메타데이터 캐시 서버도 다중화

  • 따라서 한 노드에 장애가 생겨도 다른 노드에서 데이터를 가져올 수 있다.

  • 장애가 발생한 서버는 새 서버로 교체

메타데이터 데이터베이스 장애

  • 주 데이터베이스 서버 장애

    • 부 DB 서버 가운데 하나를 주 DB 서버로 바꾸고, 부 DB 서버를 새로 하나 추가

  • 부 데이터베이스 서버 장애

    • 다른 부 DB 서버가 읽기 연산을 처리하도록 하고 그동안 장애 서버는 새 것으로 교체

알림 서비스 장애

  • 접속 중인 모든 사용자는 알림 서버와 롱 폴링 연결을 유지

  • 알림 서비스는 많은 사용자와의 연결을 유지하고 관리해야 한다.

  • 한 대 서버로 백만 개 이상의 접속을 유지하는 것은 가능하지만, 동시에 백만 개 접속을 시작하는 것은 불가

  • 롱 폴링 연결을 복구하는 것은 상대적으로 느릴 수 있다.

오프라인 사용자 백업 큐 장애

  • 이 큐 또한 다중화를 해야 한다.

  • 큐에 장애가 발생하면 구독 중인 클라이언트들은 백업 큐로 구독 관계를 재설정해야 한다.

4단계: 마무리

구글 드라이브 시스템 설계

  • 높은 수준의 일관성

  • 낮은 네트워크 지연

  • 빠른 동기화

크게 두 가지 부분으로 구성된 설계안

  • 파일의 메타데이터를 관리하는 부분

  • 파일 동기화를 처리하는 부분

알림 서비스로 이 두 부분과 병존하는 또 하나의 중요 컴포넌트

롱 폴링을 사용하여 클라이언트로 하여금 파일의 상태를 최신으로 유지

Last updated