구글 드라이브는 파일 저장 및 동기화 서비스로, 문서, 사진, 비디오, 기타 파일을 클라우드에 보관할 수 있다.
해당 파일은 어떤 단말에서도 이용 가능하고 공유도 쉽게한다.
1단계 설계 범위 확정
기능 설계
- 파일 추가
-> 가장 쉬운 방법은 파일을 구글 드라이브 안으로 떨구는 것 - 파일 다운로드
- 여러 단말에 파일 동기화.
-> 하나의 단말에서 파일을 추가하면 다른 단말에도 자동으로 동기화 - 파일 갱신 이력 조회
- 파일 공유
- 파일이 편집된거나 삭제되거나 새롭게 공유되었을 때 알림 표시
논의 대상 제외
- 구글 문서 편집 및 협업 기능
비기능 설계
- 안전성: 저장소 시스템에서 안전성이 가장 중요
- 빠른 동기화 속도: 파일 동기화에 오랜 시간이 걸리면 사용자는 인내심을 잃는다.
- 네트워크 대역폭: 네트워크 대역폭을 불필요하게 많이 소모
- 규모 확작성: 많은 양의 트래픽도 처리 가능
- 높은 가용성: 일부 장애가 발생해도 계속 사용 가능해야한다.
개략적 추정치
- 가입자는 오천만 명, 천만 명의 DAU 사용자
- 모든 사용자아게 10GB 무료 저장소 할당
- 매일 사용자는 평균 2개의 파일을 업로드, 파일 크기는 평균 500KB
- 필요한 저장 공간 = 5천만 * 10 GB = 500 페타바이트
- 업로드 API QPS = 1천만 사용자 * 2회 업로드 / 24시간 / 3600초 = 약 240
- 최대 240 * 2 = 480 QPS
2단계 개략적 설계안
한 대 서버에서 출발해서 점진적으로 천만 사용자 지원이 가능한 시스템으로 발전시켜 나가자.
서버 한 대 상황
- 파일을 올리고 다운로드 하는 과정을 처리할 웹 서버
- 사용자 데이터, 로그인 정보, 파일 정보 등의 메타 데이터를 보관할 데이터베이스
- 파일을 저장할 저장소 시스템.
데이터베이스에 업로드 되는 드라이브 파일을 저장할 drive 라는 디렉토리를 준비.
디렉토리 안에는 네임스페이스라 불리는 하위 디렉토리를 가진다.
파일은 원본 파일과 같이한다.
각 파일과 폴더는 상대 결로를 네임스페이스 이름과 결합하여 유일하게 식별
API
파일 업로드 API, 다운로드 API 그리고 파일 갱신 히스토리 제공 API 가 필요.
파일 업로드 API
두 가지 종류의 업로드를 지원
- 단순 업로드: 파일 크기가 작을 때 사용
- 이어 올리기: 파일 사이즈가 크고 네트워크 문제로 업로드가 중단될 가능성이 높다고 생각되면 사용
이어 올리기 API 예시
https://api.example.com/files/upload?uploadType=resumable
인자
- uploadType=resumable
- data: 업로드할 로컬 파일
세 단계 절차로 이루어진다.
- 이어 올리기 URL을 받기 위한 최초 요청 전송
- 데이터를 업로드하고 업로드 상태 모니터링
- 업로드에 장애가 발생하면 장애 발생시점부터 업로드를 재시작
파일 다운로드 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 프로토콜을 지원하는 것은 데이터를 보호하기 위함.
한대 서버의 제약 극복
파일 시스템이 가득차면 가장 먼저 샤딩하여 여러 서버에 나누어 저장하는 것이다.
아마존 S3를 도입하여 파일을 저장하기로 한다.
AWS 서비스 지역은 아마존 AWS가 데이터 센터를 운영하는 지리적 영역이다.
데이터를 여러 지역에 걸쳐 다중화하여 데이터 손실을 막고 가용성을 최대한 보장할 수 있다.
추가적으로 확인할 부분
- 로드밸런서
- 웹 서버
- 메타데이터 데이터베이스
- 파일 저장소
동기화 충돌
대형 저장소 시스템의 경우 돌기화 충돌이 발생할 수 있다.
충돌 해소 방법은?
먼저 처리되는 변경은 성공한 것으로 보고, 나중에 처리되는 변경은 충돌이 발생한 것으로 표시한다.
개략적 설계안
- 사용자 단말: 사용자가 이용하는 웹브라우저나 모바일 앱 등의 클라이언트
- 블록 저장소 서버
- 파일 블록을 클라우드 저장소에 업로드하는 서버
- 파일을 여러 개의 블록으로 나눠 저장, 각 블록은 고유한 해시 값을 할당된다.
- 해시 값은 메타데이터 데이터베이스에 저장
- 각 블록은 독립적인 객체로 취급되며 클라우드 저장소 시스템(본 설계는 S3)에 보관된다.
- 클라우드 저장소: 파일을 블록 단위로 나눠서 클라우드 저장소에 보관
- 아카이빙 저장소: 오래동안 사용되지 않은 비활성 데이터를 저장하기 위한 시스템
- 로드밸런서: 요청을 모든 API 서버에 고르게 분산
- API 서버: 파일 업로드 외에 거의 모든 것을 담당
- 메타데이터 데이터베이스:실제 파일은 클라우드에 보관하며, 데이터베이스에는 오직 메타 데이터만 저장
- 메타데이터 캐시: 성능을 높이기 자주 사용
- 알림 서비스: 특정 이벤트가 발생했을 때, 클라이언트에게 알리는데 사용되는 발생/구독 프로토콜 기반 시스템
- 오프라인 사용자 백업 큐: 클라이언트가 접속 중이 아니라서 파일의 최신 상태를 확인할 수 없을 때, 해당 정보를 큐에 두어 나중에 클라이언트가 접속 했을 때 동기화될 수 있게 한다.
3단계 상세 설계
블록 저장소 설계
정기적으로 갱신되는 큰 파일들은 업데이트가 일어날 때마다 전체 파일을 서버로 보내면 네트워크 대역폭을 많이 잡아먹게 된다.
최적화 방법
- 델타 동기화: 파일이 수정되면 전체 파일 대신 수정이 일어난 블록만 동기화
- 압축: 블록 단위로 압축해 두면 데이터 크기를 많이 줄일 수 있다.
해당 컴포넌트는 파일 업로드에 관계된 힘든 일을 처리하는 곳이다.
- 클라이언트가 보낸 파일을 블록 단위로 나눠야 하고, 각 블록에 압축 알고리즘을 적용하고, 암호화까지 한다.
- 전체 파일을 저장소 시스템으로 보내는 대신 수정된 블록만 전송해야한다.
아래는 새 파일이 추가되었을 때 블록 저장소 서버가 동작하는 그림.
델타 동기화 전략
- 검은색(수정된) 블록만 동기화(업로드)하면 된다.
높은 일관성 요구사항
해당 시스템은 강한 일관성 모델을 기본으로 해야한다.
메모리 캐시는 보통 결과적 일관성 모델을 지원해야한다.
- 캐시에 보관된 사본과 데이터베이스에 있는 원본이 일치한다.
- 데이터베이스에 보관된 원본에 변경일 발생하면 캐시에 있는 사본을 무효화한다.
관계형 데이터베이스는 ACID를 보장하므로 강한 일관성을 보장하기 쉽다.
NoSQL 데이터베이스는 지원하지 않으므로, 동기화 로직 안에 프로그램을 넣어야 한다.
메타데이터 데이터베이스
아래 그림은 데이터베이스의 스키마 설계안
user: 이름, 이메일, 프로파일 사진 등 사용자에 관계된 기본 정보
device: 단말 정보가 보관. push_id는 모바일 푸시 알림을 보내고 받기 위한 것. 한 사용자가 여러 대의 단말을 가질 수 있다.
namespace: 사용자의 루트 디렉토리 정보가 보관
file: 파일의 최신 정보가 보관
file_verison: 파일의 갱신 이력이 보관되는 테이블, 전부 읽기 전용 레코드, 갱신 이력이 훼손되는 것을 막는다.
block: 파일 블록에 대한 정보를 보관
업로드 절차
사용자가 파일을 업로드하면 어떤 일이 벌어지는지 시퀀스 다이어그램으로 설명
두 개의 병렬 요청이 발생
(두 요청 모두 클라이언트 1이 보내는 것)
- 파일 메타데이터를 추가하는 요청
- 파일을 클라우드 저장소로 업로드하는 용청
다운로드 절차
파일 다운로드는 파일이 새로 추가되거나 편집되면 자동으로 시작
클라이언트는 다른 클라이언트가 파일을 편집하거나 추가했다는 사실을 어떻게 알까?
- 클라이언트 A 가 접속 중이고 다른 클라이언트가 파일을 변경하면 알림 서비스가 클라이언트 A에게 변경이 발생했으니 버전을 끌어가야 한다고 알린다.
- 클라이언트 A 가 네트워크에 연결된 상태가 아닐 경우에는 데이터는 캐시에 보관될 것이다. 해당 클라이언트의 상태가 접속 중으로 바뀌면 그 때 새 버전을 가져간다.
알림 서비스
알림 서비스 목적은 파일의 일관성을 유지하기 위해, 클라이언트는 로컬에서 파일이 수정되었음을 감지하는 순간 다른 클라이언트에 그 사실을 알려 충돌 가능성을 줄여야 한다.
- 롱 풀링: 드롭박스가 해당 방식을 채택
- 웹소켓: 클라이언트와 서버 사이에 지속적인 통신 채널을 제공. 양방향 가능
본 설계에서는 롱 풀링을 채택
- 양방향 통신이 필요 없다. 반대 방향의 통신은 요구되지 않는다.
각 클라이언트는 알림 서버와 롱 풀리용 연결을 유지하다가 특정 파일에 대한 변경을 감지하면 해당 연결을 끊는다.
이 때 클라이언트는 반드시 메타데이터 서버와 연결해 파일의 최신 내역을 다운로드 해야 한다.
저장소 공간 절약
파일 갱신 이력을 보존하고 안정성을 보장하기 위해서는 파일의 여러 버전을 여러 데이터센터에 보관할 필요가 있다.
모든 버전을 자주 백업하면 저장용량이 너무 빨리 소진된다.
비용 절감을 하는 세 가지 방법
- 중복 제거: 중복된 파일 블록을 계정 차원에서 제거하는 방법. 해시 값을 비교하여 판단
- 지능적 백업 전략
- 한도 설정: 보관해야 하는 파일 버전 개수에 상한을 두는 것. 제일 오래된 버전을 버린다.
- 중요한 버전만 보관: 불필요한 버전과 사본이 만들어지는 것을 피하려면 그 가운데 중요한 것만 고른다.
- 자주 쓰이지 않는 데이터는 아카이빙 저장소로 옮긴다.
장애 처리
- 로드밸런서 장애: 장애가 발생하면 부 로드밸런서가 활성화되어 트래픽을 이어 받는다. 로드밸런서끼리는 박동 신호를 주기적으로 보내서 상태를 모니터링한다.
- 블록 저장소 서버 장애: 다른 서버가 미완료 상태 또는 대기 상태인 작업을 이어받아야 한다.
- API 서버 장애: 무상태 서버이다. 로드밸런서는 장애가 발생하면 트래픽을 해당 서버로 보내지 않는다.
- 메타데이터 캐시 장애: 캐시 서버 다중화. 다른 노드에서 데이터를 가져오거나 새 서버로 교체
- 메타데이터 데이터베이스 장애
- 주 데이터베이스 서버 장애: 부 데이터베이스 서버 가운데 하나를 주 서버로 변경
- 부 데이터베이스 서버 장애: 다른 부 데이터베이스 서바가 읽기 연산을 처리하고 장애 서버는 새 것으로 처리
- 알림 서비스 장애: 한 대 서버로 백만 개 이상의 접속을 유지할 수 있지만, 동시에 백만 개 접속을 시작하는 것은 불가능하다.
- 오프로아니 사용자 백업 큐 장애: 큐 다중화 작업. 장애가 발생하면 구독 중인 클라이언트들은 백업 큐로 구독 관계를 재설정해야한다.
4단계 마무리
설계안은 크게 두 가지 부분으로 나뉜다.
- 파일의 메타데이터를 관리하는 부분
- 파일 동기화를 처리하는 부분
알림 서비스는 두 부분과 병존하는 중요 컴포넌트이다.
고려 사항
- 블록 서버 저장 서버를 거치지 않고 클라우드 저장소에 직접 업로드하면?
- 분할, 압축, 암호화 로직을 클라이언트에 두어야 하므로 플랫폼별로 따로 구현해야한다.
(기존 설계안은 블록 저장소 서버라는 곳에 모아뒀다.) - 암호화 로직을 클라이언트 안에 두는 것은 적절하지 않다.
- 분할, 압축, 암호화 로직을 클라이언트에 두어야 하므로 플랫폼별로 따로 구현해야한다.
- 접속상태를 관리하는 로직을 별도 서비스로 옮기는 것
- 관련 로직을 안내 서비스에서 분리해 내면, 다른 서비스에서도 쉽게 활용할 수 있다.
'읽은 책 > [책] 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1권' 카테고리의 다른 글
14장.유튜브 설계 (4) | 2024.07.21 |
---|---|
13장. 검색어 자동완성 시스템 (0) | 2024.07.20 |
12장. 채팅 시스템 설계 (0) | 2024.07.20 |
11장. 뉴스 피드 시스템 설계 (1) | 2024.07.16 |
10장. 알림 시스템 설계 (0) | 2024.07.15 |