Skip to content

Latest commit

 

History

History
333 lines (271 loc) · 21.9 KB

15장_구글_드라이브_설계.md

File metadata and controls

333 lines (271 loc) · 21.9 KB

15장 구글 드라이브 설계

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

  • 지원해야 할 기능
    • 파일 업로드/다운로드, 파일 동기화, 알림
    • 모바일앱, 웹 앱 둘 다 지원
    • 파일 암호화 지원
    • 파일 크기 10GB 제한
    • 가입 사용자: 오천만명
    • 일간 능동 사용자(DAU) 기준: 천만명
    • 파일 추가(drag-and-drop)
    • 파일 다운로드
    • 여러 단말에 파일 동기화
    • 파일 갱신 이력 조회
    • 파일 공유
    • 파일이 편집/삭제/공유되었을 때 알림
  • 지원하지 않을 기능: 구글 문서 편집 및 협업 기능
  • 비 기능적 요구사항
    • 안정성: 저장소 시스템이므로 데이터 손실이 발생하면 안됨
    • 빠른 동기화 속도
    • 네트워크 대역폭: 특히 모바일 데이터 플랜을 사용하는 경우라면 좋아하지 않을 것
    • 규모 확장성: 많은 양의 트래픽 처리 가능
    • 높은 가용성: 장애가 발생하더라도 시스템은 계속 사용 가능해야 함

개략적 추정치

  • 모든 사용자에게 10GB의 무료 저장공간 할당
  • 매일 각 사용자가 평균 2개의 파일을 업로드 (각 파일의 평균 크기 500KB)
  • 읽기:쓰기 비율은 1:1
  • 필요한 저장공간 총량 = 5천만 사용자 X 10GB = 500페타바이트
  • 업로드 API QPS(Query Per Second) = 1천만 사용자 X 2회 업로드 / 24시간 / 3600초 = 약 240
  • 최대 QPS = QPS X 2 = 480

2단계 개략적 설계안 제시 및 동의 구하기

  • 아래와 같은 구성의 서버 한 대로 시작

    • 파일을 올리고 다운로드 하는 과정을 처리할 웹 서버
    • 사용자 데이터, 로그인 정보, 파일 정보 등의 메타데이터를 보관할 데이터베이스
    • 파일을 저장할 저장소 시스템. 파일 저장을 위해 1TB 공간 사용
  • 준비하기

    IMG_2643.PNG

    • 아파치 웹 서버 설치
    • MySQL 데이터베이스 설치
    • 업로드되는 파일을 저장할 drive 디렉터리 생성
      • 각 네임스페이스 안에는 특정 사용자가 올린 파일이 보관됨
      • 각 파일들은 원래 파일과 같은 이름을 가짐
      • 각 파일과 폴더는 그 상대 경로를 네임스페이스 이름과 결합하면 유일하게 식별 가능

API

  1. 파일 업로드 API (두 가지 종류의 업로드를 지원)

    • 단순 업로드: 파일 크기가 작을 때 사용
    • 이어 올리기: 파일 사이즈가 크고 네트워크 문제로 업로드가 중단될 가능성이 높다고 생각될 때 사용
      • 예) https://api.example.com/files/upload?uploadType=resumable
        • 인자 - uploadType=resumable, data: 업로드할 로컬 파일
      • 이어 올리기의 세 단계 절차
        • 이어 올리기 URL 을 받기 위한 최초 요청 전송
        • 데이터를 업로드하고 업로드 상태 모니터링
        • 업로드 장애가 발생하면 장애 발생 시점부터 업로드를 재시작
  2. 파일 다운로드 API

  3. 파일 갱신 히스토리 제공 API

  • 모든 API는 사용자 인증을 필요로 하며, HTTPS 프로토콜을 사용해야 함
    • SSL(Secure Socket Layer)는 클라이언트와 백엔드 서버가 주고받는 데이터를 보호하기 위해 사용

한 대 서버의 제약 극복

  • 용량이 부족한 경우 가장 먼저 떠오르는 해결책은 데이터를 샤딩하여 어러 서버에 나누어 저장하는 것

    IMG_2645.PNG

    • 서버에 장애가 생기면 데이터를 잃게 될 가능성이 높음
  • 넷플릭스나 에어비엔비는 아마존 S3(Simple Storage Service, 업계 최고 수준의 규모 확장성, 가용성, 보안, 성능을 제공하는 객체 저장소 서비스)를 사용

    IMG_2646.PNG

    • S3는 다중화를 지원. 같은 지역 혹은 여러 지역에 걸쳐 다중화 가능
    • AWS 서비스 지역(region)은 아마존 AWS가 데이터 센터를 운영하는 지리적 영역
    • 여러 지역에 걸쳐 다중화하면 데이터 손실을 막고 가용성을 최대한 보장할 수 있음
    • S3 버킷(bucket)은 파일 시스템 폴더와 비슷함
  • S3에 파일을 넣은 이후 개선할 부분을 좀 더 찾아보기

    IMG_2647.PNG

    • 로드밸런서: 네트워크 트래픽 분산 및 장애 발생 시 장애가 발생한 서버 우회
    • 웹 서버: 로드밸런서를 추가하면 더 많은 웹 서버를 추가하여 트래픽 폭증에도 쉽게 데응 가능
    • 메타데이터 데이터베이스: 데이터베이스를 파일 저장 서버에서 분리하여 SPOF(Single Point of Failure) 회피, 다중화 및 샤딩 정책을 적용하여 가용성과 규모 확장성 요구사항에 대응
    • 파일 저장소: S3를 파일 저장소로 사용하고, 가용성과 데이터 무손실을 보장하기 위해 두 개 이상의 지역에 데이터 다중화

동기화 충돌

  • 두 명 이상의 사용자가 같은 파일이나 폴더를 동시에 업데이트 하려고 하는 경우 먼저 처리되는 변경은 성공으로, 나중에 처리되는 변경은 충돌이 발생한 것으로 표시

    IMG_2648.PNG

    • 사용자 1의 파일 갱신 시도는 정상적으로 처리

    • 사용자 2에 대한 동기화 충돌 오류가 발생 시 시스템에는 같은 파일의 두 가지 버전이 존재하게 됨

      IMG_2648 2.PNG

      • 사용자 2가 가지고 있는 로컬 사본과 서버에 있는 최신 버전
      • 사용자는 두 파일을 하나로 합칠지 아니면 둘 중 하나를 다른 파일로 대체할지 결정해야 함

개략적 설계안

IMG_2649.PNG

  • 사용자 단말: 사용자가 이용하는 웹브라우저나 모바일 앱 등의 클라이언트
  • 블록 저장소 서버(block server), 블록 수준 저장소 서버(block-level-storage)
    • 파일 블록을 클라우드 저장소에 업로드하는 서버
    • 클라우드 환경에서 데이터 파일을 저장하는 기술
    • 파일을 여러개의 블록으로 나눠 저장하며, 각 블록에는 고유한 해시값이 할당됨.
      • 해시값은 메타데이터 데이터베이스에 저장됨
    • 각 블록은 독립적인 객체로 취급되며 클라우드 저장소 시스템에 보관됨
    • 파일을 재구성하려면 블록들을 원래 순서대로 합쳐야 함
    • 여기서는 한 블록을 4MB로 정함 (드롭박스 사례 참고)
  • 클라우드 저장소: 파일은 블록 단위로 나눠져 클라우드 저장소에 보관됨
  • 아카이빙 저장소: 오랫동안 사용되지 않은 비활성 데이터를 저장하기 위한 컴퓨터 시스템
  • 로드밸런서: 요청을 모든 API 서버에 고르게 분산하는 역할
  • API 서버: 파일 업로드 외에 거의 모든 것을 담당하는 서버
    • 사용자 인증, 사용자 프로파일 관리, 파일 메타데이터 갱신 등
  • 메타데이터 데이터베이스: 사용자, 파일, 블록, 버전 등의 메타디에터 정보를 관리
    • 실제 파일은 클라우드에 보관하며, 이 데이터베이스에는 오직 메타데이터만 저장
  • 메타데이터 캐시: 성능을 높이기 위해 자주 쓰이는 메타데이터를 캐시하기 위한 용도
  • 알림 서비스: 특정 이벤트가 발생했음을 클라이언트에게 알리는데 쓰이는 발생/구독 프로토콜 기반 시스템
    • 클라이언트에게 파일 추가/편집/삭제되었음을 알려 파일의 최신 상태를 확인하도록 함
  • 오프라인 사용자 백업 큐: 클라이언트가 접속 중이 아니어서 파일의 최신 상태를 확인할 수 없을 때 사용

3단계 상세 설계

블록 저장소 서버

  • 큰 파일들은 업데이트가 일어날 때마다 전체 파일을 서버로 보내면 네트워크 대역폭을 많이 잡아먹음

    • 델타 동기화: 파일이 수정되면 전체 파일 대신 수정이 일어난 블록만 동기화
    • 압축: 블록 단위로 압축해 두면 크기를 많이 줄일 수 있음
      • 압축 알고리즘은 파일 유형에 따라 정함
        • 텍스트 파일: gzip or bzip
        • 이미지, 비디오: 다른 압축 알고리즘 사용
  • 새 파일이 추가되었을 때

    IMG_2651.PNG

    • 주어진 파일들을 작은 블록들로 분할
    • 각 블록들을 압축
    • 클라우드 저장소로 보내기 전에 암호화
    • 클라우드 저장소로 저장
  • 델타 동기화 전략

    IMG_2652.PNG

    • 검정색으로 표시된 블록 2와 5는 수정된 블록
    • 갱신만 부분만 동기화해야하므로 이 두 블록만 클라우드 저장소에 업로드

높은 일관성 요구사항

  • 강한 일관성(strong consistency) 모델을 기본으로 지원해야 함
    • 같은 파일이 단말이나 사용자에 따라 다르게 보이는 것은 허용할 수 없음
    • 메타데이터 캐시와 데이터베이스 계층에도 같은 원칙이 적용되어야 함
    • 메모리 캐시는 보통 최종 일관성(eventual consistency) 모델을 지원
    • 강한 일관성 모델을 달성하기 위해 보장해야할 사항
      • 캐시에 보관된 사본과 데이터베이스에 있는 원본이 일치
      • 데이터베이스에 보관된 원본에 변경이 발생하면 캐시에 있는 사본을 무효화
  • 관계형 데이터베이스는 ACID(Atomicity, Consistency, Isolation, Durability)를 보장하므로 강한 일관성을 보장
  • NoSQL 데이터베이스는 이를 기본으로 지원하지 않으므로, 동기화 로직 안에 프로그램해 넣어야 함
  • 본 설계안에서는 ACID를 기본 지원하는 관계형 데이터베이스를 채택하여 높은 일관성 요구사항에 대응

메타데이터 데이터베이스

IMG_2653.PNG

(중요한 것만 간추린 아주 단순화된 형태의 스키마임에 유의)

  • user: 이름, 이메일, 프로파일 등 사용자에 관계된 기본적인 정보들
  • device: 단말 정보 보관. push_id는 모바일 푸시 알림을 보내고 받기 위한 것. 한 사용자는 여러대의 단말을 가질 수 있음
  • namespace: 사용자의 루트 디렉토리 정보가 보관됨
  • file: 파일의 최신 정보가 보관됨
  • file_version: 파일의 갱신 이력이 보관되는 테이블. 이 테이블에 보관되는 레코드는 갱신 이력이 훼손되는 것을 막기 위해 전부 읽기 전용
  • block: 파일 블록에 대한 정보를 보관하는 테이블. 특정 버전의 파일은 파일 블록을 올바른 순서로 조합하기만 하면 복원해 낼 수 있음

업로드 절차

IMG_2655.PNG

  • 두 개 요청이 병렬적으로 전송된 상황 (두 요청 모두 클라이언트 1이 전송)
    • 첫 번째 요청: 파일 메타데이터 추가
    • 두 번째 요청: 파일을 클라우드 저장소로 업로드하기 위한 것
  • 파일 메타데이터 추가
    1. 클라이언트 1이 새 파일의 메타데이터를 추가하기 위한 요청 전송
    2. 새 파일의 메타데이터를 데이터베이스에 저장하고 업로드 상태를 대기중으로 변경
    3. 새 파일이 추가되었음을 알림 서비스에 통지
    4. 알림 서비스는 관련된 클라이언트(클라이언트 2)에게 파일이 업로드되고 있음을 알림
  • 파일을 클라우드 저장소에 업로드
    1. 클라이언트 1이 파일을 블록 저장소 서버에 업로드
    2. 블록 저장소 서버는 파일을 블록 단위로 쪼갠 다음 압축하고 암호화한 다음에 클라우드 저장소에 전송
    3. 업로드가 끝나면 클라우드 스토리지는 완료 콜백을 호출, 이 콜백 호출은 API 서버로 전송됨
    4. 메타데이터 DB에 기록된 해당 파일의 상태를 완료로 변경
    5. 알림 서비스에 파일 업로드가 끝났음을 통지
    6. 알림 서비스는 관련된 클라이언트(클라이언트 2)에게 파일 업로드가 끝났음을 통지
  • 파일을 수정하는 경우에도 흐름은 비슷함

다운로드 절차

  • 파일 다운로드는 파일이 새로 추가되거나 편집되면 자동으로 시작됨
    • 클라이언트는 다른 클라이언트가 파일을 편집하거나 추가했다는 사실을 감지하는 방법
      1. 클라이언트 A가 접속 중이고 다른 클라이언트가 파일을 변경하면 알림 서비스가 클라이언트 A에게 변경이 발생했으니 새 버전을 끌어가야 한다고 알림
      2. 클라이언트 A가 접속 중이 아니면 데이터는 캐시에 보관되고, 해당 클라이언트가 접속 중으로 바뀌면 그때 해당 클라이언트는 새 버전을 가져감

IMG_2656.PNG

  • 파일이 변경되었음을 감지한 클라이언트의 상황
    • 우선 API 서버를 통해 메타데이터를 가져가야 함
    • 블록들을 다운받아 파일을 재구성해야 함
  • 파일 다운로드
    1. 알림 서비스가 클라이언트 2에게 누군가 파일을 변경했음을 알림
    2. 알림을 확인한 클라이언트 2는 새로운 메타데이터를 요청
    3. API 서버는 메타데이터 데이터베이스에게 새 메타데이터 요청
    4. API 서버에게 새 메타데이터가 반환됨
    5. 클라이언트 2 에게 새 메타데이터가 반환됨
    6. 클라이언트 2 는 새 메타데이터를 받는 즉시 블록 다운로드 요청 전송
    7. 블록 저장소 서버는 클라우드 저장소에서 블록 다운로드
    8. 클라우드 저장소는 블록 서버에 요청된 블록 반환
    9. 블록 저장소 서버는 클라이언트에게 요청된 블록 반환. 클라이언트 2는 전송된 블록을 사용하여 파일 재구성

알림 서비스

  • 알림 서비스의 목적은 파일의 일관성을 유지하기 위해 존재
    • 클라이언트는 로컬에서 파일이 수정되었음을 감지하는 순간 다른 클라이언트에게 그 사실을 알려서 충돌 가능성을 줄임
  • 알림 서비스는 단순히 이벤트 데이터를 클라이언트들로 보내는 서비스
  • 알림 서비스 방식
    • 롱 폴링: 드롭박스가 채택한 방식 ✅

      IMG_2564.PNG

      • 각 클라이언트는 알림 서버와 롱 폴링용 연결을 유지하다가 특정 파일에 대한 변경을 감지하면 연결을 끊음
      • 클라이언트는 반드시 메타데이터 서버와 연결해 파일의 최신 내역을 다운로드 해야 함
      • 해당 다운로드 작업이 끝났거나 연결 타임아웃 시간에 도달한 경우 새 요청을 보내어 롱 폴링 연결을 복원하고 유지해야 함
    • 웹 소켓: 클라이언트와 서버 사이에 지속적인 통신 채널을 제공하여 양방향 통신이 가능

      IMG_2565.PNG

      • 채팅서비스와 달리 본 시스템은 알림 서비스와 양방향 통신이 필요하지 않음
      • 서버는 파일이 변경된 사실을 클라이언트에게 알려줘야 하지만 반대 방향의 통신이 요구되지 않음
      • 웹 소켓은 실시간 양방향 통신이 요구되는 채팅 같은 시스템에 적합함
      • 알림을 보낼 일이 그렇게 자주 발생하지 않음
      • 알림을 보내야 할 경우에도 단시간에 많은 양의 데이터를 보낼 일이 없음

저장소 공간 절약

  • 파일 갱신 이력을 보존하고 안정성을 보장하기 위해서는 파일의 여러 버전을 여러 데이터센터에 보관해야 함
  • 모든 버전을 자주 백업하게 되면 저장용량이 너무 빨리 소진됨
  • 저장소 공간을 절약하기 위한 세 가지 방법
    • 중복 제거(de-dupe)
      • 중복된 파일 블록을 계정 차원에서 제거하는 방법
      • 두 블록이 같은 블록인지는 해시 값을 비교하여 판단
    • 지능적 백업 전략 도입
      • 한도 설정: 보관해야 하는 파일 버전 개수에 상한을 둠. 상한에 도달하면 오래된 버전은 버림
      • 중요한 버전만 보관: 중요한 것만 골라내서 보관
    • 아카이빙 저장소
      • 자주 쓰이지 않는 데이터를 저장하는 저장소
      • 몇달 혹은 수년간 이용되지 않은 데이터가 이에 해당
      • 아마존 S3 글래시어(glacier) 같은 아카이빙 저장소 이용료는 S3보다 훨씬 저렴

장애 처리

  • 로드밸런서 장애
    • 로드밸런서에 장애가 발생하면 secondary 로드밸런서가 활성화되어 트래픽을 이어받아야 함
    • 로드밸런서끼리는 보통 heartbeat 신호를 주기적으로 보내서 상태를 모니터링함
    • 일정 시간동안 박동 신호에 응답하지 않은 로드밸런서는 장애가 발생한 것으로 간주함
  • 블록 저장소 서버 장애
    • 블록 저장소 서버에 장애가 발생한 경우 다른 서버가 미완료 상태 또는 대기 상태인 작업을 이어받아야 함
  • 클라우드 저장소 장애
    • S3 버킷은 여러 지역에 다중화할 수 있으므로, 한 지역에서 장애가 발생하였다면 다른 지역에서 파일을 가져옴
  • API 서버 장애
    • API 서버들은 무상태 서버
    • 로드밸런서는 API 서버에 장애가 발생하면 트래픽을 해당 서버로 보내지 않음으로써 장애 서버를 격리
  • 메타데이터 캐시 장애
    • 메타데이터 캐시 서버도 다중화가 필요
    • 한 노드에 장애가 생겨도 다른 노드에서 데이터를 가져올 수 있음
    • 장애가 발생한 서버는 새 서버로 교체
  • 메타데이터 데이터베이스 장애
    • 주 데이터베이스 서버 장애: 부 데이터베이스 서버 가운데 하나를 주 데이터베이스 서버로 바꾸고, 부 데이터베이스 서버를 새로 하나 추가함
    • 부 데이터베이스 서버 장애: 다른 부 데이터베이스 서버가 읽기 연산을 처리하도록 하고 장애 서버는 교체
  • 알림 서비스 장애
    • 접속 중인 모든 사용자는 알림 서버와 롱 폴링 연결을 하나씩 유지
    • 알림 서비스는 많은 사용자와의 연결을 유지하고 관리해야 함
    • 드롭박스에서는 알림 서비스 서버 한가 관리하는 연결의 수는 1백만 개가 넘음
    • 한 대 서버에 장애가 발생하면 백만명 이상의 사용자가 롱 폴링 연결을 다시 만들어야 함
    • 한 대 서버로 백만 개 이상의 접속 유지는 가능하지만, 동시에 백만 개 접속을 시작하는 것은 불가능
    • 롱 폴링 연결을 복구하는 것은 상대적으로 느릴 수 있음
  • 오프라인 사용자 백업 큐 장애
    • 큐 역시 다중화
    • 큐에 장애가 발생하면 구독 중인 클라이언트들은 백업 큐로 구독 관계를 재설정해야 함

4단계 마무리

  • 구글 드라이브 시스템 설계
    • 높은 수준의 일관성
    • 낮은 네트워크 지연
    • 빠른 동기화 요구
  • 크게 두 가지 부분으로 분리
    • 파일의 메타데이터를 관리
    • 파일 동기화를 처리하는 부분
  • 알림 서비스는 롱 폴링을 사용하여 클라이언트로 하여금 파일의 상태를 최신으로 유지하도록 함
  • 정답은 없음, 회사마다 요구하는 제약조건에 따라 설계를 진행해야 함
  • 설계하는 과정에서 내린 결정과 선택한 기술들에 어떤 생각이 있었는지 잘 설명하는 것이 좋음
  • 시간이 남는다면 다른 선택지가 있었는지 논의해 보기
    • 블록 저장소 서버를 거치지 않고 파일을 클라우드 저장소에 직접 업로드 한다면?
      • 장점: 파일 전송을 클라우드 저장소로 직접 하므로 업로드 시간이 빨라짐
      • 단점
        • 분할, 압축, 암호화 로직을 클라이언트에 두어야 하므로 플랫폼별로 따로 구현해야 함 (이 설계안에서는 이 모두를 블록 저장소 서버라는 곳에 모아 둠)
        • 클라이언트가 해킹 당할 수 있으므로 암호화 로직을 클라이언트 안에 두는 것은 적절하지 않음
    • 접속 상태를 관리하는 로직을 별도 서비스로 옮기기
      • 관련 로직을 알림 서비스에서 분리해 내면 다른 서비스에서도 쉽게 활용할 수 있음