쿠팡의 A/B 테스트 지표 연산

지표 연산 프레임워크의 아키텍처와 그 변천사

쿠팡 엔지니어링
Coupang Engineering Blog
6 min readAug 3, 2022

--

By Beibei Ye & Isabelle Phan

시소 일러스트

본 포스트는 영문으로도 제공됩니다.

2014년, 쿠팡은 대규모 A/B 테스트를 운영할 수 있는 인하우스 실험 플랫폼을 구축하기 시작했습니다. 이 플랫폼의 가장 중요한 구성요소 중 하나는 비지니스 쪽 사용자에게 시기적절한 인사이트를 제공하여 데이터 기반의 의사결정을 가능케 하는 지표 연산 프레임워크(metric calculation framework)입니다. 쿠팡 서비스의 규모를 고려했을 때 지표 연산 프레임워크의 개발은 쉽지 않은 중요 과제였고, 수백 건의 A/B 테스트에서 생성되는 수십억 개의 데이터 행들을 실시간으로 처리할 수 있어야 했습니다.

이번 포스트에서는 크게 어떤 아키텍처를 바탕으로 쿠팡 실험 플랫폼의 지표 연산 프레임워크가 구축되어 왔는지, 2014년부터의 진화 과정을 소개하려고 합니다.

목차

- Phase 1: 느리고 한계가 뚜렷했던 시작점 (2014~2016)
-
Phase 2: 속도, 확장성, 강건성을 얻다 (2016)
-
Phase 3: 클라우드로의 이동 (2017~현재)
-
The next phase

Phase 1: 느리고 한계가 뚜렷했던 시작점

증분 연산 과정을 보여주는 다이어그램
그림 1. 증분 연산 과정을 보여줍니다. 금일 결과 = 작일 결과 + 금일 데이터

초기에는 Hive 기반으로 지표 연산을 실행했습니다. 연산 시간의 최적화를 위해 계산은 증분(incremental)으로 이루어졌습니다. 매일 당일 데이터(예: 판매 거래 및 고객 활동)만을 연산 처리했고 그 결과를 직전일의 결과와 통합하여 당일의 누적 지표를 생성했습니다.

하지만 이 방식에는 다음과 같은 한계가 있었습니다.

  • 비용 효율적이지 않고 느림. 20여 건의 A/B 테스트에 대한 지표를 계산하는데 3시간 가까이 걸렸습니다.
  • 비효율적인 데이터 수정. 증분 계산 방식(incremental calculation)은 직전일의 계산 결과에 의존적이었습니다. 소스 데이터의 문제나 코드 버그가 며칠 후에 발견되면 다른 계산 작업은 모두 미룬 채 문제가 발생한 날짜를 시작으로 이후 날짜 모두에 대해 다시 계산하고 연산해야 했습니다.
  • 부실한 지표 재연산. 하나의 테스트나 테스트의 하위 집합에만 제한적으로 영향을 미치는 문제가 발견될 경우 해당 테스트에 대해서만 지표를 재연산할 수 있는 방법이 존재하지 않았습니다. 같은 시기에 진행된 A/B 테스트는 모두 다 통합적으로 연산 처리되기 때문이었습니다.
  • 제한적인 데이터 출처. 연산 스케줄은 업스트림 데이터 소스의 데이터 동기화 빈도에 의해 제한되었습니다. 빈번한 업데이트가 일어나는 데이터 소스는 연산 과정에서 활용할 수 없었습니다. 그 결과, 당일 활동에 대한 지표는 차일 오후 2시가 되어야만 확인할 수 있었습니다.
  • 코드 유지관리의 어려움. 프레임워크는 단일 HiveQL 스크립트로 구성되었습니다. 쿼리를 재사용할 수 없기 때문에 스크립트의 코드는 수천 줄로 거대해졌고 유닛 테스트도 불가능했습니다.

이러한 제약으로 인해 개발 사이클이 크게 더뎌졌습니다. 저희는 하루에도 여러 차례 최신 지표를 확인하고 수익에 부정적인 영향을 미칠 수 있는 상황에 대해 빠른 피드백이 필요했기 때문에 지표 연산을 전면적으로 개조해야만 했습니다.

Phase 2: 속도, 확장성, 강건성을 얻다

이전 단계의 문제를 해결하기 위해 새로운 지표 연산 프레임워크 아키텍처를 설계했습니다. 장기적으로 지속 가능하고 폭발적인 성장에 맞춰 확장할 수 있는 새로운 시스템이 필요했습니다.

요건

개발에 앞서 저희는 지표 연산 프레임워크를 새롭게 설계하는 과정에서 다음의 요건들을 정의했습니다.

SLA
새로운 아키텍처의 우선순위 중 하나는 이벤트 발생(예: 클릭과 매출)과 결과 제공 사이의 지연을 최소화하는 것이었습니다. 실험용 기능(treatment)의 효과를 초기에 빠르게 측정할 수 있도록 결과의 하위 집합은 15분마다, 전체 결과는 4시간마다 연산할 수 있는 프레임워크를 설계하고자 했습니다.

확장성
동시 실행 중인 200건 이상의 A/B 테스트에 대한 지표 연산을 함께 실행할 수 있어야 했습니다.

강건성 그리고 내결함성
하드웨어 문제가 발생할 경우, 수동으로 개입하지 않아도 연산이 재개될 수 있어야 했습니다. 동작 오류가 발생했을 때에는, 빠르고 손쉽게 복구되어야 했습니다. 또한, 테스트 구성이 잘못되었을 경우를 대비해 테스트의 하위 집합에 대한 지표 재연산도 지원되어야 했습니다.

구축

위 요건을 충족하기 위해, Spark 기반의 람다(Lambda) 아키텍처로 지표 연산 프레임워크를 개발하게 되었습니다.

Apache Spark
더욱 빠른 처리 속도와 용이한 코드 유지관리 및 테스트를 위해 HiveQL 대신 Apache Spark를 도입했습니다.

동시에 실행되는 A/B 테스트 건수가 증가하면서 데이터 볼륨 역시 증가했습니다. Spark 최적화를 위해, 입력 데이터 모두를 다 같이 처리하는 대신 지표 연산 프레임워크는 테스트 별로 데이터를 나누고 각 테스트의 데이터를 개별 처리하기 시작했습니다. 분할-정복(divide-conquer) 방식의 적용으로 데이터 크기를 수백억에서 수천만 단위로 낮춰 안정화할 수 있었고 Spark의 데이터 처리 속도 적정 수준으로 유지할 수 있었습니다.

  • 입력 데이터: 200 테스트 x 15일 x 4백만 노출 로그 / 일 = 120억 로그
  • 프로세스당 데이터 연산: 1 테스트 x 15일 x 4백만 노출 로그 / 일 = 6천만 로그
병렬 스파크 잡의 시퀀스
그림 2. 병렬 스파크 잡의 시퀀스

또한, Spark 성능을 최적화하고 Spark 리소스를 최대한으로 활용하기 위해 테스트 데이터의 처리 과정을 위 그림과 같이 Akka 액터 프레임워크를 사용해 병렬화했습니다. 마스터 액터(master actor)는 테스트 정보를 가져오고 워커 액터 풀(worker actor pool)을 생성합니다. 그리고 각 테스트에 대해 메시지를 생성하여 순차 순환 방식으로 워커 액터에 배정합니다. 하나의 워커가 실패하더라도 다른 테스트는 계속 처리되고, 처리 실패한 테스트는 Spark job 최종 단계에 보고되어 별도의 잡을 실행하여 처리하도록 합니다.

이 병렬 처리 방식은 연산 시간을 크게 줄였습니다. 또한, 테스트 수준에서 실패를 분리해 하나의 테스트 실패가 다른 테스트의 처리를 방해하지 않도록 했습니다.

람다 아키텍처

람다 아키텍처의 개요
그림 3. 람다 아키텍처의 개요

강건성 요건을 충족하기 위해 람다 아키텍처를 도입했습니다. 람다 아키텍처에는 3개의 레이어가 있습니다.

배치 레이어(Batch Layer)는 신뢰할 수 있는 출처로 모든 데이터를 정확하고 안전하게 처리합니다. 내결함적(fault tolerant)이라 일부 에러가 발생해도 전체적으로는 정상 작동하지만, 이미 결과 데이터는 수 시간의 지연이 있습니다. 속도 레이어(Speed Layer)는 배치 레이어의 지연성을 보완합니다. 최종 배치와 현시점 사이의 델타 데이터를 처리합니다. 정확성보다 속도를 중시하며 거의 실시간으로 결과를 제공하지만, 증분 연산 방식을 사용하기 때문에 좀 더 복잡합니다. 서빙 레이어(Serving Layer)는 배치와 속도 레이어의 출력을 합쳐 비지니스 쪽 사용자에게 결과를 제공합니다.

지표 연산 프레임워크에 람다 아키텍처를 다음과 같이 적용했습니다.

  • 수신 데이터 소스. 테스트에 노출된 고객의 데이터와 실시간 주문 데이터는 Kafka로 제공됩니다. 일간 판매 거래 데이터와 시간당 고객 활동 데이터는 Hadoop Distributed File System(HDFS)에 저장됩니다. A/B 테스트 구성 데이터는 MySQL에 저장됩니다.
  • 배치 레이어. 데이터의 마스터 복사본은 날짜 기준으로 분할(partition)되며 테스트는 HDFS에 보관됩니다(append 모드만 지원됨). 일괄 처리 작업(batch job)에는 4시간마다 실행되는 Spark job과 하루에 1번 실행되는 Spark reconciliation job(재조정 작업)이 포함됩니다. 증분 연산은 이 레이어에선 사용되지 않습니다.
  • 속도 레이어. Spark streaming job은 Kafka로 제공되는 실시간 주문 데이터와 테스트에 노출된 고객의 데이터를 수집합니다. 주요 실적 지표의 하위 집합은 15분마다 증분 계산됩니다.
  • 서빙 레이어. 지표 결과는 MySQL 데이터베이스로 전송(push)되고, 비지니스 쪽 사용자가 확인할 수 있도록 웹 애플리케이션에 표시됩시다.

데이터 집산 단계

표준 람다 아키텍처에서는 배치 레이어가 중간 데이터세트(intermediate dataset)가 아닌 모든 원시 입력 데이터(raw input data)를 처리합니다. 그래서 코드 버그나 데이터 문제와 같은 사고가 생기더라도 데이터를 수정하면 배치 레이어가 처음부터 데이터를 재처리해 정확한 결과를 출력하기 때문에 최고의 시스템 강건성을 보장할 수 있습니다.

하지만 초기 성능 테스트 결과, 당시 사용 중이던 Spark 1.5는 수십억 단위의 데이터 세트를 효율적으로 합칠 수 없었습니다. 지표 연산 프레임워크의 SLA와 확장성 요건을 충족하기 위해서는 일간 중간 데이터세트를 유지관리하는 방식을 선택해야 했습니다.

아래의 그림은 배치 레이어 내에 어떠한 데이터 집산 단계(data aggregation level)가 있는지를 보여줍니다. 입력 데이터는 고객으로부터 사전 집산되고 이 데이터세트는 어떠한 테스트에도 의존성이 없습니다. 해당 데이터는 일간 테스트 수준 지표의 연산을 위해 어떠한 테스트에서도 재사용될 수 있습니다. 누적 지표는 테스트 기간에 걸쳐 생성된 일간 테스트 수준 데이터를 집산하여 계산합니다.

데이터 집계 수준
그림 4. 데이터 집계 단계

새로운 아키텍처 덕분에, 지표 연산 프레임워크는 모든 합의된 SLA를 충족시킬 수 있었습니다.

  • 배치 레이어가 4시간마다 전체 데이터세트를 처리하게 되면서, 수백 개의 A/B 테스트 결과를 제공하는 데 걸리는 시간이 14시간에서 5시간으로 단축됐습니다.
  • 속도 레이어는 결과의 하위 집합 데이터를 15분마다 제공하게 되었습니다.

Phase 3: 클라우드로의 이동

새로운 아키텍처를 바탕으로 개조된 지표 연산 프레임워크는 쿠팡의 엔지니어와 비즈니스 쪽 사용자의 경험을 크게 개선했습니다. 그리고 쿠팡 서비스가 클라우드 환경으로 이전하기 시작하면서, 프레임워크의 성능을 강화할 수 있는 또 다른 기회를 포착했습니다.

2017년, 온프레미스로부터 클라우드 환경으로의 이전이 완료되었고 데이터 스토리지를 HDFS에서 Amazon Simple Storage Service (S3)로 변경했습니다. 클라우드 이전으로 인해 실험 플랫폼의 지표 연산 프레임워크에 두 가지 큰 변화가 있었습니다.

  • Spark 1.5가 2.1로 업그레이드되어 Tungsten 지원이 가능해졌습니다. Tungsten을 기반으로 메모리와 CPU 리소스를 더 효율적으로 활용할 수 있게 되었습니다.
  • 데이터를 S3에 직접 쓰고 읽을 수 있게 되었습니다. I/O 속도가 상당히 개선되었을 뿐더러 잡이 Hive Metastore에서 독립하면서 안정성 역시 강화되었습니다.

클라우드 환경으로 이전 후 지표 연산 시간은 4시간에서 1.5 시간으로 60% 이상 줄어들었습니다.

The next phase

이번 포스트에서는 실험 플랫폼의 지표 연산 프레임워크가 어떻게 변화해왔는지를 공유했습니다. 일간으로만 지표를 연산할 수 있던 Hive 기반 프레임워크가 Spark 기반의 배치 및 실시간 동적 처리 엔진으로 진화했습니다.

쿠팡의 실험 플랫폼 팀은 비즈니스 쪽 사용자를 위해 지표 연산 프레임워크를 지속해서 혁신하고 개선하는 데 최선을 다하고 있습니다. 현재 팀에서는 도메인 별로 지표를 구성할 수 있는 차세대 지표 엔진을 개발 중입니다. 새 엔진은 고급 구성을 기반으로 신규 지표를 정의하고 추가하는데 필요한 코드와 시간을 단축시킬 수 있을 것입니다. 다음 포스트에서 이러한 개선 사항 내용을 공유할 수 있기를 바랍니다.

진정한 데이터 기반 기업의 실험 플랫폼 개선 업무에 관심이 있다면 지금 쿠팡의 채용 공고를 확인해 보세요!

--

--

쿠팡 엔지니어링
Coupang Engineering Blog

쿠팡의 엔지니어들은 매일 쿠팡 이커머스, 이츠, 플레이 서비스를 만들고 발전시켜 나갑니다. 그 과정과 결과를 이곳에 기록하고 공유합니다.