News Feed

슈퍼볼급 트래픽을 견디는 NBC유니버설의 분산 시스템 설계 원칙

컨텐츠 정보

  • 조회 457

본문

올림픽이나 프리미어리그 경기, 시즌 피날레 등 대형 이벤트의 인프라를 운영할 때면 대부분 시스템이 좀처럼 겪지 않는 썬더링 허드(thundering herd) 문제에 직면한다. 수백만 명의 사용자가 단 3분이라는 짧은 시간 안에 동시에 로그인하고, 콘텐츠를 탐색한 뒤 재생 버튼을 누르기 때문이다.

이런 상황은 미디어 업계에 국한된 문제가 아니다. 블랙프라이데이를 앞둔 이커머스 기업의 CTO나 시장이 급변하는 상황에서 밤을 지새우는 금융 시스템 아키텍트에게도 동일한 악몽이다. 근본적인 문제는 언제나 같다. 수요가 시스템 수용 능력을 한 자릿수 이상으로 초과할 때, 어떻게 살아남을 수 있을까?

엔지니어링 팀 대부분은 오토스케일링(auto-scaling)이 해결책이 될 것이라 기대한다. 그러나 ‘슈퍼볼급’ 규모에서 오토스케일링은 환상에 가깝다. 반응 속도가 너무 느리기 때문이다. 클라우드 서비스 업체가 새 인스턴스를 띄우는 사이 이미 지연 시간은 급증하고 데이터베이스 커넥션 풀은 고갈되며, 사용자는 500 오류 화면을 마주하게 된다.

대규모 동시 접속을 버티기 위해 NBC유니버설(NBCUniversal)이 사용하는 아키텍처 패턴은 4가지다. 터치다운을 스트리밍하든, 한정판 스니커즈 결제 대기열을 처리하든 동일하게 적용된다.

1. 공격적인 로드 셰딩

엔지니어가 가장 흔히 저지르는 실수는 로드 밸런서로 들어오는 모든 요청을 처리하려고 시도하는 것이다. 동시 접속이 폭증하는 이벤트 상황에서는 치명적인 선택이다. 시스템 처리 용량이 초당 10만 건(RPS)인데 12만 건의 요청이 들어올 경우, 모두를 처리하려다 보면 데이터베이스가 잠기고 결국 아무도 서비스를 받지 못하는 상황으로 이어지기 쉽다.

NBC유니버설은 비즈니스 우선순위에 따라 로드 셰딩(load shedding)을 적용한다. 12만 명의 요청을 모두 처리하려다 사이트 전체를 마비시키는 것보다, 10만 명에게는 완벽한 서비스를 제공하고 나머지 2만 명에게는 “잠시만 기다려 달라”라고 안내하는 편이 낫다는 판단에서다.

이를 위해서는 게이트웨이 계층에서 트래픽을 명확한 티어로 분류해야 한다.

  • 티어 1(핵심) : 로그인, 영상 재생, 이커머스의 경우 결제, 재고 잠금 등 반드시 성공해야 하는 요청
  • 티어 2(저하 가능) : 검색, 콘텐츠 탐색, 사용자 프로필 수정 등 오래된 캐시 데이터를 활용해 제공 가능
  • 티어 3(비필수) : 추천, 다른 사용자가 함께 구매한 콘텐츠, 소셜 피드 등 실패하더라도 사용자에게 별도로 드러나지 않아도 되는 영역

NBC유니버설은 적응형 동시성 제한을 활용해 하위 시스템의 지연 시간이 증가하는 시점을 감지한다. 예를 들어 데이터베이스 응답 시간이 특정 임계치(예 : 50밀리초)를 넘는 순간, 시스템은 자동으로 티어 3 서비스 호출을 중단한다. 이 경우 사용자는 평소보다 노출되는 요소가 줄어든 홈 화면을 보게 되지만, 영상 재생이나 결제는 정상적으로 완료된다.

대규모 트래픽을 처리하는 시스템이라면 ‘저하 모드(degraded mode)’를 반드시 정의해야 한다. 트래픽 급증 상황에서 어떤 티어를 비활성화할지 미리 결정해 두지 않으면 그 판단은 시스템이 대신 내리게 되고, 결과적으로는 모든 기능이 동시에 멈추는 상황으로 이어지기 쉽다.

2. 벌크헤드와 블라스트 반경 격리

Diagram: Using a ship metaphor to describe the bulkhead pattern in concurrency

Manoj Yerrasani

유람선은 선체 내부를 방수 구획인 벌크헤드(bulkhead)로 나눈다. 한 구획에 물이 차더라도 배 전체가 가라앉지 않도록 하기 위해서다. 분산 시스템에서도 이와 같은 개념이 필요하지만, 실제로는 구획이 없는 배처럼 시스템을 설계하는 경우가 적지 않다.

필자는 사소한 기능 하나가 대규모 장애로 이어진 사례를 여러 차례 목격했다. 예를 들어 사용자 아바타를 제공하는 외부 API가 장애를 일으켰다고 가정해 보자. 로그인 서비스가 세션을 확정하기 전에 아바타 로딩을 기다리도록 설계돼 있다면, 이 외부 API 장애로 인해 로그인 전체 흐름이 멈추게 된다. 단순한 시각적 요소 하나가 핵심 비즈니스 기능을 마비시키는 셈이다.

이를 방지하기 위해 NBC유니버설은 벌크헤드 패턴을 적용한다. 서로 다른 의존성을 각각 분리된 스레드 풀과 커넥션 풀로 격리해 특정 구성 요소의 장애가 시스템 전체로 확산하는 것을 막는 방식이다.

이커머스 환경에서는 ‘재고 서비스’와 ‘사용자 리뷰 서비스’가 동일한 데이터베이스 커넥션 풀을 공유하면 안 된다. 리뷰 서비스를 겨냥한 봇이 대량으로 데이터를 수집해 부하를 발생시키더라도, 그로 인해 상품 재고를 조회하는 데 필요한 자원이 소진되는 상황은 막아야 한다는 의미다.

이를 위해 엄격한 타임아웃과 서킷 브레이커(Circuit Breaker) 패턴을 적용한다. 비필수 의존성이 50% 이상 실패할 경우, 즉시 해당 호출을 중단하고 기본값을 반환하는 방식이다. 예를 들어 일반적인 아바타 이미지나 캐시된 리뷰 점수를 보여주는 식이다.

고처리량 서비스에서는 스레드 풀 격리보다 세마포어(semaphore) 격리를 선호한다. 스레드 풀은 컨텍스트 스위칭으로 인한 오버헤드를 수반하지만, 세마포어는 특정 의존성에 대해 허용되는 동시 호출 수만 제한해 초과 트래픽을 큐에 쌓지 않고 즉시 거부한다. 주변 기능이 불타고 있더라도 핵심 트랜잭션은 살아남는다.

3. 요청 병합으로 썬더링 허드 길들이기

5만 명의 사용자가 정확히 같은 시점에 홈 화면을 불러오는 상황을 떠올려 보자. 경기 시작 시간이나 신제품 출시 순간처럼 모든 요청이 동시에 몰린다. 이때 5만 건의 요청은 모두 동일한 데이터를 요구한다. 바로 슈퍼볼 스트리밍에 대한 메타데이터다. 이 5만 건의 요청을 그대로 데이터베이스로 흘려보내면 데이터베이스는 버티지 못하고 무너진다.

캐싱이 가장 먼저 떠오르는 해법이지만, 일반적인 캐싱만으로는 충분하지 않다. 이 경우 캐시 스탬피드(Cache Stampede)라는 또 다른 문제에 취약해지기 때문이다. 인기 있는 캐시 키가 만료되는 순간, 수천 개의 요청이 동시에 캐시 미스를 감지하고 해당 데이터를 재생성하기 위해 한꺼번에 데이터베이스로 몰려드는 상황이 발생한다.

이를 해결하기 위해 NBC유니버설은 흔히 ‘싱글플라이트(singleflight)’라고 불리는 요청 병합(request collapsing) 기법을 활용한다.

캐시 미스가 발생하면 가장 먼저 들어온 요청 하나만 데이터베이스로 향해 데이터를 가져온다. 동시에 시스템은 4만 9,999명의 사용자가 동일한 캐시 키를 요청하고 있다는 사실을 인식한다. 이들 요청을 데이터베이스로 보내는 대신 대기 상태로 유지한다. 첫 번째 요청이 결과를 반환하면, 시스템은 해당 값을 캐시에 저장하고 그 단일 결과를 기반으로 5만 명의 사용자 모두에게 응답을 제공한다.

이 패턴은 유통 업계의 플래시 세일과 같은 상황에서 특히 중요하다. 백만 명의 사용자가 상품 재고 여부를 확인하기 위해 동시에 페이지를 새로고침하는 상황에서, 백만 번의 데이터베이스 조회를 수행할 수는 없다. 한 번만 조회한 뒤 그 결과를 모든 사용자에게 전달하는 방식이 합리적이다.

NBC유니버설은 확률적 조기 만료(Probabilistic Early Expiration), 이른바 X-패치(X-Fetch) 알고리즘도 함께 활용한다. 캐시 항목이 완전히 만료될 때까지 기다리는 대신, 아직 유효한 상태에서 백그라운드로 데이터를 다시 가져오는 방식이다. 이를 통해 사용자는 항상 웜 캐시(warm cache)에 접근하게 되고, 캐시 스탬피드가 발생하는 상황을 사전에 차단할 수 있다.

4. ‘게임데이’ 리허설

앞서 언급한 패턴은 실제로 검증되기 전까지는 이론에 불과하다. 또한 경험상 위기 상황에서 평소보다 더 뛰어난 대응을 해내는 경우는 드물다. 결국 위기 대응 수준은 훈련 수준에 수렴한다.

올림픽이나 슈퍼볼과 같은 대형 이벤트를 앞두고 NBC유니버설은 의도적으로 시스템을 망가뜨린다. 실제 운영 환경, 또는 이에 근접한 복제 환경에서 대규모 트래픽 급증을 시뮬레이션하고, 인위적으로 장애를 주입하는 ‘게임데이’를 진행한다. 이 과정에서 다음과 같은 구체적인 재난 시나리오를 가정한다.

  • 기본 레디스 클러스터가 갑자기 사라지면 어떻게 되는가?
  • 추천 엔진의 지연 시간이 2초까지 치솟으면 어떤 일이 벌어지는가?
  • 60초 안에 500만 명이 동시에 로그인하면 시스템은 버틸 수 있는가?

이러한 훈련을 통해 로드 셰딩이 실제로 작동하는지, 벌크헤드가 장애 확산을 제대로 차단하는지 검증한다. 이 과정에서 클라이언트 라이브러리에 설정된 기본 타임아웃처럼 사소해 보이는 설정 하나가 그동안의 모든 노력을 무력화하는 사례를 발견하는 경우도 적지 않다.

이커머스 업계라면, 예상되는 블랙프라이데이 트래픽보다 최소 50% 이상 높은 수준을 가정해 스트레스 테스트를 진행해야 한다. 시스템이 실제로 무너지는 지점, 즉 파괴 지점(breaking point)을 명확히 파악해야 한다. 초당 몇 건의 주문이 들어올 때 데이터베이스가 한계에 도달하는지 정확히 알지 못한다면, 해당 이벤트를 치를 준비가 됐다고 보기 어렵다.

회복탄력성은 사고방식에 달렸다

회복탄력성은 AWS나 애저에서 구매하는 것이 아니다. 쿠버네티스로 전환하거나 노드를 더 추가한다고 해서 이런 문제를 해결할 수 있는 것도 아니다.

NBC유니버설의 ‘슈퍼볼급 기준’은 장애를 바라보는 관점 자체의 근본적인 전환을 요구한다. 구성 요소는 반드시 고장 난다고 가정해야 한다. 네트워크는 느려질 수밖에 없다고 봐야 한다. 사용자가 때로는 DDoS 공격과 다름없는 행동을 보일 것이라고 전제해야 한다.

스트리밍 플랫폼이든, 금융 거래 원장이든, 온라인 쇼핑몰이든 목표는 절대 깨지지 않는 시스템을 만드는 데 있지 않다. 일부는 깨지더라도 핵심 비즈니스 가치가 유지될 수 있도록 부분적이고 점진적으로 무너지는 시스템을 설계하는 것이 핵심이다.

트래픽이 실제로 쏟아진 뒤에야 가정을 검증하는 것은 너무 늦다.
dl-itworldkorea@foundryco.com

관련자료

댓글 0
등록된 댓글이 없습니다.