가상 면접 사례로 배우는 대규모 시스템 설계 기초 Study [15장] 구글 드라이브 설계
Contents
Note
팀 내에서 진행하는 Study 정리 입니다.
함께 논의하고 싶은 주제
- 같이 스터디를 함으로써 책을 빠른시간내에 읽고 정리해볼 수 있어서 좋았습니다. ( 그 과정은 조금 힘들지만 그래도 다 읽어서 좋습니다!)
- 저장소 공간을 절약할 때 자주 바뀌는 파일로 인해 버전 이력이 많이 생긴다면 중요한 버전만 어떻게 골라낼 수 있을까?
1. 문제 이해 및 설계 범위 확정
1.1 기능적 요구사항
- 파일추가. 가장 쉬운 방법은 drag-and-drop
- 파일 다운로드
- 여러 단말에 파일 동기화
- 파일 갱신 이력 조회(revision history)
- 파일 공유
- 파일이 편집되거나 삭제되거나 새롭게 공유되었을 때 알림푯;
- 제외기능 : 구글문서 편집 및 협업 기능
1.2 비 기능적 요구사항
- 데이터에 대한 안정성
- 빠른 동기화 속도
- 네트워크 대역폭
- 규모 확장성
- 높은 가용성
1.3 개략적 추정치
- 가입자는 오천만 명. DAU 천만명이라고 가정
- 모든 사용자에게 10GB 제공
- 매일 각 사용자가 평균 2개의 파일을 업로드. 각 파일 크기는 500KB
- 읽기:쓰기 비율 1:1
- 필요한 저장공간 총량 = 5천만 사용자 * 10GB = 500PB
- 업로드 API QPS = 천만 사용자 * 2회 업로드 / 24시간 / 3600초 = 약 240
- 최대 QPS = QPS * 2 = 480
2. 개략적 설계안 제시 및 동의 구하기
서버 한 대로 구성해보기
- 파일 업로드 및 다운로드를 위한 웹서버
- 사용자, 로그인 정보, 파일 정보 등을 관리하는 데이터베이스
- 파일을 저장할 저장소
Note
[예시]
- 아파치 웹 서버, MySQL 데이터베이스를 설치하고 파일저장소인
drive/
디렉터리를 생성 dirve/
디렉터리 안에는 네임스페이스라 불리는 하위 디렉터리를 구성- 각 네임스페이스에는 특정 사용자가 올린 파일을 보관
- 각 파일과 폴더는 상대 경로를 네임스페이스 이름과 결합하여 유일하게 식별
2.1 API
- 클라이언트와 백엔드 서버가 주고받는 데이터를 보호하기 위해 SSL(Secure Socket Layer)를 지원하는 HTTPS 프로토콜을 사용
파일 업로드 API
- 단순 업로드 : 파일 크기가 작을 때 사용
- 이어 올리기(resumable upload) : 파일 사이즈가 크고 네트워크 문제로 업로드가 중단될 가능성이 높은 경우
이어올리기 API 예시
https://api.example.com/files/upload?uploadType=resumable
인자
uploadType=resumable
- data : 업로드할 로컬 파일
- 이어 올리기 URL을 받기 위한 최초 요청 전송
- 데이터를 업로드하고 업로드 상태 모니터링
- 업로드 장애 발생시 장애 발생시점부터 업로드 재시작
파일 다운로드 API
다운로드 API 예시
https://api.example.com/files/download
인자
- path : 다운로드 할 파일의 경로
|
|
파일 갱신 히스토리 API
파일 갱신 히스토리 API 예시
인자
- path: 갱신 히스토리를 가져올 파일의 경로
- limit: 히스토리 길이의 최대치
|
|
2.2 한 대 서버의 제약 극복
- 파일 시스템이 가득차는 경우 데이터를 샤딩하여 여러 서버에 나누어 저장
- 아마존 S3와 같은 객체 저장소 서비스를 활용
- 데이터의 다중화는 같은 지역 혹은 여러 지역 안에 걸쳐 다중화가 가능
- 여러 지역 안에 걸쳐 다중화하면 데이터 손실을 막고 가용성을 최대한 보장
더 개선할 부분 찾기
- 로드밸런서: 로드밸런서는 트래픽을 고르게 분산. 웹서버 장애시 다른 서버로 우회
- 웹 서버: 로드밸런서를 추가하면 웹 서버를 손쉽게 추가 가능
- 메타데이터 데이터베이스: 데이터베이스를 파일 저장 서버에 분리 SPOF(Single Point Of Failure)를 회피. 다중화, 샤딩 정책적용으로 가용성 규모확장성 대응
- 파일 저장소 : 가용성과 데이터 무손실을 보장하기 위해 두 개 이상에 지역에 데이터 다중화
2.3 동기화 충돌
- 두 명 이상의 사용자가 같은 파일이나 폴더를 동시에 업데이트 하는 경우
- 먼저 처리된 변경을 성공한 것으로 보고 나중에 처리되는 변경은 충돌이 발생한 것으로 표기
- 오류가 발생한 시점에는 같은 파일의 두 가지 버전이 존재
2.4 개략적 설계안
- 사용자 단말 : 사용자가 이용하는 웹브라우저나 모바일 앱 등 클라이언트
- 블록 저장소 서버(block server)
- 블록 수준 저장소(blcok-level storage)
- 파일을 여러개의 블록으로 나누어 저장하고 각 블록에는 고유한 해시값을 할당
- 해시값은 메타데이터 데이터베이스에 저장
- 각 블록은 독립적인 개체이고 클라우드 저장소 시스템에 보관
- 재 구성을 위해 블록들을 원래 순서대로 병합
- 클라우드 저장소 : 블록단위로 나누어진 파일은 저장소에 보관
- 아카이빙 저장소(cold storage) : 오랫동안 사용하지 않은 비활성 데이터를 저장
- 로드밸런서 : 요청을 모든 API 서버에 고르게 분산
- API 서버 : 파일 업로드 외 모든 것을 담당.
- 메타데이터 데이터베이스 : 사용자, 파일, 블록, 버전 등 메타데이터 정보 관리. 실제 파일은 클라우드에 보관되며 메타데이터만 관리
- 메타데이터 캐시 : 자주쓰이는 메타데이터는 캐시
- 알림 서비스 : 클라이언트에게 특정 이벤트 알림.생성/구독 프로토콜 기반 시스템 사용
- 오프라인 사용자 백업 큐(offline backup queue) : 클라이언트가 접속중이 아닐 경우 해당 정보를 큐에 두어 나중에 클라이언트가 접속했을 때 동기화
3. 상세 설계
3.1 블록 저장소 서버
큰 파일 업데이트 최적화
- 델타 동기화(delta sync) : 파일이 수정되면 전체 파일 대신 수정이 일어난 블록만 동기화
- 압축(compression) : 블록 단위의 압축은 데이터 크기를 많이 줄일 수 있음. 파일 유형에 따라 압축알고리즘 지정
블록 저장소 동작 방식
- 주어진 파일을 작은 블록으로 분할
- 각 블록 압축
- 클라우드 저장소로 보내기 전 암호화
- 클라우드 저장소로 전송
블록저장소 서버에 델타 동기화 전략과 알고리즘을 도입하였으므로 네트워크 대역폭 사용량을 절감
3.2 높은 일관성 요구사항
- 강한 일관성(strong consistency) 모델을 지원해야함
- 메모리 캐시는 결과적 일관성(eventual consistency) 모델을 지원
- 관계형 데이터베이스는 ACID(Atomicity, Consistency, Isolation, Durability)를 보장하므로 강한 일관성을 보장하기 쉬움
- NoSQL은 기본적으로 지원하지 않으므로 동기화 로직 안에 프로그램하여 넣어야 함
캐시의 강한 일관성 달성
- 캐시에 보관된 사본과 데이터베이스의 원본이 일치
- 데이터베이스 보관된 원본에 변경이 발생하면 캐시에 있는 사본을 무효
3.3 메타데이터 데이터베이스
- user : 이름, 이메일, 프로파일 사진 등 사용자와 관계된 정보 보관
- device : 단말 정보 저장. 한 사용가 여러 대의 단말을 가질 수 있다.
- namespace : 사용자의 루트 디렉터리 정보 보관
- file : 파일이 최신 정보 보관
- file_version : 파일의 갱신 이력 보관. 갱신 이력의 훼손을 막기 위해 읽기 전용
- block : 파일 블록에 대한 정보를 보관. 특정 버전의 파일은 파일 블록을 올바른 순서로 조합하면 복원가능
3.4 업로드 절차
- 파일 메타데이터 추가, 파일 클라우드 저장소 업로드 두 가지 요청이 병렬적으로 실행
파일 메타데이터 추가
- 클라이언트 1이 새 파일의 메타데이터를 추가하기 위해 요청 전송
- 새 파일의 메타데이터를 데이터베이스에 저장하고 업로드 상태를 pending으로 변경
- 새 파일 추가 알림 서비스 통지
- 알림 서비스는 관련 클라이언트에게 파일 업로드 알림
파일을 클라우드 저장소에 업로드
- 클라이언트 1이 파일을 블록 저장소 서버에 업로드
- 블록 저장소 서버는 파일을 블록 단위로 분할한 다음 압축하고 암호화 한 다음에 클라우드 저장소에 전송
- 업로드 완료 후 클라우드 스토리지는 완료 콜백 호출.
- 메타데이터 데이터베이스에 기록된 해당 파일의 상태를 uploaded로 변경
- 알림 서비스에게 파일 업로드 완료 통지
- 알림 서비스는 관련 클라이언트에게 파일 업로드 완료를 알림
3.5 다운로드 절차
다른 클라이언트의 변경 감지
- 클라이언트 A가 접속 중이고 다른 클라이언트가 파일 변경 : 알림서비스가 A에게 변경 알림. 새 버전 알림
- 클라이언트 A가 접속 중이 아닐 때 다른 클라이언트가 파일 변경 : 데이터는 캐시에 보관. A가 접속 중으로 변경시 새 버전을 가져감
- 알림 서비스가 클라이언트 2에게 누군가 파일 변경을 알림
- 클라이언트 2는 새로운 메타데이터 요청
- API 서버는 메타데이터 데이터베이스에게 새 메타데이터 요청
- API 서버 새 메타데이터 반환
- 클라이언트2 는 새 메타데이터 수신
- 클라이언트2는 새 메타데이터를 받는 즉시 블록 다운로드 요청 전송
- 블록 저장소 서버는 클라우드 저장소에 블록 다운로드
- 클라우드 저장소는 블록 서버에 요청된 블록 반환
- 블록 저장소 서버는 클라이언트에게 요청된 블록 반환. 클라이언트 2는 전송된 블록을 사용하여 파일 재구성
3.6 알림 서비스
- 롱 폴링(long polling)
- 웹 소켓(Websocket) : 클라이언트와 서버 사이 지속적인 통신 채널을 제공. 양방향 통신 가능
롱 폴링 사용 이유
- 채팅 서비스와 달리 본 시스템은 서버는 파일이 변경된 사실을 클라이언트에게 알려주어야 하지만 그 반대는 요구되지 않음
- 웹소켓은 양방향 통신이 요구되는 곳에 적합. 알림을 보낼일은 자주 발생하지 않고 단시간에 많은 양의 데이터를 보낼일은 없음
3.7 저장소 공간 절약
- 중복제거(de-dupe): 중복된 파일을 블록을 계정 차원에서 제거. 두 블록의 해시값 비교
- 지능적 백업 전략 도입
- 한도 설정 : 보관해야 하는 파일의 개수 상한. 상한에 도달하면 제일 오래된 버전 파기
- 중요한 버전만 보관 : 자주 파일이 변경되는 경우 중요한 이력만 보존해야 한다.
- 자주 쓰이지 않는 데이터는 아카이빙(cold storage)로 이동. 몇 달 혹은 수년간 이용되지 않는 데이터
3.8 장애 처리
- 로드밸런서 장애
- 로드밸런서 장애시 부 로드밸런서가 활성화
- 로드밸런서 끼리 박동(heartbeat) 신호를 주기적으로 보내 상태 모니터링
- 일정 시간 동안 박동 신호에 응답하지 않는 로드 밸런서는 장애 간주
- 블록 저장소 서버 장애: 다른 서버가 미완료 상태 또는 대기 상태인 작업을 이어받음
- 클라우드 저장소 장애: S3 버킷은 여러 지역에 다중화. 다른지역에 파일을 가져옴
- API 서버 장애 : 장애 서버에 트래픽을 보내지 않고 격리
- 메타데이터 캐시 장애 : 메타데이터 개시 서버 다중화
- 메타데이터 데이터베이스 장애
- 주 데이터베이스 서버 장애 : 부 데이터베이스 서버 하나를 주 데이터베이스로 변경. 부 데이터베이스 추가
- 부 데이터베이스 서버 장애 : 다른 부 데이터베이스 서버가 읽기 연산 처리. 장애서버는 교체
- 알림 서비스 장애
- 접속 중인 모든 사용자는 알림 서버와 롱 폴링 연결
- 서버 한대가 장애 발생시 수백만 개의 롱 폴링을 재연결. 동시에 여러개의 연결을 하는 것은 불가능
- 오프라인 사용자 백업 큐 장애: 큐 다중화. 큐 장애시 구독 클라이언트들도 백업 큐 구독 관계를 재설정
4. 마무리
- 블록 저장소를 거치지 않고 파일을 클라우드 저장소에 직접 업로드 한다면?
- 분할, 압축, 암호화 로직을 클라이언트에 두어야 하므로 플랫폼별로 따로 구현 필요
- 클라이언트가 해킹 당할 가능성이 있음 암호화 로직을 클라이언트 안에 두는 것을 적절하지 않음