Hive는 인증, 멤버십, 프로모션, 빌링, 노티피케이션, 고객센터, 애널리틱스 등으로 구성된 모바일 게임 플랫폼입니다. Hive 플랫폼을 이용해 게임을 개발하고 운영하기 위해서 게임 클라이언트에 탑재되는 Hive SDK와 각 기능의 운영을 위한 Hive 콘솔이 제공됩니다.
Hive 애널리틱스는 게임 클라이언트와 게임 서버에서 발생하는 로그를 수집해서, 게임의 상태를 분석할 수 있도록 각종 지표들을 제공하고, 유저 세그먼트를 통한 타기팅 마케팅 등을 할 수 있는 환경을 제공하는 서비스입니다. 2013년에 최초로 콘솔이 만들어졌고, 이후 Google 클라우드 플랫폼을 이용한 차세대 버전이 2017년에 오픈했습니다.
먼저 어떤 배경에서 Google 클라우드 플랫폼을 도입하게 되었는지 말씀드리겠습니다. 2012년에 RDBMS 기반으로 데이터웨어하우스(Data Warehouse, DW)를 구축해서 애널리틱스 서비스를 했었는데, 3년 정도 사용을 하니 저장 용량이나 처리 속도 등에서 한계를 느꼈고, 수집되는 데이터의 종류도 점점 많아지면서 새로운 기능에 대한 요구도 증가했습니다. 이에 따라 다각도의 검토 후에 Google의 BigQuery를 이용할 것을 결정했고, 2016년에 Google 클라우드 플랫폼 기반의 시스템 개발을 시작해서 2018년부터는 차세대 버전을 전면적으로 사용하기 시작했습니다.
다시 말씀드리면 개편의 주된 목적은 클라우드 기반으로 쿼리 속도와 저장 용량의 한계를 개선하는 것이었습니다. 이를 위해 Hadoop을 기반으로 자체 구축하는 것도 검토했지만, 단기적인 재무 부담과 빠른 시간 내에 새로운 시스템을 구축할 필요성을 고려하면 BigQuery가 더 유리하다고 판단했습니다. 그래서 DW를 BigQuery 기반으로 바꾸는 작업을 하게 되었습니다. 이렇게 하려면 2012년부터 쌓인 모든 데이터를 이관해야 했고, 실시간으로 들어오는 데이터 파이프라인도 BigQuery로 변경해야 했습니다.
전체 시스템 구성도
Google에서는 람다 아키텍쳐(Lambda Architecture)를 기반으로 위와 같은 구성을 제안해주셨습니다. Google의 PubSub, GCS, Dataflow, BigTable을 사용하고, Fluentd를 통해 데이터를 BigQuery에 적재하여 자체 개발한 사이트에서 그 데이터를 활용할 수 있다는 내용입니다.
실제 구축된 결과는 BigTable을 제외하고 Google에서 제안한 구성과 비슷하게 되었습니다. 최종적으로 레거시 시스템과 Google 클라우드 플랫폼 상의 시스템을 통합한 ETL 구성도는 아래 그림과 같습니다. 그림의 윗 부분은 실시간 ETL인데, 운영 중인 서버에서 발생한 로그들을 Fluentd로 취합해서 Google PubSub와 Dataflow를 거쳐 BigQuery에 탑재하고 있다는 내용입니다. 아래 부분에서는 레거시 시스템을 그대로 유지하면서 동일 데이터를 짧은 주기의 배치 ETL로 GCS, Dataflow를 거쳐 BigQuery에 넣고 있습니다. 그럼 이제부터는 각 구성요소를 하나씩 설명 하겠습니다.
실시간 ETL 구성요소
Fluentd
Fluentd는 실리콘밸리의 Treasure Data 사에서 개발을 했는데, 얼마 전에 이 회사가 ARM 사에 인수되어 이제는 ARM의 Fluentd라고 할 수 있습니다. 매우 널리 사용되어 잘 알려진 로그 수집 프로그램이며, 그 특징은 아래와 같습니다.
네트워크 장애 시 재시도 또는 재전송 데이터 급증 시 버퍼링 기능 설치와 사용이 쉽고 레퍼런스 많음 로드밸런싱을 통한 고가용성 구축 용이 다양한 입출력 플러그인
Scribed
Scribed는 Facebook 사에서 공개한 오픈소스 로그 수집 프로그램으로 단일 서버에서 성능은 거의 최고이며, 2013년 도입 후 현재까지 잘 동작하고 있습니다. 그러나 신규로 구축하는 시스템에서 사용하기는 우려가 되는 부분이 있었습니다. 업데이트가 중단되어 Facebook에서도 더이상 사용하지 않고 있고, 입출력에 대한 확장성이 없어 PubSub으로 데이터 전송이 불가했습니다.
Google Cloud PubSub
PubSub은 대용량 큐(Queue)인데, Kafka와 같은 기능이 Google 클라우드 플랫폼에서 제공되는 것입니다. Google에서는 전 세계의 데이터센터에 분산 저장되므로 99.95% 가용성을 보장하고, 낮은 레이턴시와 데이터 전송을 보장한다고 밝히고 있습니다. 실제 사용해본 결과도 매우 안정적이었습니다.
앞서 설명한 Fluentd에서 취합된 로그들이 PubSub을 거쳐 BigQuery에 올라갑니다. 그런데 Google에서 제안 내용에도 Fluentd가 포함되어있지만, Google이 Fluntd의 PubSub 플러그인을 제공하는 것은 아니었습니다. 오픈소스 프로젝트에서 검색을 해서 2개의 플러그인을 찾았습니다. 그중 하나는 1초 단위로 버퍼링된 데이터를 계속 올리는데 PubSub의 전송(publish) 제한 용량을 초과하여 사용할 수 없었습니다. 다른 하나는 파일이 대용량이면 잘게 나누어 전송하므로 제한 용량 문제는 해결되었습니다. 하지만 프로토콜 오류 메시지가 발생하였습니다. 오픈소스 프로젝트이므로 코드를 직접 확인하였고 프로토콜 내 Base64 관련 부분과 UTF-8 변환 로직에 오류가 있는 것을 발견하여 수정하였습니다. 수정한 코드는 GitHub에도 공개했습니다.
PubSub은 전송 건마다 최소 비용이 발생하므로, 로그 1건 단위로 전송을 하면 그 비용을 감당하기 어렵습니다. 따라서 수집된 데이터를 버퍼링하여 전송해야 하는데, 한번에 전송하는 데이터의 크기는 10 MB 및 1000 줄을 초과할 수 없습니다. 또한 대용량 큐의 일반적인 특징으로 PubSub 또한 전송된 데이터는 중복이 발생할 수 있고 그 순서도 보장이 되지 않습니다. 따라서 타임스탬프(timestamp) 와 GUID를 사용하여 백엔드에서 해결해야 합니다.
PubSub의 가장 큰 장점은 관리가 필요 없다는 점이었습니다. 용량이나 처리 속도에 대한 관리가 불필요하고, 서버 단에서 발생할 수 있는 로우 레벨의 장애 대응도 불필요합니다. 2016년 이후 현재까지 장애가 없었습니다. 또한 큐 서버를 직접 운영하는 것과 비용의 차이가 거의 없었습니다. PubSub을 이용하는 비용은 Kafka/zookeeper 3~5세트를 GCE(Google Cloud Engine) 에 올려서 운영하는 비용과 비슷한 수준입니다.
Google Cloud Dataflow
그 다음은 Dataflow입니다. PubSub에 들어온 데이터는 이 Dataflow를 거쳐 BigQuery에 들어갑니다. Dataflow는 ETL(Extract, Transform, Load)에서 변환(Transform) 역할을 합니다. DataFlow는 Spark 스트리밍 같은 빅데이터 분산 처리 프레임워크입니다. 이 서비스의 소스코드는 공개되어 Apache Beam이라는 오픈소스 프로젝트로 개발되고 있습니다.
Dataflow는 상당히 많은 기능이 이 있는데, 여기서는 주로 사용하는 파이프라인에 대해서만 설명하겠습니다. PubSub에서 데이터를 끌어와서 여러가지 전처리를 하는데, 예를 들면 잘못된 포맷으로 온 데이터를 정제하고, GeoIP로 지역을 구분하고, 개인정보를 제거하고, 날짜 포맷을 맞추거나, 타임존을 동일한 기준으로 변환하는 등의 작업이 있습니다. 그 다음 처리가 끝난 데이터를 각각 적합한 BigQuery 테이블로 찾아 넣어줍니다.
아직은 Dataflow에 대한 레퍼런스가 많은 편이 아니라서 개발 과정을 간단히 소개드리고자 합니다. 우선 개발 및 배포를 실행할 머신에 Google 클라우드 SDK 인증을 해야합니다. 배포되는 Apache Beam SDK를 이용해 소스코드를 작성한 뒤에 그 코드를 실행하면 됩니다. 개발자가 할 것은 이것이 전부이고, 이후는 SDK와 Google 클라우드가 분산 처리 후 실행까지 알아서 진행해줍니다. 그 과정은 다음과 같은데, Jar 파일이 생성되고, GCS에 업로드되고, 그다음 설정한 워커 수대로 GCE가 생성됩니다. 그리고 그 GCE에 Jar 파일이 배포되고, Google 클라우드 내부의 Dataflow Job 관리자가 해당 소스를 실행하면 스트리밍 서비스가 구동되어 올라갑니다.
위 과정에 따라 작성된 코드가 정상적으로 실행되고 있는지 로그를 조회할 필요가 있습니다. 당연하지만 생성된 다수의 GCE에서 발생한 로그들을 하나하나 추적해야하는 건 아니고 StackDriver라는 통합 로그 뷰어가 제공되어 간단하게 웹 콘솔 상에서 실시간으로 Dataflow 로그를 볼 수 있습니다.
이렇게 생성된 Dataflow Job은 유지보수를 위해 변경이 필요할 수 있습니다. 이를 위해 배포 시 기존 Dataflow Job을 갱신하는 업데이트 옵션이 있지만 파이프라인 그래프가 바뀌는 경우에는 사용이 불가합니다. 따라서 별도의 워크플로우를 만들어 동작시키고 있습니다.
Git에서 다운로드
Maven 빌드 실행
새로운 Dataflow Job을 생성(export)
새로운 Dataflow Job이 초기화되어 데이터가 실제 유입될 때까지 대기
기존의 Dataflow Job을 중지(drain)
시스템을 구축하기 위한 개발 초기 단계에서 몇가지 어려움을 겪었습니다. 그 첫번째 문제는 BigQuery 테이블에 동적으로 입력되지 않는 점이었습니다. 당시에는 별도로 구현해야 했는데, 지금은 Apache Beam 내에 BigqueryDynamicSink 모듈이 제공되어 이런 어려움이 없어졌습니다. 그외에 다음과 같은 문제가 있었습니다.
Jar 실행 파일 용량 제한
파이프라인 실행 파일(Jar Stub)의 용량이 10MB로 제한
실행 파일에 정적(static) 데이터를 포함하기 어려워 실행 시에 동적으로 GCS에서 로드하도록 구현
파이프라인 분기 그래프가 과도하게 크면 실행 불가
TableRow 객체를 BigQuery에 입력할 때 타입, 필드명 등 스키마에 오류가 있으면 치명적임
스키마 오류 시 무한 재시도로 성능 저하를 일으킴
입력 직전에 스키마 체크와 보정 로직을 구현하여 해결
2018년 현재는 재시도에 대한 옵션을 줄 수 있게 개선됨
그외에 개발 단계에서 다음과 같은 사항들도 알게 되었습니다.
각 ParDo는 단일 스레드
멀티 스레드로 동작할 것으로 생각하고 이를 고려하여 구현했지만(thread-safe) 그럴 필요가 없음
BigQuery 입력 시 사용하는 TableRow 클래스
값(value)이 직렬화(serializable) 객체이어야 함
JsonNode 오브젝트를 그대로 넣으면 런타임 에러
ParDo 객체는 Stub 변환됨
객체 내부의 멤버 변수 값은 모두 직렬화(serializable)되어야 함
객체는 상황에 따라 파기(destroy)되고 다시 생성되기도 함
정적(static) 변수 사용 시에 주의 필요
초기 실행 시 정적 변수에 값을 할당해도 배포 후 각 워커에서는 null 값을 가짐
싱글톤 패턴을 사용한다면 각 실행 머신에서 초기화 하는 형태(lazy loading)으로 구현해야 함
단일 코드이지만 실행되는 곳이 모두 다름
초기 실행 시는 실행한 PC 에서 작동
Pipeline.run() 실행 후의 코드는 Google 클라우드 VM 에서 작동
할당량(quota) 문제
Dataflow Job의 수가 늘어날수록 더 많은 Google 클라우드의 리전(region)을 선택할 수 있는데, 한 리전에서 사용할 수 있는 최대 머신 수가 제한됨
시스템을 구축하여 운영하던 초기에 어려운 점이 있었는데, 바로 Dataflow 자체의 버그로 인해 발생했던 일이었습니다. Dataflow Job을 변경하고 재시작하려고 했으나 중지(drain)가 완료되지 않은 상태가 되었습니다. 그래서 강제로 종료한 뒤에 그때까지 전송된 데이터를 검증해야 했습니다. 이 문제에 대해 StackOverflow의 Dataflow 그룹에 질문을 올리니, Google 엔지니어들이 직접 답을 해주었습니다. 그 답변은 “희귀한 버그에 걸린 것 같으며 수정하겠다”는 내용이었습니다. 당시에는 Dataflow가 베타 상태라서 이런 일이 있었던 것 같습니다. 약 2주 뒤에 해당 버그가 수정되었다는 답이 달렸고, 이제는 동일한 문제가 발생하지 않고 있습니다.
비용 문제도 신경쓰이는 부분이었는데, 오토스케일링의 한계(limit)를 설정하고, CPU 수는 최소로 하고, 워커를 늘리는 방식으로 비용을 절감하였습니다.
대량의 데이터가 전송될 때 CPU, 워커를 무제한으로 늘리며 과도한 비용이 발생하므로, 오토스케일링 설정에 한계를 설정해 적정한 수준을 유지해야함
단순한 JSON 로그 처리에는 CPU 수를 늘리는 것보다 2 CPU(n1-standard-2)의 워커 수를 늘리는 것이 것으로 효과적
배치 ETL 구성요소
신규 시스템을 실제 업무에 활용하기 위해서는 실시간 데이터 이외에 과거에 DB에 있던 데이터들을 모두 BigQuery에 적재되어야 했습니다. 이 작업은 병렬 업로드, 데이터 검증 등의 기능을 갖춘 Python 프로그램을 개발해서 처리했습니다.
이런 마이그레이션을 위한 툴은 오픈소스 프로젝트로 공개되어 있습니다. Embulk라는 소프트웨어가 그 대표적인 사례입니다. 당시에는 Embulk라는 것이 있는지 몰랐습니다. 나중에 우연히 Facebook의 BigQuery 그룹에 가입해서 알게 되었습니다. 많은 데이터 소스와 타겟에 대한 플러그인이 제공되고 속도도 굉장히 빠릅니다. 더 자세한 내용은 조대협 님의 블로그에도 자세한 설명을 보실 수 있습니다.
시스템이 완성된 후에 SQL Server에서 BigQuery로 옮기는 작업 외에도 BigQuery에서 집계한 결과를 MySQL로 전송하는 시스템이 필요했습니다. 그 밖에 각각 다른 데이터 소스로 데이터 이동이 필요했습니다. Embulk는 Fluentd 처럼 다양한 플러그인이 존재해서 아래와 같은 다양한 종류의 데이터 소스 간 전송을 매우 쉽게 처리할 수 있었고, 성능 또한 매우 좋아서 구지 별도의 프로그램을 개발할 필요가 없어 보였습니다.
Embulk를 이용하면 데이터 이동이 쉽게 해결될 것으로 예상했습니다. 그러나 실제 사용해보니 BigQuery를 데이터 소스로 사용하는 플러그인은 당시에 좀 불완전했습니다. 테이블 용량이 100만 줄이 넘으니 전체 데이터가 온전히 전송되지 않는 버그였습니다. 이 역시 플러그인 버그를 직접 수정했고, GitHub에 투고하여 해당 프로젝트의 컨트리뷰터가 되었습니다.
버그를 수정했지만 전송하는 데이터의 용량이 수백 GB 정도가 되니 그 성능이 매우 떨어졌습니다. 플러그인의 버그를 수정하면서 구조를 보니 충분히 그럴 수 있어 보였습니다. 그래서 대용량 전송을 위한 플러그인을 직접 개발했습니다. 이것을 GitHub에 투고했더니 Embulk 공식 사이트의 플러그인 목록에 등재되었습니다.
Digdag
애널리틱스에서 보여지는 지표 중에는 여러 단계를 거친 집계가 성공해야지만 최종적인 지표를 볼 수 있는 부분이 있습니다. 이를 위해 스케줄링, 순차 실행 제어, 실행 시간 제한, 자동 재시도, 부분 재시도, 에러 핸들링, 모니터링, 이력 관리 등의 작업을 자동으로 해주는 것이 워크플로우 엔진입니다. 오픈소스로 공개된 엔진은 여러가지가 있는데, 제일 유명한 것이 Airflow와 Luigi입니다. 필자도 Python을 좋아하지만 모든 워크플로우를 Python으로 기술하는 것과, Windows 기반의 개발 환경에서 구현된 워크플로우를 테스트할 수 없는 점도 불편했습니다. 이에 Digdag를 채택했습니다. 참고로 Digdag는 Fluentd와 Embulk를 만든 Treasure Data의 후루하시 사다유키 님이 가장 최근에 진행하고 있는 프로젝트입니다.
Windows 환경 지원
개발 환경과 운영 환경에서 동일하게 동작
YAML 로 태스크 정의
워크플로우 작성이 쉬움
작성된 코드의 가독성이 양호
워크플로우 엔진을 도입하면 이와 같이 다단계로 진행되는 많은 스케줄 작업들이 편리해집니다. 신규 시스템의 배치 ETL도 그 중에 하나인데, 레거시 시스템에서 발생한 JSON 파일들을 10분 단위로 BigQuery에 올리기 위한 스케줄을 Digdag로 관리합니다.
앞서 언급한 부분의 BigQuery 배치 업로드에도 Dataflow를 사용하는데, 이 때 참 유용한 것이 바로 Dataflow Template 기능입니다. 필자도 처음에는 이런 기능이 있는지 몰랐는데 함께 일하는 동료들이 본 시스템을 구축하면서 도입했습니다. 배포할 때마다 컴파일과 업로드를 다시 할 필요없이 GCS에 올려두고 사용할 수 있는 기능입니다. 덕분에 Dataflow에서 배치 처리를 매우 빠르게 구동할 수 있었습니다.
Digdag 사용 중에도 오픈소스 공헌을 하나 했습니다. 워크플로우에 Python 스크립트를 사용할 때 ‘virtualenv’을 지원하지 않았습니다. 이 부분을 직접 개발해서 투고했고, 0.9.31 버전에 채택되어 공식 사이트에 등재되었습니다.
마치며
마지막으로 BigQuery 기반으로 신규 시스템을 구축되고 운영한 이후 유관부서 담당자의 소감을 소개하겠습니다.
게임빌 A 팀장
“이제 다른 것은 못 쓸 것 같아요. 무엇보다 속도가 빨라서 좋습니다. 엄청난 속도 덕분에 기존 DB로는 어려웠던 유저들의 시계열 데이터에 대한 분석이 가능합니다. 이탈하기 전에는 어떤 행동들을 했는지, 또는 게임 설치 후 무엇부터 하는지 등이요. Data Studio, Google 스프레드시트 등 다른 Google 시스템과 연동이 쉬워서 좋습니다. 특히 별도의 BI 툴을 구매하지 않고, BigQuery와 DataStudio를 활용해 편리하게 대시보드를 생성하고 공유하고 있습니다.”
컴투스 B 팀장
“BigQuery 사용으로 인하여 원본 데이터 접근에 대한 부담감이 확실히 많이 줄어 들었습니다. 기존에는 원본에서 특정 게임만 추출하려면 몇시간이나 기다려야 했는데, 현재는 원본 데이터 조회도 몇 초 수준으로 빠르고, DB 부하에 대한 부담감도 없어서 원하는대로 쿼리를 작성할 수 있게 되었습니다. 이에 기존에는 보기 힘들었던 지표들을 제가 원하는대로 많이 추출할 수 있게 되어 업무 성과가 향상되었고, 많은 성취감도 느끼고 있습니다.”
웹개발, 게임서버 개발자를 거쳐 현재는 게임빌컴투스플렛폼에서 데이터 엔지니어를 하고 있는 10년차 프로그래머 입니다. 플랫폼 사용자분들이 HIVE 애널리틱스를 통해 놀라운 인사이트를 얻고 독보적 시장 경쟁력을 확보할 수 있도록 돕는 일을 하고 있습니다. 제가 하는 일의 가치가 제가 받는 월급 몇 배 이상의 가치가 있는지 항상 생각하며 자부심을 가질 수 있도록 노력중입니다.