ndc-2016



ndc-2016

0 0


ndc-2016

NDC 2016, <슈판워> 맨땅에서 데이터 분석 시스템 만들어나가기

On Github beingryu / ndc-2016

 

<슈판워> 맨땅에서 데이터 분석 시스템 만들어나가기

내가 그를 적재하기 전에는 그는 다만 하나의 레코드에 지나지 않았다 내가 그를 변환했을 때 그는 나에게로 와서 정보가 되었다

넥슨지티 창조기술팀

류원하 (wonha.ryu@nexon-gt.com)

2016년 4월 26일 NDC 2016에서 발표

Spacebar / Shift+Spacebar를 통해 조작하세요 (자세한 조작은 ?키로 확인)

소개

우린 누군가 또 무슨 이야기를 하려 하는가

<슈퍼 판타지 워>

  • 넥슨지티 자체 개발 세 번째 모바일 타이틀
  • 턴 방식 SRPG
  • Unity, Node.js, MySQL

창조기술팀

a.k.a. 해결사들

… 창조기술팀에서는 여러 게임 개발 프로젝트에서 겪고 있는 … 다양한 문제해결을 진행하며 그에 필요한 기반기술을 연구개발, 적용하는 일을 하고 있습니다. …

업무 범위에 제한은 없으나, 최근에 진행하였던 업무의 예는 다음과 같습니다.

  • 빅데이터 규모의 로그 및 DB 데이터 수집 가공, 분석 활용 시스템 개발
  • 게임 개발 프로젝트의 기술 스택에 대한 관리 및 기술적 의사결정 참여
  • 개발 지식의 정리, 보존, 공유

(채용공고 발췌)

목차

계기 (반복 시작) 어떤 상황이었나요? 어떻게 했나요? (반복 끝) 이것이 미래세계다 회고

참고: 기술 세션이며, 게임에 대한 데이터나 정보는 다루지 않습니다.

계기

글로벌 동시 런칭

2015년 11월 5일! 사실 소프트런칭을 하긴 했지만

흔한 프로젝트의 작업 우선순위

컨텐츠 구현 버그픽스 컨텐츠 구현 컨텐츠 구현 버그픽스 ……. 사후 분석을 위한 설계 및 기능 구현

출시 이후엔 데이터가 보고 싶어진다

  • 유저들의 컨텐츠 소비 속도 파악
  • 각종 커스텀 지표
  • 어뷰징 대응
  • "Data Exploration"

하지만 개발팀은 당연히 그럴 여유가 없다

창조기술팀 출동

네 그럼 저희가 분석할 수 있게 도와드리죠! 믿음직

팀 로고 갖고 싶어요…

어떤 상황이었나요?

첫 번째 시도에 대하여

DB! DB를 보자!

  • 정형 데이터베이스
  • 정형 로그 → RDBMS에 곱게 보관
  • 간단할 것으로 예상 쿼리 몇 개만 잘 짜면 되겠지

이건… 수평분할을 하는 맛이다

Global DB

UID GDB ID LDB ID 0 0 0 1 1 1 2 2 0 3 0 1

Game DB

GDB 0: GDB 1: GDB 2:

Log DB

LDB 0: LDB 1:

어 그럼 조인이 안되겠네?? N × M

Video courtesy of Star Trek the Next Generation

글로벌 동시 런칭…?

설상가상

한국 서버

아시아 서버

글로벌 서버

개척자 서버

어쩌면 좋지… N × M × K

Video courtesy of Star Trek the Next Generation

어떻게 했나요?

첫 번째 시도

One Instance to Rule Them All

한국 서버

아시아 서버

글로벌 서버

개척자 서버

MySQL들아 내게 힘을 모아줘

맞는 방향인지 의심스럽다

  • 가진 건 full snapshot backup뿐
  • EC2 한 인스턴스에 mysqld_multi로 DB 인스턴스 14개 생성
  • 제대로 가고 있는 거 맞나…

끝나지 않는 복원 작업

14 × (압축 해제 + 복원 + 고유 ID 변경캐릭터, 장비 ID + 내보내기 + 불러오기)

분석은 할 수 있기는 했다

어쨌든 쿼리는 날릴 수 있으니까…

왜 잘 안됐나요?

첫 번째 시도에 대하여

복원하는 데 며칠 걸렸을까요?

≈ 4일!

  • 그제서야 데이터를 적재했을 뿐, 아무런 분석도 하지 못한 상태 인덱스도 별로 없는데…
  • 정보에는 유통기한이 있다
    • 런칭 직후일수록 더더욱 중요

제가 수를 세는 법

0개, 1개, 2개, 많다ㅋㄲㅈㅁ

이건 두 번도 못해 안돼

  • 멘탈에도 극도로 안 좋았고, 시간도 많이 썼다
    • 창조기술팀 모토: "우리의 멘탈은 비싼 자원이다"
  • 이걸 일별로 증분적으로 할 수 있을까?
    • 쌓이는 속도?
    • DB 크기? 이미 꽤 컸음
  • 기껏 분할한 걸 다시 합치다니?
  • 나는 누군가 여긴 또 어딘가

이렇게 된 이상 Spark로 간다

배운 게 도둑질입니다

누구나 자신의 능력과 경험을 바탕으로 문제를 풀 뿐입니다.

— 류원하

어떻게 했나요?

좌충우돌 Spark 분투기

일단 다 가져와서 저장해보자

Spark ❤ Parquet

오르막 주의

"빅 데이터" 생태계 이야기…

Apache Spark™ is a fast and general engine for large-scale data processing.

Apache Spark (cont.)

  • 컴퓨팅 프레임워크: MR의 대체재?
  • Scala/JVM 기반 PySpark도 있음
  • 핵심 개념: "Resilient Distributed Datasets" (RDD)
    • In-memory, DAG-based computing
val lines = sc.textFile("data.txt")
val lineLengths = lines.map(s => s.length)
val totalLength = lineLengths.reduce((a, b) => a + b)

Spark SQL

  • Pig Latin, HiveQL처럼 구조화된 데이터를 편리하게 다루기
  • DataFrameRow 기반 런타임 조작 & Dataset1.6에 나온 새 API; Strongly typed
  • 강력한 쿼리 엔진: Catalyst + Plan 최적화 엔진
    • Predicate pushdown, Bytecode compilation 등
val df = context.read.format("json").load("s3n://...")
df.registerTempTable("people")
val names = context.sql("SELECT name FROM people WHERE age >= 42")
val names = df.where("age >= 42").select("name") // this works too
names.write.format("parquet").save("s3n://.../names.parquet")

Apache Parquet is a columnar storage format available to any project in the Hadoop ecosystem, regardless of the choice of data processing framework, data model or programming language.

  • ORC와 유사한 열 단위로 저장하는 파일 규격
  • 단순한 타입 몇 개와 blob, 그리고 중첩 구조
  • 인코딩과 압축 지원

Spark + Parquet

// Sample DataFrame schema
root
 |-- idx: long (nullable = true)
 |-- reg_date: timestamp (nullable = true)
 |-- user_idx: long (nullable = true)
 |-- action_type: integer (nullable = true)
 ...
  • 모든 로그를 가져다가 Parquet으로 저장 직접 구현했으나 Apache Sqoop도 있습니다
  • 인덱싱 X, 압축 O → 그럭저럭 작아진 용량
  • 데이터가 작으니 로컬 파일시스템에 저장합시다 S3/EMRFS는 일단 보류
    • Hive 스타일 파티셔닝: user_part=42/time_ym=201604/time_d=26/*.parquet
    • 적당한 데이터 리텐션 정책

그 파일이 개발자가 보기에 좋았노라

spark-shell: Spark-powered Scala REPL Shell

scala> val df = sqlContext.read.parquet(".../log_pvp/gl")
df: org.apache.spark.sql.DataFrame = [lord_idx: bigint, ...]
scala> df.registerTempTable("pvp")
scala> val win_df = sqlContext.sql(
    "SELECT lord_idx, SUM(is_win) AS win_count FROM pvp
     WHERE time_ym = 201604 AND time_d = 26 AND is_win = 1
     GROUP BY lord_idx")
win_df: org.apache.spark.sql.DataFrame = [lord_idx: bigint, win_count: bigint]
scala> win_df.sort($"win_count".desc).limit(10).collect()
res1: Array[org.apache.spark.sql.Row] = Array([12345, 42], [23456, 41], ...)

그런데, 왜 잘 안됐나요?

분석 병목: 데이터 엔지니어

  • Spark SQL ≠ SQL
  • 시스템에 대한 이해 없이 접근 불가능한 자료들
  • 데이터 엔지니어가접니다 그때그때 요구사항에 맞춰 데이터를 받아 내려주는 흐름

이상: Data Exploration

  • 데이터를 바탕으로 이것저것 뒤적여 봐야 정보와 직관을 얻는다
  • 개발자는 시스템 구축해주기도 바쁘므로, 탐색은 전문가가 해야 한다
  • 필요한 내용이 고정되면스펙이 나오면 정례화를 위한 로직은 엔지니어가 짜면 된다
  • 날것의 데이터는 엑셀각은 아니라 어쩔 수 없다 엑셀, 어디까지 가 봤니?

자동화! 자동화가 필요하다!

시스템 엔지니어의 최종 목표: 시스템에서 '나'를 제거하기…?

어떻게 했나요?

현재 상황

일별 배치 작업

  • 완전 날것의 데이터는 탐색/분석 불가, 임의의 aggregation
  • 로그를 긁어 오듯, 매일 배치 작업
    • 실시간까지는 일단 무리
  • 일단 그 결과도 똑같이 Parquet-ify
  • 최근 N일간의 데이터는 RDBMS넵MySQL에 적재
  • 개별 작업은 Scala로, 작업 관리자는 Ruby로 구현
    • Spark 리소스 관리 문제

BI: Business Intelligence

약은 약사에게 분석은 분석가에게

좋은 도구, 좋은 결과물

Jupyter같은 걸 쥐어주기엔 너무 어렵다,BI 도구들 가운데서 힘세고 강한 도구!를 찾자

  • Tableau
  • Qlik
  • SAP …
  • SAS …
  • Oracle …
  • IBM …
  • Microsoft …

  • 테이블 형태의 structured data에 강함
  • 엑셀과 유사한 수식 및 함수
  • 개념적으로 다소 어려운 부분이 있으나 비교적… 분석가 친화적
  • 다른 곳에서도 은근 많이 쓴다 카더라 GDC 2016, <Making "Big Data" Work for 'Halo': A Case Study>

Tableau로 데이터 불러들이기

  • Spark SQL Connector: 버전 문제로 실패
  • Hive: 언젠가 해야 하지만 지금 세팅하고 싶지는 않음이미 너무 밀렸어
  • 그냥 RDBMS넵MySQL에 쏟아넣자…
    • Tableau라고 수 GB 단위로 데이터를 쉽게 내려받을 수 있는 건 아님
  • 하는 김에 게임DB 정보도 일정 주기로 복제
  • 게임DB + 로그 + 일별 배치 작업 결과 → RDBMS → Tableau 까지 ETL 파이프라인
    • Extract
    • Transform
    • Load

지금까지 했던 일들 정리

  • 최초에 삽질도 좀 있었지만,
  • MySQL에 쌓은 정형 로그를 바탕으로
  • 주기적으로 해당 로그를 가져가 파일 포맷을 변환해 저장하고
  • 일별로 배치 작업을 돌려 그 결과를 가지고
  • 전문 데이터 분석 도구를 붙여 데이터를 탐색하고 직관을 발견했다

자동화의 함정: 이론

자동화의 함정: 실제

중간 결론

완벽한 솔루션을 구축한 것은 아니지만,최소한의 노력으로 최대한의 결과를 내려 노력

Comics courtesy of XKCD, https://xkcd.com/1319/

아무튼 그들은 오래오래 행복하게

일했으면 좋았을텐데

앞으로는?

내 데이터가 이렇게 귀여울 리 없어

더 큰 서비스를 마주하게 된다면?

그래서 좀 더 제대로 시스템을 구축해야 한다면?

최초 분석을 빨리 진행해서 더 나은 액션을 취할 수 있었다면?

본격 빅데이터 아키텍처 설계안입코딩

Image courtesy of Camelia.boban, https://commons.wikimedia.org/wiki/File:BigData_2267x1146_white.png under CC BY-SA 3.0

이것이 미래세계다! 생성편

랜덤한 설계 아이디어들

  • 정형 로그 = ❤, 비정형 로그 = 👍
  • 시계열 구간도 필요 시작과 끝
    • 세션, 인던 플레이, …
  • 게임 서버로서의 서비스와 게임 서버로서의 서비스
  • 직렬화 방식에 대한 고민
    • JSON의 타입은 나쁘지만 못 쓸 정도는 아닌데…?

좋은 로깅 라이브러리가 필요하지 않을까

아주 복잡할 필요는 없지만,

  • 유형에 따라: 정형, 반정형, 비정형
  • 우선순위에 따라: Guaranteed, Best-effort
  • 기능에 따라: 클라이언트, 서버, …
  • 직렬화 규격에 따라: Text/JSON, msgpack, …
// structured
ServerLogger.logAction(ctx, [&](auto x){ return x.userId(...).action(...); });
// semi-structured
ServerLogger.logs(ctx, "action",
    [&](auto x){ return x.field("userId", ...).field("action", ...); });
// unstructured
ServerLogger.logf(ctx, "user %d trying to do %s", ...);

이것이 미래세계다! 수집편

이것이 미래세계다! 적재편

왜 추천하나요?

  • 텍스트 로그의 편의성
    • 모든 로그를 Parquet으로 저장할 필요까지는 없더라
  • Snappy Framing Format
    • Hadoop에서 사용 가능한 분산 처리 가능 압축 포맷 (단점: 직접 구현해야…)
    • LZO는 GPL, bz2는 구림
  • 프로젝트에 맞는 조정이 필요
    • 로그 유형이 다양한 RPG라면 로그 타입순으로 정렬하기보다는 UID로
      • 까짓거 둘 다 하면 되지용량이 두 배일 뿐
    • 실시간 적재도 프로젝트에 따라
  • 아무튼 전체적으로 완성도 높은 디자인!

이것이 미래세계다! 분석편

분석가-엔지니어 피드백 루프가 중요

  • 대체로 분석가는 코드를 쓸 수 없고, 엔지니어는 분석이 하기 싫음
  • 분석가는 계속 데이터의 동향을 파악해야 함
    • ML의 한계: 데이터셋의 특성이 바뀌면 모델이 바뀌어야
  • 분석가의 요구사항은 계속 바뀌고 자기도 잘 모름
  • 분석가에게 최대한의 자유도를 주는 방법을 추구하고픔
  • …결국 데이터셋을 그대로 주는 편이 낫지 않나?

약은 약사에게, 분석은 분석가에게

툴이나 데이터 소스의 형태가 중요한 것이 아니라 피드백 루프가 중요 사람의 문제

정리 및 교훈

정리: 우리는 무엇을 했는가 (repeat)

  • 최초에 삽질도 좀 있었지만,
  • MySQL에 쌓은 정형 로그를 바탕으로
  • 주기적으로 해당 로그를 가져가 파일 포맷을 변환해 저장하고
  • 일별로 배치 작업을 돌려 그 결과를 가지고
  • 전문 데이터 분석 도구를 붙여 데이터를 탐색하고 직관을 발견했다

Spark 적용 소감

  • Spark SQL + Parquet = Awesome!
    • DataFrame API는 강한 타입 기반 언어인 Scala와 뭔가 어설픈 조합이지만…
  • 디버깅이 쉬운 환경은 아니다
    • 작업이 죽으면 태스크인지 드라이버인지 구분하라
    • 특히 OOM은 자주 마주할 운명이다
    • 너무 긴 RDD 체인은 좋지 않다
  • 리소스 관리를 너무 잘 하려고 하지 마라
    • JVM 기반이라 어쩔 수 없는 부분들이 있다

전체 분석 과정에서 얻은 교훈

  • 무에서 유를 창조할 순 없더라:슈판워의 경우 믿을만한 로그들을 쌓고 있었기에 성공적인 분석을 할 수 있었다.
  • 모래밭에 무언가를 지을 순 없더라:슈판워의 경우 RDBMS에 저장하고 있었기에 단단한 데이터 공급원이 있었다.
  • 관리되지 않는 로그는 없느니만 못한 것 같더라:개발 초기에 만들고 방치한 깨진 창문은 로그일지라도 위험하다.
  • '업적'은 훌륭한 정보더라:특정 업적들의 보유 여부와 시점은 이미 여러가지 분석을 돌린 결과물이나 다름없다.
  • unique는 정말 unique해야 하더라:수평분할을 한다면 시작 키를 다르게, 랜덤한 값은 충분히 커야 한다. Birthday Paradox

참고 자료

If I have seen further, it is by standing on the shoulders of giants.

EOD

감사합니다

Q & A

  <슈판워> 맨땅에서 데이터 분석 시스템 만들어나가기 내가 그를 적재하기 전에는 그는 다만 하나의 레코드에 지나지 않았다 내가 그를 변환했을 때 그는 나에게로 와서 정보가 되었다 넥슨지티 창조기술팀 류원하 (wonha.ryu@nexon-gt.com) 2016년 4월 26일 NDC 2016에서 발표 Spacebar / Shift+Spacebar를 통해 조작하세요 (자세한 조작은 ?키로 확인)