효율적인 동영상 업로드를 위한 여정 - 대규모 시스템 설계 기초 14장

Broccolism·2022년 11월 20일
11
post-thumbnail

가상 면접 사례로 배우는 대규모 시스템 설계 기초

2022년 조사 결과 유투브에서 매일 재생되는 비디오 수는 10억을 넘는다고 한다. (https://www.omnicoreagency.com/youtube-statistics/)

14장 유튜브 설계

말이 유튜브 설계지, 사실 영상 업로드 시스템 설계에 가까웠다.. (추천, 라이브 스트리밍에 대한 내용은 나와있지 않다.)

서론

어떤 점에 신경써야 할까?

유튜브 같은 비디오 스트리밍 서비스와 다른 서비스의 가장 큰 차이점은 다루는 데이터의 크기 차이일 것이다. 단순 텍스트나 이미지 몇장이 아닌 비디오를 다룬다. 그래서 특별히 고려해야 할 점이 몇가지 있다.

  • 비디오는 일반적인 데이터와 다르게 다뤄야 한다.
    • 여기서 말하는 일반적인 데이터는 회원 프로필 정보 등 작은 용량의 데이터를 말한다.
  • 비디오 업로드와 스트리밍 속도가 빨라야한다.
  • 재생 품질을 선택할 수 있어야 한다.
  • 인프라 비용을 통제할 수 있어야 한다.
    • 비디오 한 편의 용량은 기가바이트 단위로 올라갈 수 있기 때문에 너무 많은 데이터 용량을 차지하면 인프라 비용도 올라갈 것이다.

관련 개념: CDN, 인코딩, GOP

  • CDN, Content Delivery Network 은 일종의 캐시 시스템이다. 목적은 콘텐츠를 사용자에게 빠르게 전달하는 것이다. 이를 위해 사용자와 물리적으로 가까운 곳에 서버를 설치하여 원본의 복사본을 저장해둔다.
  • 비디오 인코딩 video encoding 은 video transcoding이라고도 부른다. 비디오의 포멧을 변환하는 과정을 말한다. 원본 비디오의 품질을 손상시키지 않고 압축하여 빠르게 전송하기 위한 필수 단계다.
    • 인코딩 포맷은 아주 다양하지만 대부분 다음 2가지로 구성된다.
      • 컨테이너 container: 비디오 파일 + 오디오 + 메타데이터를 묶은 것. 우리가 아는 .avi, .mov, .mp4 파일 확장자를 통해 컨테이너 포맷을 알 수 있다.
      • 코덱 codec: 비디오 화질을 보존하면서 파일 크기를 줄이기 위한 압축/해제 알고리즘이다. 가끔 곰플레이어에서 ‘코덱을 찾을 수 없음’이라는 오류가 뜨는데, 이 알고리즘을 인식하지 못해 생긴 오류다. (보통 그 코덱을 찾아서 설치하면 오류가 해결되었다.)
  • GOP, Group of Pictures 는 인코딩 된 비디오의 특정 프레임 몇장을 묶은 그룹이다. 보통 몇 초 단위로 끊기며, 독립적으로 재생할 수 있다. 최적화된 영상 재생을 위해 특정 포맷을 따른다.

전체적인 설계도

앞서 언급했듯이 비디오는 다른 데이터와 조금 다르게 다룰 것이다. 빠른 재생을 위해 CDN을 사용하고, 메타데이터를 보관해 요청을 빠르게 처리할 것이다.

아래 그림에서 검은색 화살표는 영상 업로드 과정을, 빨간색 화살표는 영상 재생 과정을 나타낸다. 재생 과정은 심플하다. 곧바로 CDN에서 스트리밍을 받으면 된다. (책에서는 CDN에서 스트리밍하는 자세한 과정은 다루지 않았다.)

책에서는 업로드 과정을 좀 더 자세히 다루었다. 영상 1개를 업로드하기 위해 (1) 원본 저장소에서 CDN까지 (2) API서버와 메타데이터 캐시 및 DB 사이에 데이터가 흘러간다.

(1) 원본 저장소에서 CDN까지: 영상 인코딩을 비롯한 가공 작업을 거친 뒤 최종본을 CDN까지 업로드한다. 인코딩에 시간이 많이 걸리기 때문에 비동기로 처리하기 위해 중간에 인코딩 완료 큐를 두었다. 인코딩 완료 핸들러는 큐에서 각 영상을 꺼낸 뒤 메타데이터를 업데이트하고, 인코딩 된 영상 저장소로 보낸다. 마지막으로 사용자에게 직접 영상을 보여줄 CDN에 업로드한다.

(2) API서버에서 메타데이터 캐시/DB까지: (1)번 흐름이 이루어지는 동안 클라이언트는 병렬적으로 또다른 요청을 보낸다. 비디오 메타데이터를 업데이트하는 요청이다. 또한 비디오 업로드가 완료되면 API서버가 사용자에게 업로드가 완료 되었음을 알린다.

빠른 업로드를 위한 상세 설계

인코딩을 위한 상세 구조

비디오 변환 작업은 여러 단계를 거칠 수 있다. 단순히 동영상을 압축하는 작업뿐만 아니라 워터마크를 표시하거나 인코딩 화질을 선택하는 등 비디오 업로더 맞춤 기능이 제공될 수 있기 때문이다. 책에서는 이런 커스텀 기능을 제공하기 위해 DAG(Directed Acyclic Graph) 프로그래밍 모델을 도입한 방식을 소개한다. 실제로 페이스북의 스트리밍 비디오 엔진이 이 모델을 사용한다.

원본 파일을 비디오(영상), 오디오 가공 단계와 메타데이터 생성 단계로 나누어 처리한다. 비디오와 오디오를 나누어 처리한 다음 마지막에 병합하는 방식이다.

비디오 처리 아키텍처

빨간색으로 표시한 비디오 부분을 좀 더 살펴보자. 다음 작업을 수행한다.

  • 검사: 좋은 품질의 비디오인지, 손상은 없는지 확인한다.
  • 비디오 인코딩: 비디오를 다양한 해상도, 코덱, 비트레이트 조합으로 인코딩한다.
    • 예) 360p.mp4, 480p.mp4, 720p.mp4, 1080p.mp4, 4k.mp4 등의 결과물이 생길 수 있다.
  • 썸네일 추출: 사용자가 업로드한 이미지나 비디오에서 썸네일을 자동 추출한다.
  • 워터마크: 비디오에 대한 식별정보를 오버레이 형태로 띄워 표시한다.

그렇다면 위의 작업은 어떤 과정으로 수행될까? 각 작업은 시간과 컴퓨팅 파워를 꽤 잡아먹을 수 있기 때문에 다음 구조를 통해 비디오 작업이 이루어진다.

  1. 전처리기: DAG 생성과 캐시 생성, 그리고 추가 작업 총 3가지를 처리한다.
    1. DAG 생성: 위에서 봤던 검사, 썸네일 추출, 워터마크, ... 의 작업 중 어떤 것을 수행해야 하는지와 수행 순서 등을 받아 DAG를 만든다.
    2. 데이터 캐시: 안정성을 위해 GOP와 메타데이터를 임시 저장소에 보관한다. 특히 비디오 인코딩이 실패하는 경우 이 캐시를 보면 안정적으로 작업을 재개할 수 있다.
    3. 비디오 분할: 비디오 스트림을 GOP 단위로 쪼갠다. 단, 이는 원래 단말이나 브라우저가 담당하는 역할이기 때문에 단말/브라우저가 GOP 단위의 비디오 분할을 지원하지 않는 경우에만 전처리기가 담당한다.
  2. DAG 스케줄러: DAG 그래프를 N단계로 분할해서 각각을 자원 관리자에게 넘겨준다. 정확히는 각 스텝을 자원 관리자의 작업 큐에 집어넣는다.
    1. 예) 1단계로 비디오/오디오 분할 작업, 2단계로 썸네일 추출 및 워터마크 작업
  3. 자원 관리자: 현재 상황을 보고 어떤 작업을 수행해야 가장 효율적일지 결정하여 작업 서버에게 넘겨준다. 3개의 큐와 스케줄러로 구성된다. 스케줄러는 아래 큐 3개를 보고 최적의 작업+서버 조합을 골라 넘겨 준 뒤, 작업이 완료되면 해당 작업을 실행 큐에서 제거한다.
    1. 작업 큐: DAG 스케줄러가 넘겨주는 작업이 들어있는 우선 순위 큐.
    2. 작업 서버 큐: 작업 서버의 가용 상태가 들어있는 우선 순위 큐.
    3. 실행 큐: 현재 실행 중인 작업과 작업 서버 정보가 들어있는 큐.
  4. 작업 실행 서버: DAG에 정의한 작업을 수행한다.
    1. 이 때, 각 작업 종류에 따라 작업 서버도 구분하면 좋다.
      1. 예) 워터마크 담당 서버, 인코딩 담당 서버, 썸네일 담당 서버, 병합 담당 서버
  5. 임시 저장소: 작업 서버의 프로세스가 실패한 경우를 대비하고 빠른 작업 속도를 위해 임시로 데이터를 저장한다.
    1. 메타데이터는 메모리에, 비디오/오디오 데이터는 BLOB 저장소에 두는 것이 좋다.
    2. 비디오 프로세싱이 완료되면 삭제한다.
  6. 인코딩 된 비디오: 위 파이프라인의 최종 결과물.

비동기 처리로 인코딩을 빠르게

비디오 하나가 위 파이프라인을 거쳐가는 데 꽤 많은 시간이 소요될 것이다. 중간에 실패하는 경우도 있을 것이고, 지연되는 경우도 많을 것이다. 따라서 파이프라인의 각 단계가 이전 단계의 완료 여부에 의존하지 않을 수 있도록 비동기 처리가 필요하다. 즉, 앞 단계가 너무 빠르거나 너무 느리게 끝나더라도 자신이 할 일을 꾸준히 처리할 수 있도록 해줘야 한다.

최적화 기법

비디오 병렬 업로드

비디오 전부를 단 한번만에 올리는 것은 비효율적이다. 지연이나 실패가 있을 수 있기 때문이다. 따라서 비디오 하나를 GOP 여러개로 분할해서 업로드하는 것이 좋다. 일부가 실패하더라도 빠르게 업로드를 재개할 수 있기 때문이다.

업로드 센터를 가까운 곳에

비디오 스트리밍 시 CDN을 사용하는 것과 같은 원리로, 비디오 업로더와 업로드 센터를 물리적으로 가깝게 위치시키는 것도 방법이다. 책에서는 아예 CDN을 업로드 센터로 사용한다.

모든 절차를 병렬화

원본 저장소에서 GOP를 다운로드하고, GOP를 인코딩하고, 이를 다시 인코딩 된 영상 저장소로 업로드 한 뒤 CDN으로 보내는… 일련의 작업 역시 비동기로 처리되어야 한다. 이를 위해 각 작업 사이에 메시지 큐를 도입해서 각 단계의 결합도를 낮출 수 있다. 예를 들어 인코딩 모듈과 업로드 모듈 사이에 메시지 큐를 두면, 인코딩 작업 속도와 관계 없이 업로드 작업을 병렬적으로 처리할 수 있다. 책에서는 모든 단계 사이에 메세지 큐를 두는 모습을 보여준다.

빠른 접근을 위한 상세 설계

이제 영상 업로드가 아닌 빠른 접근을 위한 방법을 살펴보자. 사실 대부분 위에서 말한 내용이라 간략하게만 언급하겠다.

메타 데이터

메타 데이터를 통해 영상에 빠르게 접근할 수 있다. 여기서 말하는 ‘접근’이란 검색, 영상 추천, 광고 등을 위해 동영상 정보를 탐색하는 것을 말한다. 당연히 재생을 위한 메타데이터도 저장된다. 주로 파일 이름, 크기, 포맷 등의 정보가 들어간다. 구글의 설명에 따르면 유튜브는 동영상을 인덱싱하기 위해 비디오 제목, 태그, 설명을 메타데이터로 사용한다.

CDN & 여러 화질로 인코딩

CDN을 사용해 영상을 빠르게 스트리밍 할 수 있다. 또한 영상을 여러 화질로 인코딩해두면 사용자의 네트워크 상황에 따라 화질을 조절해 속도를 보장할 수 있다.

기타 최적화 및 오류 처리 방식

안정성 최적화를 위해: 미리 생성된 업로드 URL

악의를 가진 클라이언트가 비디오를 업로드하거나, 잘못된 장소에 비디오가 업로드 되는 일을 막히 위해 미리 지정된(pre-signed) 업로드 URL을 사용한다.

  1. 클라이언트는 API 요청을 보내 자신에게만 접근 권한이 부여된 URL을 요청한다.
    1. 아마존 S3에서는 ‘미리 사인된 URL’, 마이크로소프트 Azure는 ‘접근 공유 시그니처’ 라고 부른다.
  2. API 서버가 해당 URL을 돌려준다.
  3. 클라이언트는 받은 URL이 가리키는 위치에 비디오를 업로드한다.

비용 최적화를 위해: long-tail 분포

책의 연구 결과에 따르면 유튜브의 비디오 스트리밍은 롱테일(long-tail) 분포를 따른다고 한다. 이를 기반으로 몇가지 최적화를 할 수 있다.

  • 인기 비디오는 CDN을 통해, 나머지 비디오는 비디오 서버를 통해 재생한다.
  • 인기가 별로 없는 비디오는 인코딩 할 필요가 없을 수도 있다.
  • 짧은 비디오라면 필요할 때마다 인코딩하여 재생할 수 있다.
  • 어떤 비디오는 특정 지역에서만 인기가 높다. 이를 다른 모든 지역에 옮겨둘 필요는 없다.

오류 처리

시스템 오류에는 2가지 종류가 있다. 회복 가능 오류(recoverable error)와 회복 불가능 오류(non-recoverable error)다. 스트리밍 시스템에서 각각의 예시는 다음과 같다.

  • 회복 가능 오류: 비디오의 일부를 인코딩하는데 실패함
  • 회복 불가능 오류: 비디오 포맷이 잘못됨

회복 가능 오류는 몇 번 재시도 해보고 끝까지 안 되면 클라이언트에게 오류 코드를 반환하하고, 회복 불가능 오류는 곧바로 작업을 중단하고 오류 코드를 반환하면 된다.

각 서버별 장애를 막기 위해서 다음과 같은 조치를 할 수 있다.

  • API 서버 장애: API 서버를 state-less하게 두어 신규 요청을 다른 API 서버로 우회시킨다.
  • 메타데이터 캐시 서버 장애: 데이터를 다중화한다. 장애가 난 캐시 서버는 새로운 것으로 교체한다.
  • 메타데이터 DB 서버 장애: 리더 서버가 죽었다면 새로운 리더를 선출하고, 부 서버가 죽었다면 다른 부 서버를 통해 일긱 연산을 처리하고 죽은 서버는 새것으로 교체한다.
profile
설계를 좋아합니다. 코드도 적고 그림도 그리고 글도 씁니다. 넓고 얕은 경험을 쌓고 있습니다.

0개의 댓글