Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

[NDC18] 야생의 땅 듀랑고의 데이터 엔지니어링 이야기: 로그 시스템 구축 경험 공유 (2부)

1,847 views

Published on

NDC18에서 발표하였습니다. 현재 보고 계신 슬라이드는 2부 입니다.(총 2부)
- 1부 링크: https://goo.gl/3v4DAa
- 2부 링크: https://goo.gl/wpoZpY
(SlideShare에 슬라이드 300장 제한으로 2부로 나누어 올렸습니다. 불편하시더라도 양해 부탁드립니다.)

Published in: Data & Analytics
  • Be the first to comment

[NDC18] 야생의 땅 듀랑고의 데이터 엔지니어링 이야기: 로그 시스템 구축 경험 공유 (2부)

  1. 1. 〈야생의 땅: 듀랑고〉의 데이터 엔지니어링 이야기 넥슨 컴퍼니 왓 스튜디오 전효준 : 로그 시스템 구축 경험 공유 2부
  2. 2. SlideShare에 슬라이드 300장 제한으로 부득이하게 2부로 나누어 올렸습니다. 현재 보고 계신 슬라이드는 2부입니다.
  3. 3. 분석 마지막으로 이렇게 S3에 적재한 로그를 분석하는 단계인데요.
  4. 4. 분석 클러스터 S3 ZeppelinSpark AWS EMR DataPipeline 전체적으로 Apache Spark와 Apache Zeppelin을 사용하고 있습니다.
  5. 5. 분석 클러스터 S3 ZeppelinSpark AWS EMR 클러스터 컴퓨팅 엔진 DataPipeline Apache Spark는 클러스터 컴퓨팅 엔진으로
  6. 6. 분석 클러스터 S3 ZeppelinSpark AWS EMR 대화식 인터프리터 + 쉬운 시각화 DataPipeline Zeppelin은 이런 Spark를 대화식 인터프리터에서 사용하게 해주고
  7. 7. 분석 클러스터 S3 ZeppelinSpark AWS EMR 대화식 인터프리터 + 쉬운 시각화 DataPipeline 결과를 시각화하는 용도로 사용하고 있고요.
  8. 8. 분석 클러스터 S3 ZeppelinSpark AWS EMR 클러스터 구성 DataPipeline 이러한 Spark와 Zeppelin의 클러스터를 구성하는 데에
  9. 9. 분석 클러스터 S3 ZeppelinSpark AWS EMR 클러스터 구성 DataPipeline EMR이라는 서비스를 사용하고 있습니다.
  10. 10. 분석 클러스터 S3 ZeppelinSpark AWS EMR DataPipeline 배치잡 구성 그리고 여기 옆에 DataPipeline은 배치잡을 구성하는 데 사용하고있습니다.
  11. 11. ZeppelinSpark AWS EMR 먼저 Spark와 Zeppelin, 그리고 이것을 구성하는 EMR에 대해서 말씀드리려고 합니다.
  12. 12. • 클러스터 컴퓨팅 엔진 • • Python, Scala, R, Java 언어지원 • 다양한 모듈 지원 (SparkSQL, MLlib 등) Apache Spark Spark 인 메모리 데이터 처리 Apache Spark는 클러스터 컴퓨팅 엔진인데요.
  13. 13. • 클러스터 컴퓨팅 엔진 • • Python, Scala, R, Java 언어지원 • 다양한 모듈 지원 (SparkSQL, MLlib 등) Apache Spark Spark 인 메모리 데이터 처리 → 빠르다! 이전에 Hadoop에서 디스크 기반으로 수행하던 MapReduce 작업을
  14. 14. • 클러스터 컴퓨팅 엔진 • • Python, Scala, R, Java 언어지원 • 다양한 모듈 지원 (SparkSQL, MLlib 등) Apache Spark Spark 인 메모리 데이터 처리 → 빠르다! Spark에서는 메모리에서 처리하기 때문에
  15. 15. • 클러스터 컴퓨팅 엔진 • • Python, Scala, R, Java 언어지원 • 다양한 모듈 지원 (SparkSQL, MLlib 등) Apache Spark Spark 인 메모리 데이터 처리 → 빠르다! 빠른 속도라는 장점을 가지고 있습니다.
  16. 16. • 클러스터 컴퓨팅 엔진 • • Python, Scala, R, Java 언어지원 • 다양한 모듈 지원 (SparkSQL, MLlib 등) Apache Spark Spark 인 메모리 데이터 처리 → 빠르다! 다양한 언어를 지원하기도 하고
  17. 17. • 클러스터 컴퓨팅 엔진 • • Python, Scala, R, Java 언어지원 • 다양한 모듈 지원 (SparkSQL, MLlib 등) Apache Spark Spark 인 메모리 데이터 처리 → 빠르다! 데이터에 대하여 SQL을 사용할 수 있는 SparkSQL 모듈과
  18. 18. • 클러스터 컴퓨팅 엔진 • • Python, Scala, R, Java 언어지원 • 다양한 모듈 지원 (SparkSQL, MLlib 등) Apache Spark Spark 인 메모리 데이터 처리 → 빠르다! 머신러닝 라이브러리, 스트리밍 모듈 등 여러가지 모듈을 지원하는데
  19. 19. • 클러스터 컴퓨팅 엔진 • • Python, Scala, R, Java 언어지원 • 다양한 모듈 지원 (SparkSQL, MLlib 등) Apache Spark Spark 인 메모리 데이터 처리 → 빠르다! 클러스터 컴퓨팅 엔진이라고 소개해 드리긴 했지만
  20. 20. • 클러스터 컴퓨팅 엔진 • • Python, Scala, R, Java 언어지원 • 다양한 모듈 지원 (SparkSQL, MLlib 등) Apache Spark Spark 인 메모리 데이터 처리 → 빠르다! 범용 분산 플랫폼이 맞는 표현입니다.
  21. 21. df = spark.read.json('logs.json') over_21 = df.where('age >= 21').count() 보고 계신 코드는 Spark에서 JSON 포맷의 로그를 읽고
  22. 22. df = spark.read.json('logs.json') over_21 = df.where('age >= 21').count() 나이가 21세 이상인 사람들에 대한 로그를 카운트하는 예제입니다.
  23. 23. df = spark.read.json('logs.json') over_21 = df.where('age >= 21').count() Spark에서 저희가 주로 사용하는 데이터 프레임이라는 데이터 구조를 사용한 코드인데요.
  24. 24. df = spark.read.json('logs.json') over_21 = df.where('age >= 21').count() 실제로 보시는 바와 같이 쉽게 사용할 수 있습니다.
  25. 25. • • 다양한 인터프리터 사용가능 • 쉬운 시각화 Apache Zeppelin Zeppelin 대화식 분석이 가능한 웹 기반 노트북 다음으로는 이런 Spark 사용을 쉽게해주는 Apache Zeppelin입니다.
  26. 26. • • 다양한 인터프리터 사용가능 • 쉬운 시각화 Apache Zeppelin Zeppelin 대화식 분석이 가능한 웹 기반 노트북 대화식 분석이 가능하기 때문에
  27. 27. • • 다양한 인터프리터 사용가능 • 쉬운 시각화 Apache Zeppelin Zeppelin 대화식 분석이 가능한 웹 기반 노트북 파이썬으로 데이터 분석하시는 분들께서 많이 사용하시는 Jupyter notebook 처럼
  28. 28. • • 다양한 인터프리터 사용가능 • 쉬운 시각화 Apache Zeppelin Zeppelin 대화식 분석이 가능한 웹 기반 노트북 파라미터를 바꿔가면서 결과를 보면서 작업을 이어갈 수 있고
  29. 29. • • 다양한 인터프리터 사용가능 • 쉬운 시각화 Apache Zeppelin → Spark! Zeppelin 대화식 분석이 가능한 웹 기반 노트북 다양한 인터프리터를 사용할 수 있기 때문에
  30. 30. • • 다양한 인터프리터 사용가능 • 쉬운 시각화 Apache Zeppelin → Spark! Zeppelin 대화식 분석이 가능한 웹 기반 노트북 Spark 인터프리터부터, 각종 언어들, JDBC 등 여러 가지를 연동하여 사용할 수 있습니다.
  31. 31. • • 다양한 인터프리터 사용가능 • 쉬운 시각화 Apache Zeppelin → Spark! Zeppelin 대화식 분석이 가능한 웹 기반 노트북 마지막으로 가장 유용하다고 생각하는 기능 중 하나인 시각화인데요.
  32. 32. • • 다양한 인터프리터 사용가능 • 쉬운 시각화 Apache Zeppelin → Spark! Zeppelin 대화식 분석이 가능한 웹 기반 노트북 별다른 처리 없이도 결과에 따라서 잘 알아서 시각화를 해줍니다.
  33. 33. SparkSQL 보시는 것은 실제 Zeppelin의 사용 모습입니다.
  34. 34. SparkSQL 이렇게 Spark SQL 쿼리로 분석할 수 있고 (쿼리 내용 중요하지 않음)
  35. 35. 다이나믹 폼 사용가능 실제 쿼리의 조건절에 들어가는 값을 다양하게 바꿀 수 있는 다이나믹 폼도 지원합니다.
  36. 36. 다양한 그래프 또한, 단순 테이블, 막대 그래프, 파이 그래프 등 여러 가지 그래프를 지원하고요.
  37. 37. CSV 다운로드 결과를 CSV로 다운로드할 수 있습니다.
  38. 38. 스케줄링 그리고 노트북을 특정 시간대에 자동으로 실행하도록
  39. 39. 스케줄링 스케줄링이 가능합니다.
  40. 40. 스케줄링 보시는 바와 같이 Cron Expression으로 자동실행 스케줄을 지정할 수 있습니다.
  41. 41. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR 이렇게 Apache Spark와 Apache Zeppelin의 클러스터를 구성하는 데에는
  42. 42. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR AWS의 EMR 서비스를 사용하고 있습니다.
  43. 43. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR + Zeppelin Spark 클러스터를 클릭 몇 번으로 쉽게 구성가능하고
  44. 44. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR + Zeppelin 여기에 Zeppelin도 같이 구성할 수 있습니다.
  45. 45. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR + Zeppelin 게다가 AWS에서 유휴자원을 싼 가격에 사용할 수 있는
  46. 46. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR + Zeppelin 스팟 인스턴스도 사용가능한데요.
  47. 47. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR + Zeppelin 도쿄 리전 r4.4xlarge 기준으로 4분의 1가격에 사용가능합니다.
  48. 48. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR + Zeppelin 또한 쉽게 확장 및 축소가 가능하다는 것도 장점입니다.
  49. 49. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR + Zeppelin 10개 인스턴스로 5분 1개 인스턴스로 50분 아주 단순하게 생각했을 때
  50. 50. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR + Zeppelin 10개 인스턴스로 5분 1개 인스턴스로 50분 10개 인스턴스로 5분동안 처리하는 것과
  51. 51. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR + Zeppelin 10개 인스턴스로 5분 1개 인스턴스로 50분 1개 인스턴스로 50분동안 처리하는 것이
  52. 52. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR + Zeppelin 10개 인스턴스로 5분 1개 인스턴스로 50분 비용 만약 같은 비용이라면
  53. 53. • Spark 클러스터를 손쉽게 구성 • 스팟 인스턴스 사용가능 • 쉬운 확장 및 축소 AWS EMR EMR + Zeppelin 10개 인스턴스로 5분 1개 인스턴스로 50분 비용 당연히 많은 인스턴스로 짧은 시간 동안 처리하는 게 훨씬 이득이겠죠.
  54. 54. 정기적인 배치 잡은 어떻게 실행하나요? 하지만 정기적으로 필요한 분석을 위한 배치잡은 어떻게 실행하는 지 궁금하실 수 있는데요.
  55. 55. Zeppelin의 노트북 스케줄 기능 ? 여기서 아까 보셨던 Zeppelin의 노트북 스케줄 기능을 생각하실 수 있습니다.
  56. 56. Zeppelin의 노트북 스케줄 기능 ? 실패 시 자동 재시도 완료 시 클러스터 반납 하지만 Zeppelin은 실패시 자동으로 재시도하거나
  57. 57. Zeppelin의 노트북 스케줄 기능 ? 실패 시 자동 재시도 완료 시 클러스터 반납 완료 시 인스턴스를 반납하는 것과 같은 동작은 할 수 없기 때문에
  58. 58. Zeppelin의 노트북 스케줄 기능 ? 실패 시 자동 재시도 완료 시 클러스터 반납 Ad-hoc 분석용으로만 사용 저희는 Zeppelin을 Ad-hoc 분석용으로만 사용하고 있습니다.
  59. 59. DataPipeline 그 대안으로 AWS의 DataPipeline이라는 서비스를 사용하고 있습니다.
  60. 60. 보시는 것은 DataPipeline 서비스를 AWS 콘솔에서 봤을 때의 모습인데요.
  61. 61. 다이어그램을 간단히 설명드리면
  62. 62. 매일 00시에 매일 특정 시간에
  63. 63. 매일 00시에 클러스터를 실행하고 EMR 클러스터를 띄우고
  64. 64. 매일 00시에 클러스터를 실행하고 특정 Job을 수행해라특정 Job을 수행해라라는 의미인데요.
  65. 65. 매일 00시에 클러스터를 실행하고 특정 Job을 수행해라실제로 Job이 실패했을 때 재시도하거나
  66. 66. 매일 00시에 클러스터를 실행하고 특정 Job을 수행해라실행이 끝났을 때 클러스터를 모두 종료할 수 있기 때문에
  67. 67. 매일 00시에 클러스터를 실행하고 특정 Job을 수행해라비용을 절감할 수 있습니다.
  68. 68. 분석 클러스터 S3 ZeppelinSpark AWS EMR DataPipeline 이렇게 저희의 분석 클러스터가 어떻게 구성되었는지 말씀드렸는데요.
  69. 69. 분석 클러스터 S3 ZeppelinSpark AWS EMR DataPipeline 마지막으로 전체 아키텍처를 정리를 해보면
  70. 70. 게임 서버 Fluentd S3Lambda Lambda Kinesis ES ZeppelinSpark AWS EMR DataPipeline 다음과 같이 표현될 수 있습니다.
  71. 71. S3Lambda ZeppelinSpark AWS EMR DataPipeline 게임 서버 Fluentd Lambda Kinesis ES 5초 이내 1. 로그를 확인하기 까지 로그를 확인하는 데 까지 5초 이내로 확인할 수 있고
  72. 72. S3Lambda Lambda ES ZeppelinSpark AWS EMR DataPipeline 게임 서버 Fluentd 2. 로그 유입량이 증가하면? Kinesis 확장 포인트 만약 로그 유입량이 증가하더라도
  73. 73. S3Lambda Lambda ES ZeppelinSpark AWS EMR DataPipeline 게임 서버 Fluentd 2. 로그 유입량이 증가하면? Kinesis 확장 포인트 Kinesis의 샤드 개수만 증가시키면 됩니다.
  74. 74. S3Lambda Lambda ES ZeppelinSpark AWS EMR DataPipeline 게임 서버 Fluentd 2. 로그 유입량이 증가하면? Kinesis 확장 포인트 아, Fluentd는 저희 같은 경우 아까 말씀드렸다시피
  75. 75. S3Lambda Lambda ES ZeppelinSpark AWS EMR DataPipeline 게임 서버 Fluentd 2. 로그 유입량이 증가하면? Kinesis 확장 포인트 각각의 호스트에 하나씩 존재하므로 호스트 개수만큼 알아서 확장하는 구조입니다.
  76. 76. • 도쿄 리전 기준 • 초당 1000개 로그 유입, 각 로그는 2KB(월 5TB 정도) • Kinesis 샤드 2개 + 데이터 PUT 비용 • Lambda (처리시간 1초) → 약 $90 → 약 $20 3. Kinesis + Lambda 월 유지비용 → 월 $110 정도로 유지 비용(저장비용 제외)
  77. 77. • 도쿄 리전 기준 • 초당 1000개 로그 유입, 각 로그는 2KB(월 5TB 정도) • Kinesis 샤드 2개 + 데이터 PUT 비용 • Lambda (처리시간 1초) → 약 $90 → 약 $20 3. Kinesis + Lambda 월 유지비용 → 월 $110 정도로 유지 비용(저장비용 제외) 여기서 잠시 로그 수집 파이프라인의 비용을 살펴보면
  78. 78. • 도쿄 리전 기준 • 초당 1000개 로그 유입, 각 로그는 2KB(월 5TB 정도) • Kinesis 샤드 2개 + 데이터 PUT 비용 • Lambda (처리시간 1초) → 약 $90 → 약 $20 3. Kinesis + Lambda 월 유지비용 → 월 $110 정도로 유지 비용(저장비용 제외) 만약에 초당 1000개의 로그가 지속적으로 유입되고
  79. 79. • 도쿄 리전 기준 • 초당 1000개 로그 유입, 각 로그는 2KB(월 5TB 정도) • Kinesis 샤드 2개 + 데이터 PUT 비용 • Lambda (처리시간 1초) → 약 $90 → 약 $20 3. Kinesis + Lambda 월 유지비용 → 월 $110 정도로 유지 비용(저장비용 제외) 초당 2MB정도 유입된다고 가정하면
  80. 80. • 도쿄 리전 기준 • 초당 1000개 로그 유입, 각 로그는 2KB(월 5TB 정도) • Kinesis 샤드 2개 + 데이터 PUT 비용 • Lambda (처리시간 1초) → 약 $90 → 약 $20 3. Kinesis + Lambda 월 유지비용 → 월 $110 정도로 유지 비용(저장비용 제외) 월 5TB정도의 로그가 발생하는데요.
  81. 81. • 도쿄 리전 기준 • 초당 1000개 로그 유입, 각 로그는 2KB(월 5TB 정도) • Kinesis 샤드 2개 + 데이터 PUT 비용 • Lambda (처리시간 1초) → 약 $90 → 약 $20 3. Kinesis + Lambda 월 유지비용 → 월 $110 정도로 유지 비용(저장비용 제외) 이 때 최소로 필요한 Kinesis 샤드 2개와
  82. 82. • 도쿄 리전 기준 • 초당 1000개 로그 유입, 각 로그는 2KB(월 5TB 정도) • Kinesis 샤드 2개 + 데이터 PUT 비용 • Lambda (처리시간 1초) → 약 $90 → 약 $20 3. Kinesis + Lambda 월 유지비용 → 월 $110 정도로 유지 비용(저장비용 제외)데이터를 기록하는 비용, Lambda에서 처리하는 비용을 모두 합쳐도
  83. 83. • 도쿄 리전 기준 • 초당 1000개 로그 유입, 각 로그는 2KB(월 5TB 정도) • Kinesis 샤드 2개 + 데이터 PUT 비용 • Lambda (처리시간 1초) → 약 $90 → 약 $20 3. Kinesis + Lambda 월 유지비용 → 월 $110 정도로 유지 비용(저장비용 제외) 월 110달러 정도의 유지 비용이 발생합니다.
  84. 84. • 도쿄 리전 기준 • 초당 1000개 로그 유입, 각 로그는 2KB(월 5TB 정도) • Kinesis 샤드 2개 + 데이터 PUT 비용 • Lambda (처리시간 1초) → 약 $90 → 약 $20 3. Kinesis + Lambda 월 유지비용 → 월 $110 정도로 유지 비용(저장비용 제외)물론 이건 저장비용을 제외하고, 최소로 맞춘 스펙이니 참고만 부탁드립니다.
  85. 85. 게임 서버 Fluentd S3Lambda Lambda Kinesis ES DataPipeline 4. 빠르게 분석하고 싶을 때 확장 포인트 ZeppelinSpark AWS EMR 분석을 빠르게 하고 싶을 때는
  86. 86. 게임 서버 Fluentd S3Lambda Lambda Kinesis ES DataPipeline 4. 빠르게 분석하고 싶을 때 확장 포인트 ZeppelinSpark AWS EMR EMR 클러스터만 확장해주면 됩니다.
  87. 87. 5. 대규모 분석 비용 • 모든 로그에 대하여 • r4.8xlarge X 10 스팟 인스턴스, 도쿄 리전 기준 • 생성된 모든 섬의 개수 • 가장 많이 채집된 자원(10위까지) • 모든 섬의 날짜별 일일 활성 유저수 → 5분 = $1 미만 → 10분 = 약$1 → 15분 = 약 $ 1.3 프로비저닝 시간 제외, 2018년 4월 24일 기준 만약 대규모 분석을 위해서
  88. 88. 5. 대규모 분석 비용 • 모든 로그에 대하여 • r4.8xlarge X 10 스팟 인스턴스, 도쿄 리전 기준 • 생성된 모든 섬의 개수 • 가장 많이 채집된 자원(10위까지) • 모든 섬의 날짜별 일일 활성 유저수 → 5분 = $1 미만 → 10분 = 약$1 → 15분 = 약 $ 1.3 프로비저닝 시간 제외, 2018년 4월 24일 기준 도쿄 리전을 기준으로 r4.8xlarge 타입의 인스턴스를 스팟 인스턴스로 10개를 띄우고
  89. 89. 5. 대규모 분석 비용 • 모든 로그에 대하여 • r4.8xlarge X 10 스팟 인스턴스, 도쿄 리전 기준 • 생성된 모든 섬의 개수 • 가장 많이 채집된 자원(10위까지) • 모든 섬의 날짜별 일일 활성 유저수 → 5분 = $1 미만 → 10분 = 약$1 → 15분 = 약 $ 1.3 프로비저닝 시간 제외, 2018년 4월 24일 기준 분석이 끝나는 즉시 종료한다면
  90. 90. 5. 대규모 분석 비용 • 모든 로그에 대하여 • r4.8xlarge X 10 스팟 인스턴스, 도쿄 리전 기준 • 생성된 모든 섬의 개수 • 가장 많이 채집된 자원(10위까지) • 모든 섬의 날짜별 일일 활성 유저수 → 5분 = $1 미만 → 10분 = 약$1 → 15분 = 약 $ 1.3 프로비저닝 시간 제외, 2018년 4월 24일 기준 보시는 바와 같이 시간과 비용을 최대한 절감할 수 있습니다.
  91. 91. #4 ‘잘’ 사용하기 다음으로는 이런 아키텍처에서
  92. 92. #4 ‘잘’ 사용하기 각각의 구성요소들을 잘 사용하기 위한 방법들을 말씀드리려고 합니다.
  93. 93. 게임 서버 Fluentd S3Lambda Lambda ES ZeppelinSpark AWS EMR DataPipeline Kinesis 확장 포인트 Kinesis 샤드 개수는 어떻게 결정하나요? 첫 번째로, 방금 로그 유입량이 늘어나면
  94. 94. 게임 서버 Fluentd S3Lambda Lambda ES ZeppelinSpark AWS EMR DataPipeline Kinesis 확장 포인트 Kinesis 샤드 개수는 어떻게 결정하나요? Kinesis의 샤드개수를 늘리면 된다고 말씀드렸는데요.
  95. 95. • 샤드의 처리량 제한 • Lambda의 처리속도 Kinesis 샤드 개수는 어떻게 결정하나요? 사실 이 Kinesis 샤드 개수를 결정하기 위해
  96. 96. • 샤드의 처리량 제한 • Lambda의 처리속도 Kinesis 샤드 개수는 어떻게 결정하나요? 고려해야 할 두 가지가 있습니다.
  97. 97. Kinesis 샤드 개수는 어떻게 결정하나요? • 샤드의 처리량 제한 • Lambda의 처리속도 샤드 하나에 초당 처리할 수 있는 처리량 제한이 있고요.
  98. 98. Kinesis 샤드 개수는 어떻게 결정하나요? • 샤드의 처리량 제한 • Lambda의 처리속도 또한 Lambda의 처리속도를 고려해야합니다.
  99. 99. Lambda Iterator age 가장 최근에 처리한 레코드가 Kinesis에 기록된 시간과 Lambda가 레코드를 받은 시간차이 이 그래프는 Lambda의 Iterator age라는 메트릭입니다.
  100. 100. Lambda Iterator age 가장 최근에 처리한 레코드가 Kinesis에 기록된 시간과 Lambda가 레코드를 받은 시간차이 Lambda가 가장 최근에 처리한 레코드의
  101. 101. Lambda Iterator age 가장 최근에 처리한 레코드가 Kinesis에 기록된 시간과 Lambda가 레코드를 받은 시간차이 Kinesis에 실제로 처음 기록된 시간과
  102. 102. Lambda Iterator age 가장 최근에 처리한 레코드가 Kinesis에 기록된 시간과 Lambda가 레코드를 받은 시간차이 Lambda가 이 레코드를 처리하기 위해 이벤트를 받은
  103. 103. Lambda Iterator age 가장 최근에 처리한 레코드가 Kinesis에 기록된 시간과 Lambda가 레코드를 받은 시간차이 시간 차이인데요.
  104. 104. Lambda Iterator age 증가하면 안됨 가장 최근에 처리한 레코드가 Kinesis에 기록된 시간과 Lambda가 레코드를 받은 시간차이 결국 Kinesis에 데이터가 들어오는 속도보다
  105. 105. Lambda Iterator age 증가하면 안됨 가장 최근에 처리한 레코드가 Kinesis에 기록된 시간과 Lambda가 레코드를 받은 시간차이 Lambda의 처리속도가 느리면
  106. 106. Lambda Iterator age 증가하면 안됨 가장 최근에 처리한 레코드가 Kinesis에 기록된 시간과 Lambda가 레코드를 받은 시간차이 메트릭이 계속 상승곡선을 이루게 됩니다.
  107. 107. Lambda Iterator age 증가하면 안됨 가장 최근에 처리한 레코드가 Kinesis에 기록된 시간과 Lambda가 레코드를 받은 시간차이 여기서 Lambda는 이벤트가 일어나는 만큼 자동으로 확장하지 않느냐?라고 생각하실 수 있는데요.
  108. 108. Lambda Iterator age 증가하면 안됨 가장 최근에 처리한 레코드가 Kinesis에 기록된 시간과 Lambda가 레코드를 받은 시간차이 Lambda를 일반적인 사용예인 API형태로 사용할 때와 Kinesis를 트리거할 때는
  109. 109. Lambda Iterator age 증가하면 안됨 가장 최근에 처리한 레코드가 Kinesis에 기록된 시간과 Lambda가 레코드를 받은 시간차이 다른 동작 형태를 보입니다.
  110. 110. Shard 1 Kinesis Lambda Shard 2 Shard 3 예를 들어 Kinesis에 3개의 샤드가 존재하고
  111. 111. Shard 1 Kinesis Lambda Shard 2 Shard 3 Lambda가 이를 트리거한다고 가정해보겠습니다.
  112. 112. Shard 1 Kinesis Lambda Shard 2 Shard 3 보통은 Kinesis의 샤드에는 랜덤하게 데이터가 분배되는데
  113. 113. Shard 1 Kinesis Lambda Shard 2 Shard 3 데이터가 1번 샤드에 하나 들어오면
  114. 114. Shard 1 Kinesis Lambda Shard 2 Shard 3 ! Lambda는 “아! 이번 샤드에 데이터가 들어왔구나!”하고
  115. 115. Shard 1 Kinesis Lambda Shard 2 Shard 3 ! 이 것을 처리하려고 하는데요.
  116. 116. Shard 1 Kinesis Lambda Shard 2 Shard 3 이때 Lambda는 1번 샤드의 데이터를 처리합니다.
  117. 117. Shard 1 Kinesis Lambda Shard 2 Shard 3 만약에 모든 데이터가 다 처리되면
  118. 118. Shard 1 Kinesis Lambda Shard 2 Shard 3 더 이상 Lambda가 실행되지 않고요.
  119. 119. Shard 1 Kinesis Lambda Shard 2 Shard 3 물론 여기서 데이터를 처리하더라도
  120. 120. Shard 1 Kinesis Lambda Shard 2 Shard 3 Kinesis에 데이터는 계속 보존되는데
  121. 121. Shard 1 Kinesis Lambda Shard 2 Shard 3 그림에서는 이해를 돕기 위해 없앴습니다.
  122. 122. Shard 1 Kinesis Lambda Shard 2 Shard 3 그리고 다시 3번 샤드에 데이터가 들어오면
  123. 123. Shard 1 Kinesis Lambda Shard 2 Shard 3 다시 이것을 처리하게 되겠죠.
  124. 124. Shard 1 Kinesis Lambda Shard 2 Shard 3 만약 3개의 샤드에 동시에 데이터가 들어오면
  125. 125. Shard 1 Kinesis Lambda Shard 2 Shard 3 Lambda는 3개 샤드만큼 동시실행하여
  126. 126. Shard 1 Kinesis Lambda Shard 2 Shard 3 각각의 샤드에 대해서 처리하게 됩니다.
  127. 127. Shard 1 Kinesis Lambda Shard 2 Shard 3 1초 마다 배치 처리 만약 지속적으로 데이터가 들어오는 상황에서는
  128. 128. Shard 1 Kinesis Lambda Shard 2 Shard 3 1초 마다 배치 처리 각각의 샤드에 대해서 1초마다 동기적으로 배치처리를 하게 됩니다.
  129. 129. Shard 1 Kinesis Lambda Shard 2 Shard 3 1초 마다 배치 처리 샤드 개수 증가 Lambda 동시실행 결국 Lambda는 최대 Kinesis의 샤드개수만큼
  130. 130. Shard 1 Kinesis Lambda Shard 2 Shard 3 1초 마다 배치 처리 샤드 개수 증가 Lambda 동시실행 동시실행이 될 수 있기 때문에
  131. 131. Shard 1 Kinesis Lambda Shard 2 Shard 3 1초 마다 배치 처리 샤드 개수 증가 Lambda 동시실행 Kinesis의 샤드 개수를 증가시키는 것은
  132. 132. Shard 1 Kinesis Lambda Shard 2 Shard 3 1초 마다 배치 처리 샤드 개수 증가 Lambda 동시실행 Lambda의 동시실행 수를 높일 수 있다는 것을 의미합니다.
  133. 133. Kinesis – Lambda 처리속도를 높이는 방법 1. Kinesis stream의 샤드 개수를 증가시킨다. 2. Lambda의 가용 메모리를 증가시킨다. (Lambda는 가용 메모리 크기에 따라 CPU 성능이 조절됨) 여기서 Lambda의 처리속도를 높이는 방법을 정리해보면
  134. 134. Kinesis – Lambda 처리속도를 높이는 방법 1. Kinesis stream의 샤드 개수를 증가시킨다. 2. Lambda의 가용 메모리를 증가시킨다. (Lambda는 가용 메모리 크기에 따라 CPU 성능이 조절됨) Kinesis의 샤드 개수를 증가시켜서 Lambda의 동시 실행 수를 늘리는 방법이 있고
  135. 135. Kinesis – Lambda 처리속도를 높이는 방법 1. Kinesis stream의 샤드 개수를 증가시킨다. 2. Lambda의 가용 메모리를 증가시킨다. (Lambda는 가용 메모리 크기에 따라 CPU 성능이 조절됨) Lambda는 설정된 메모리 크기에 따라
  136. 136. Kinesis – Lambda 처리속도를 높이는 방법 1. Kinesis stream의 샤드 개수를 증가시킨다. 2. Lambda의 가용 메모리를 증가시킨다. (Lambda는 가용 메모리 크기에 따라 CPU 성능이 조절됨) CPU 성능이 조절되기 때문에
  137. 137. Kinesis – Lambda 처리속도를 높이는 방법 1. Kinesis stream의 샤드 개수를 증가시킨다. 2. Lambda의 가용 메모리를 증가시킨다. (Lambda는 가용 메모리 크기에 따라 CPU 성능이 조절됨) Lambda의 가용 메모리를 증가시켜서 CPU 성능을 높이는 방법이 있습니다.
  138. 138. Kinesis – Lambda 처리속도를 높이는 방법 1. Kinesis stream의 샤드 개수를 증가시킨다. 2. Lambda의 가용 메모리를 증가시킨다. (Lambda는 가용 메모리 크기에 따라 CPU 성능이 조절됨) 하지만 저희의 경우 CPU 보다는
  139. 139. Kinesis – Lambda 처리속도를 높이는 방법 1. Kinesis stream의 샤드 개수를 증가시킨다. 2. Lambda의 가용 메모리를 증가시킨다. (Lambda는 가용 메모리 크기에 따라 CPU 성능이 조절됨) I/O에 인텐시브하기 때문에 두 번째 방법은 배제하고 있습니다.
  140. 140. 로그파일 포맷: JSON • 복잡한 게임 시스템 → 복잡한 로그 스키마 • Lambda는 최대 1000개의 로그를 하나의 파일로 저장 (Kinesis 샤드의 초당 최대 쓰기 개수) 이제 저희가 로그를 어떻게 저장하는 지 말씀드리려고 합니다.
  141. 141. 로그파일 포맷: JSON • 복잡한 게임 시스템 → 복잡한 로그 스키마 • Lambda는 최대 1000개의 로그를 하나의 파일로 저장 (Kinesis 샤드의 초당 최대 쓰기 개수) 저희는 아이템 구조 등 복잡한 게임시스템으로
  142. 142. 로그파일 포맷: JSON • 복잡한 게임 시스템 → 복잡한 로그 스키마 • Lambda는 최대 1000개의 로그를 하나의 파일로 저장 (Kinesis 샤드의 초당 최대 쓰기 개수) 보다 유연한 로그 스키마가 필요했고
  143. 143. 로그파일 포맷: JSON • 복잡한 게임 시스템 → 복잡한 로그 스키마 • Lambda는 최대 1000개의 로그를 하나의 파일로 저장 (Kinesis 샤드의 초당 최대 쓰기 개수) 때문에 JSON 형식으로 로그를 저장하고 있습니다.
  144. 144. 로그파일 포맷: JSON • 복잡한 게임 시스템 → 복잡한 로그 스키마 • Lambda는 최대 1000개의 로그를 하나의 파일로 저장 (Kinesis 샤드의 초당 최대 쓰기 개수) 여기서 Lambda는 최대 1000개의 로그를 하나의 파일로 저장하는데요.
  145. 145. 엄청나게 많은 파일 개수 → 네트워크 오버헤드 로그파일 포맷: JSON • 복잡한 게임 시스템 → 복잡한 로그 스키마 • Lambda는 최대 1000개의 로그를 하나의 파일로 저장 (Kinesis 샤드의 초당 최대 쓰기 개수) 사실 이런 저장방식은 엄청나게 많은 파일을 생성시키고
  146. 146. 엄청나게 많은 파일 개수 → 네트워크 오버헤드 로그파일 포맷: JSON • 복잡한 게임 시스템 → 복잡한 로그 스키마 • Lambda는 최대 1000개의 로그를 하나의 파일로 저장 (Kinesis 샤드의 초당 최대 쓰기 개수) 결국 분석 시에 네트워크 오버헤드를 유발하게 됩니다.
  147. 147. JSON → Parquet 때문에 저희는 JSON을 Parquet 형식의 파일로 가공하는데요.
  148. 148. JSON → Parquet 많은 수의 파일을 단지 몇 개로 분할된 Parquet 파일로 변환하고 있고
  149. 149. JSON → Parquet 이런 변환을 1시간 마다 배치잡으로 처리하고 있습니다.
  150. 150. { "type":"ItemPurchased", "seller_id":"1a2b3c4d5e6f", "@time":"2018-04-24T……", "item":{ "level":12, "durability":1234, "tags":{ ... }, } ... } JSON실제 저희 로그 형태를 보면 중첩된 데이터 구조로 이루어져 있는데
  151. 151. Parquet 이런 중첩 데이터 구조를 모두 지원하면서도
  152. 152. Parquet 구조화된 형태를 가지는 Parquet 포맷으로 변환하게됩니다.
  153. 153. Parquet • 스키마를 가진 컬럼형 저장 포맷 • 복잡한 중첩 데이터 구조도 지원 • 기본적으로 Snappy 압축 • Column projection • Predicate pushdown Parquet 파일 형식에 대해서 소개해드리면
  154. 154. Parquet • 스키마를 가진 컬럼형 저장 포맷 • 복잡한 중첩 데이터 구조도 지원 • 기본적으로 Snappy 압축 • Column projection • Predicate pushdown 먼저 스키마를 가진 컬럼형 저장 포맷이라고 말씀드릴 수 있습니다.
  155. 155. Parquet • 스키마를 가진 컬럼형 저장 포맷 • 복잡한 중첩 데이터 구조도 지원 • 기본적으로 Snappy 압축 • Column projection • Predicate pushdown 또한 구조화된 포맷임에도 불구하고
  156. 156. Parquet • 스키마를 가진 컬럼형 저장 포맷 • 복잡한 중첩 데이터 구조도 지원 • 기본적으로 Snappy 압축 • Column projection • Predicate pushdown 복잡한 중첩데이터 구조도 지원하고요.
  157. 157. Parquet → 네트워크 트래픽 감소 • 스키마를 가진 컬럼형 저장 포맷 • 복잡한 중첩 데이터 구조도 지원 • 기본적으로 Snappy 압축 • Column projection • Predicate pushdown 기본적으로 Snappy 형식의 압축을 하고있고
  158. 158. Parquet → 네트워크 트래픽 감소 • 스키마를 가진 컬럼형 저장 포맷 • 복잡한 중첩 데이터 구조도 지원 • 기본적으로 Snappy 압축 • Column projection • Predicate pushdown Column projection, Predicate Pushdown 기능을 제공합니다.
  159. 159. Parquet → 네트워크 트래픽 감소 • 스키마를 가진 컬럼형 저장 포맷 • 복잡한 중첩 데이터 구조도 지원 • 기본적으로 Snappy 압축 • Column projection • Predicate pushdown Snappy 압축과 아래 두가지 기능은
  160. 160. Parquet → 네트워크 트래픽 감소 • 스키마를 가진 컬럼형 저장 포맷 • 복잡한 중첩 데이터 구조도 지원 • 기본적으로 Snappy 압축 • Column projection • Predicate pushdown 네트워크 트래픽을 감소 시킬 수 있습니다.
  161. 161. Column projection 이 Column projection에 대해서 간단히 설명을 드리면
  162. 162. Column projection 4월 1일에 접속한 레벨 60의 플레이어 수 SELECT COUNT(DISTINCT(player_entity_id)) FROM PlayerEntered_asia_a WHERE __date="2018-04-01" AND player_level=60 4월 1일에 접속한 레벨 60의 플레이어 수를 추출하는 쿼리를 예제로 들 수 있습니다.
  163. 163. Column projection 쿼리에 필요한 컬럼만 스캔 +- *FileScan parquet [player_entity_id#44,player_level#45,__date#53] 이 쿼리의 플랜을 보면
  164. 164. Column projection 쿼리에 필요한 컬럼만 스캔 +- *FileScan parquet [player_entity_id#44,player_level#45,__date#53] 쿼리에 실제로 필요한 플레이어 아이디와, 레벨, 날짜만 스캔하는 것을 알 수 있습니다.
  165. 165. Column projection 쿼리에 필요한 컬럼만 스캔 +- *FileScan parquet [player_entity_id#44,player_level#45,__date#53] 결국 컬럼형 저장방식이기 때문에
  166. 166. Column projection 쿼리에 필요한 컬럼만 스캔 +- *FileScan parquet [player_entity_id#44,player_level#45,__date#53] 쿼리에 필요한 컬럼만 스캔하여
  167. 167. Column projection 쿼리에 필요한 컬럼만 스캔 +- *FileScan parquet [player_entity_id#44,player_level#45,__date#53] 네트워크 트래픽을 낮출 수 있습니다.
  168. 168. Predicate pushdown 레벨 60인 데이터만 필터링 PushedFilteres: [IsNotNull(player_level), EqualTo(player_level,60)] 또 Predicate pushdown은 같은 예제로
  169. 169. Predicate pushdown 레벨 60인 데이터만 필터링 PushedFilteres: [IsNotNull(player_level), EqualTo(player_level,60)] 메타데이터에 저장된 통계값을 이용해
  170. 170. Predicate pushdown 레벨 60인 데이터만 필터링 PushedFilteres: [IsNotNull(player_level), EqualTo(player_level,60)] 레벨이 60인 데이터만 필터링하여 스캔합니다.
  171. 171. Predicate pushdownColumn projection 이렇게 Column Projection과 Predicate pushdown을 활용하여
  172. 172. Predicate pushdownColumn projection 최대한 필요한 데이터만 읽도록 하여
  173. 173. Predicate pushdownColumn projection 네트워크 트래픽을 낮출 수 있는데요.
  174. 174. Table Partitioning • 데이터 스캔 범위를 제한가능 • 저장경로에 '<key>=<value>' 형태로 분리 여기에 테이블 파티셔닝이라는 기능을 이용하면
  175. 175. Table Partitioning → 네트워크 트래픽 감소• 데이터 스캔 범위를 제한가능 • 저장경로에 '<key>=<value>' 형태로 분리 추가적으로 네트워크 트래픽을 감소시킬 수 있습니다.
  176. 176. Table Partitioning s3://<bucket>/<schema>/__date=<yyyy-mm-dd>/<filename> → 네트워크 트래픽 감소• 데이터 스캔 범위를 제한가능 • 저장경로에 '<key>=<value>' 형태로 분리 여러 가지 방법이 있긴 하지만
  177. 177. Table Partitioning s3://<bucket>/<schema>/__date=<yyyy-mm-dd>/<filename> → 네트워크 트래픽 감소• 데이터 스캔 범위를 제한가능 • 저장경로에 '<key>=<value>' 형태로 분리 저장경로에 Key=Value 형태로 중간 경로를 지정하는 형태로 사용할 수 있습니다.
  178. 178. Table Partitioning s3://<bucket>/<schema>/__date=<yyyy-mm-dd>/<filename> → 네트워크 트래픽 감소• 데이터 스캔 범위를 제한가능 • 저장경로에 '<key>=<value>' 형태로 분리 실제로 저희는 날짜별로 파티셔닝을 하고 있는데
  179. 179. Table Partitioning SELECT COUNT(DISTINCT(player_entity_id)) FROM PlayerEntered_asia_a WHERE __date="2018-04-01" AND player_level=60 아까와 같이 4월 1일에 접속한 레벨이 60인 플레이어 수를 구하는 예제에서
  180. 180. SELECT COUNT(DISTINCT(player_entity_id)) FROM PlayerEntered_asia_a WHERE __date="2018-04-01" AND player_level=60 실제 컬럼처럼 사용가능 Table Partitioning WHERE __date="2018-04-01" s3://<bucket>/<schema>/__date=<yyyy-mm-dd>/<filename> 아까 저장경로 상에서 지정한 date라는 파티션을
  181. 181. SELECT COUNT(DISTINCT(player_entity_id)) FROM PlayerEntered_asia_a WHERE __date="2018-04-01" AND player_level=60 실제 컬럼처럼 사용가능 Table Partitioning WHERE __date="2018-04-01" s3://<bucket>/<schema>/__date=<yyyy-mm-dd>/<filename> 실제 컬럼처럼 사용가능하고요.
  182. 182. SELECT COUNT(DISTINCT(player_entity_id)) FROM PlayerEntered_asia_a WHERE __date="2018-04-01" AND player_level=60 쿼리 플랜에서 동작을 확인할 수 있다. Table Partitioning PartitionFilters: [isnotnull(__date#53), (cast(__date#53as string)=2018-04-01)] 쿼리플랜에서도 다음과 같이 파티션 필터가 동작하면서
  183. 183. SELECT COUNT(DISTINCT(player_entity_id)) FROM PlayerEntered_asia_a WHERE __date="2018-04-01" AND player_level=60 쿼리 플랜에서 동작을 확인할 수 있다. Table Partitioning PartitionFilters: [isnotnull(__date#53), (cast(__date#53as string)=2018-04-01)] date가 4월 1일인 파티션을 필터링하는 것을 볼 수 있습니다.
  184. 184. SELECT COUNT(DISTINCT(player_entity_id)) FROM PlayerEntered_asia_a WHERE __date="2018-04-01" AND player_level=60 쿼리 플랜에서 동작을 확인할 수 있다. Table Partitioning PartitionFilters: [isnotnull(__date#53), (cast(__date#53as string)=2018-04-01)] 실제로 해당되는 파티션의 데이터만 S3에서 읽기 때문에
  185. 185. SELECT COUNT(DISTINCT(player_entity_id)) FROM PlayerEntered_asia_a WHERE __date="2018-04-01" AND player_level=60 쿼리 플랜에서 동작을 확인할 수 있다. Table Partitioning PartitionFilters: [isnotnull(__date#53), (cast(__date#53as string)=2018-04-01)] 훨씬 네트워크 트래픽을 감소시킬 수 있습니다.
  186. 186. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 다음은 로그 스키마 관리인데요.
  187. 187. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 결국은 JSON에서 Parquet로 구조화된 데이터 유형을 사용하기 때문에
  188. 188. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 로그 스키마 관리가 필요합니다.
  189. 189. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 먼저 저희는 접속로그, 채집로그 등
  190. 190. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 로그 유형별로 스키마를 가지고 있습니다.
  191. 191. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 새롭게 컨텐츠가 들어갈 때 새로운 스키마를 쉽게 추가할 수 있지만
  192. 192. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 기존에 존재하는 스키마를 변경할 땐
  193. 193. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 약간 제한되는 것들이 있습니다.
  194. 194. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 약간 제한되는 것들이 있습니다.
  195. 195. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 예를 들어, 컬럼 이름을 바꾼다고 하면
  196. 196. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 이미 적재되어 있는 모든 로그에 대하여
  197. 197. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. 새로운 컬럼 이름으로 바꿔서 다시 저장하는 마이그레이션 비용이 매우 크기 때문입니다.
  198. 198. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. → 컬럼 추가만 가능! 하위호환성을 유지하여야 한다. 때문에 저희는 컬럼 추가만 가능하게 하여
  199. 199. 로그 스키마 관리 • 로그는 다양한 스키마를 가진다. • 접속로그, 채집로그, 구매로그 등 • 새로운 스키마는 언제든지 추가될 수 있어야 한다. • 스키마 변경 시 적재된 로그를 모두 마이그레이션할 수 없다. → 컬럼 추가만 가능! 하위호환성을 유지하여야 한다. 하위호환성을 유지하는 정책을 두고 있습니다.
  200. 200. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 저희는 서버 코드 상에서 로그 스키마를 관리하는데요.
  201. 201. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 스키마 별로 클래스가 존재하고
  202. 202. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 이 클래스로부터 데이터를 담은 객체를 생성하여 로그를 전송합니다.
  203. 203. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 새로운 버전 배포 시에
  204. 204. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 각각의 스키마 별로
  205. 205. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 SparkSQL에서 StructType이라는 클래스에서 다루는 JSON 포맷으로 스키마를 추출하고
  206. 206. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 이 것을 따로 특정 S3 버킷에 저장합니다.
  207. 207. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 그리고 JSON 파일을 Parquet로 변환하는 배치잡에서는
  208. 208. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 아까 저장했던 스키마 중 가장 최근 버전의 스키마를 읽고 처리하는데요.
  209. 209. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 스키마 저장소를 따로 관리하기 때문에
  210. 210. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 새로 추가된 스키마에 대해서도
  211. 211. 로그 스키마 업데이트 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 별다른 처리 없이 배치잡에서 알 수 있습니다.
  212. 212. 로그 스키마 업데이트 schema = StructType.fromJson(schema_json) Parquet 변환 시 로드 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 PySpark
  213. 213. 로그 스키마 업데이트 schema = StructType.fromJson(schema_json) Parquet 변환 시 로드 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 PySpark JSON으로된 로그를 Parquet로 변환시
  214. 214. 로그 스키마 업데이트 schema = StructType.fromJson(schema_json) Parquet 변환 시 로드 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 PySpark 보시는 코드와 같이 JSON으로된 스키마 파일을 로드하여
  215. 215. 로그 스키마 업데이트 schema = StructType.fromJson(schema_json) Parquet 변환 시 로드 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 PySpark StructType 객체를 생성하고
  216. 216. 로그 스키마 업데이트 schema = StructType.fromJson(schema_json) Parquet 변환 시 로드 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 PySpark JSON 로그의 스키마를 지정하고 있습니다.
  217. 217. 로그 스키마 업데이트 schema = StructType.fromJson(schema_json) Parquet 변환 시 로드 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 PySpark 물론 JSON 로그로 부터 스키마를 추론할 수 있기도 하지만
  218. 218. 로그 스키마 업데이트 schema = StructType.fromJson(schema_json) Parquet 변환 시 로드 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 PySpark 성능이 느려지고
  219. 219. 로그 스키마 업데이트 schema = StructType.fromJson(schema_json) Parquet 변환 시 로드 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 PySpark 명시적인 것이 훨씬 좋다는 판단 하에
  220. 220. 로그 스키마 업데이트 schema = StructType.fromJson(schema_json) Parquet 변환 시 로드 1. 스키마 별 Class 추가 또는 업데이트 2. 배포 시 Spark StructType JSON 포맷으로 추출 3. AWS S3에 저장 4. Parquet 변환 배치잡에서 매번 스키마 정보를 읽고 수행 PySpark 스키마를 따로 정의하고 관리하는 방법을 사용하고 있습니다.
  221. 221. EMR Spark 클러스터 스펙 • 메모리가 중요하므로 r4 타입을 선호 • 작은 인스턴스 4개 보다 큰 인스턴스 1개로 • 좋은 인스턴스가 네트워크 속도도 더 빠르다 • EMR이 YARN 컨테이너에 할당하는 메모리 비율이 더 크다 다음으로는 EMR에서 Spark 클러스터를 할당하는 팁인데요.
  222. 222. EMR Spark 클러스터 스펙 • 메모리가 중요하므로 r4 타입을 선호 • 작은 인스턴스 4개 보다 큰 인스턴스 1개로 • 좋은 인스턴스가 네트워크 속도도 더 빠르다 • EMR이 YARN 컨테이너에 할당하는 메모리 비율이 더 크다 인 메모리 방식이기 때문에 많은 메모리가 필요할 수 있어서
  223. 223. EMR Spark 클러스터 스펙 • 메모리가 중요하므로 r4 타입을 선호 • 작은 인스턴스 4개 보다 큰 인스턴스 1개로 • 좋은 인스턴스가 네트워크 속도도 더 빠르다 • EMR이 YARN 컨테이너에 할당하는 메모리 비율이 더 크다 메모리 최적화 타입인 r4 타입을 선호하고 있습니다.
  224. 224. EMR Spark 클러스터 스펙 • 메모리가 중요하므로 r4 타입을 선호 • 작은 인스턴스 4개 보다 큰 인스턴스 1개로 • 좋은 인스턴스가 네트워크 속도도 더 빠르다 • EMR이 YARN 컨테이너에 할당하는 메모리 비율이 더 크다 또한 작은 인스턴스 여러 개 보다는
  225. 225. EMR Spark 클러스터 스펙 • 메모리가 중요하므로 r4 타입을 선호 • 작은 인스턴스 4개 보다 큰 인스턴스 1개로 • 좋은 인스턴스가 네트워크 속도도 더 빠르다 • EMR이 YARN 컨테이너에 할당하는 메모리 비율이 더 크다 큰 인스턴스 하나를 사용하고 있는데
  226. 226. EMR Spark 클러스터 스펙 • 메모리가 중요하므로 r4 타입을 선호 • 작은 인스턴스 4개 보다 큰 인스턴스 1개로 • 좋은 인스턴스가 네트워크 속도도 더 빠르다 • EMR이 YARN 컨테이너에 할당하는 메모리 비율이 더 크다 그 이유는 좋은 인스턴스일수록
  227. 227. EMR Spark 클러스터 스펙 • 메모리가 중요하므로 r4 타입을 선호 • 작은 인스턴스 4개 보다 큰 인스턴스 1개로 • 좋은 인스턴스가 네트워크 속도도 더 빠르다 • EMR이 YARN 컨테이너에 할당하는 메모리 비율이 더 크다 AWS에서 더 높은 네트워크 속도를 제공하고 있고
  228. 228. EMR Spark 클러스터 스펙 • 메모리가 중요하므로 r4 타입을 선호 • 작은 인스턴스 4개 보다 큰 인스턴스 1개로 • 좋은 인스턴스가 네트워크 속도도 더 빠르다 • EMR이 YARN 컨테이너에 할당하는 메모리 비율이 더 크다 EMR에서 Spark를 사용할 때
  229. 229. EMR Spark 클러스터 스펙 • 메모리가 중요하므로 r4 타입을 선호 • 작은 인스턴스 4개 보다 큰 인스턴스 1개로 • 좋은 인스턴스가 네트워크 속도도 더 빠르다 • EMR이 YARN 컨테이너에 할당하는 메모리 비율이 더 크다 YARN이라는 리소스 매니저를 사용하는데
  230. 230. EMR Spark 클러스터 스펙 • 메모리가 중요하므로 r4 타입을 선호 • 작은 인스턴스 4개 보다 큰 인스턴스 1개로 • 좋은 인스턴스가 네트워크 속도도 더 빠르다 • EMR이 YARN 컨테이너에 할당하는 메모리 비율이 더 크다 이 때 YARN 컨테이너에 할당하는 메모리 비율이 더 크기 때문입니다.
  231. 231. #5 개선할 점 네, 여기까지 저희 로그 시스템에 대한 소개를 마쳤습니다.
  232. 232. #5 개선할 점 그래서 결론이 “완벽한 로그 시스템인가?” 에 대하여
  233. 233. #5 개선할 점 실제 운영결과, 앞서 말했던 목표를 달성하였지만
  234. 234. #5 개선할 점 몇 가지 분명 불편한 점이 있었습니다.
  235. 235. #5 개선할 점 마지막으로 개선할 점을 말씀드리면서 정리를 하고자합니다.
  236. 236. • 분석가는 언제나 부족 • 비 개발자도 분석하기도 쉬웠으면 • SQL은 과연 쉬운가…? • 사실상 대중화 실패 • 하는 사람만 한다. 분석의 대중화 먼저 분석의 대중화가 어려웠습니다.
  237. 237. • 분석가는 언제나 부족 • 비 개발자도 분석하기도 쉬웠으면 • SQL은 과연 쉬운가…? • 사실상 대중화 실패 • 하는 사람만 한다. 분석의 대중화 분석에 대한 수요는 많지만 분석가는 언제나 부족합니다.
  238. 238. • 분석가는 언제나 부족 • 비 개발자도 분석하기도 쉬웠으면 • SQL은 과연 쉬운가…? • 사실상 대중화 실패 • 하는 사람만 한다. 분석의 대중화 때문에 비 개발자도 분석하기 쉬웠으면 이라는 생각도 하는데요.
  239. 239. • 분석가는 언제나 부족 • 비 개발자도 분석하기도 쉬웠으면 • SQL은 과연 쉬운가…? • 사실상 대중화 실패 • 하는 사람만 한다. 분석의 대중화 사실 이런 점을 위해서 나름 간단한 분석은 쉽게 할 수 있도록
  240. 240. • 분석가는 언제나 부족 • 비 개발자도 분석하기도 쉬웠으면 • SQL은 과연 쉬운가…? • 사실상 대중화 실패 • 하는 사람만 한다. 분석의 대중화 SQL을 사용할 수 있는 것을 목표로 하였습니다만
  241. 241. • 분석가는 언제나 부족 • 비 개발자도 분석하기도 쉬웠으면 • SQL은 과연 쉬운가…? • 사실상 대중화 실패 • 하는 사람만 한다. 분석의 대중화 사실상 대중화는 쉽지 않았습니다.
  242. 242. • 분석의 규모에 따라 • 클러스터 크기가 자동으로 조절되었으면 분석 규모에 따른 오토 스케일링 또한 분석 규모에 따라 오토 스케일링이 되었으면 좋겠다라는 생각을 했습니다.
  243. 243. • 분석의 규모에 따라 • 클러스터 크기가 자동으로 조절되었으면 분석 규모에 따른 오토 스케일링 비용을 좀 더 효과적으로 절감하고
  244. 244. • 분석의 규모에 따라 • 클러스터 크기가 자동으로 조절되었으면 분석 규모에 따른 오토 스케일링 시간을 효율적으로 사용할 수 있었으면 하는 바람이 있습니다.
  245. 245. 시각화의 다양화 • 한편으로는 복잡한 시각화가 필요할 때도 있다. • Zeppelin으로는 아직 제한적 한편으로는 Zeppelin에만 시각화에 대한 상당부분을 의존하고 있기 때문에
  246. 246. 시각화의 다양화 • 한편으로는 복잡한 시각화가 필요할 때도 있다. • Zeppelin으로는 아직 제한적 복잡한 시각화의 경우
  247. 247. 시각화의 다양화 • 한편으로는 복잡한 시각화가 필요할 때도 있다. • Zeppelin으로는 아직 제한적 Jupyter notebook에서 따로 시각화하고 있는데요.
  248. 248. 시각화의 다양화 • 한편으로는 복잡한 시각화가 필요할 때도 있다. • Zeppelin으로는 아직 제한적 이 부분도 아직은 아쉬움으로 남고 있습니다.
  249. 249. 로그 스키마 변경 • 이미 쌓인 로그에 대하여 마이그레이션 불가능 • 오직 컬럼 추가만 가능 세 번째는 아까 말씀드렸던 로그 스키마 변경입니다.
  250. 250. 로그 스키마 변경 • 이미 쌓인 로그에 대하여 마이그레이션 불가능 • 오직 컬럼 추가만 가능 쉽게 스키마를 추가할 수 있고
  251. 251. 로그 스키마 변경 • 이미 쌓인 로그에 대하여 마이그레이션 불가능 • 오직 컬럼 추가만 가능 컬럼 추가도 자유롭지만
  252. 252. 로그 스키마 변경 • 이미 쌓인 로그에 대하여 마이그레이션 불가능 • 오직 컬럼 추가만 가능 컬럼 이름을 바꿀 수 없다는 것이
  253. 253. 로그 스키마 변경 • 이미 쌓인 로그에 대하여 마이그레이션 불가능 • 오직 컬럼 추가만 가능 의외로 시간이 지나면서 불편을 초래하고 있습니다.
  254. 254. “이 컬럼은 정확히 뭘 의미하나요?” • 100개가 넘는 스키마, 모두 기억하지 못함 • 문서가 없기 때문에 발생하는 현상 마지막으로 컨텐츠가 추가되거나
  255. 255. “이 컬럼은 정확히 뭘 의미하나요?” • 100개가 넘는 스키마, 모두 기억하지 못함 • 문서가 없기 때문에 발생하는 현상 남겨야 할 정보들이 더 많아지고 하면서
  256. 256. “이 컬럼은 정확히 뭘 의미하나요?” • 100개가 넘는 스키마, 모두 기억하지 못함 • 문서가 없기 때문에 발생하는 현상 스키마 개수도 100개가 넘고
  257. 257. “이 컬럼은 정확히 뭘 의미하나요?” • 100개가 넘는 스키마, 모두 기억하지 못함 • 문서가 없기 때문에 발생하는 현상 컬럼도 계속 추가되고 있는데요.
  258. 258. “이 컬럼은 정확히 뭘 의미하나요?” • 100개가 넘는 스키마, 모두 기억하지 못함 • 문서가 없기 때문에 발생하는 현상 어떤 컬럼이 정확히 무엇을 의미하는 지에 대해
  259. 259. “이 컬럼은 정확히 뭘 의미하나요?” • 100개가 넘는 스키마, 모두 기억하지 못함 • 문서가 없기 때문에 발생하는 현상 직접 코드를 보아야 알 수 있는 경우도 있었습니다.
  260. 260. “이 컬럼은 정확히 뭘 의미하나요?” • 100개가 넘는 스키마, 모두 기억하지 못함 • 문서가 없기 때문에 발생하는 현상 이 것은 결국 문서가 없기 때문에 발생하는 현상인데요.
  261. 261. “이 컬럼은 정확히 뭘 의미하나요?” • 100개가 넘는 스키마, 모두 기억하지 못함 • 문서가 없기 때문에 발생하는 현상 → Docstring을 이용한 스키마 문서화? 스키마 별로 클래스를 정의하고 있기 때문에
  262. 262. “이 컬럼은 정확히 뭘 의미하나요?” • 100개가 넘는 스키마, 모두 기억하지 못함 • 문서가 없기 때문에 발생하는 현상 → Docstring을 이용한 스키마 문서화? Docstring을 이용해서 스키마를 문서화하는 방법을 고안 중입니다.
  263. 263. 감사합니다 네, 제가 준비한 내용은 여기까지인데요.
  264. 264. 감사합니다 많은 것을 공유하고 싶었고
  265. 265. 감사합니다 때문에 많은 내용을 다루다 보니
  266. 266. 감사합니다 조금 지루할 수도 있고 미처 자세히 설명하지 못한 부분도 있을 수 있지만
  267. 267. 감사합니다 여기까지 들어주신 분들께 감사드립니다.
  268. 268. 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기 김찬웅 님 / 4월 25일 오후 4시 30분 〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3 이흥섭 님 / 4월 25일 오후 3시 20분 〈야생의 땅: 듀랑고〉 NoSQL 위에서 MMORPG 개발하기 최호영 님 / 4월 26일 오전 11시 듀랑고의 서버에 관련된 발표로
  269. 269. 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기 김찬웅 님 / 4월 25일 오후 4시 30분 〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3 이흥섭 님 / 4월 25일 오후 3시 20분 〈야생의 땅: 듀랑고〉 NoSQL 위에서 MMORPG 개발하기 최호영 님 / 4월 26일 오전 11시 내일과 내일 모레에도 세션들이 준비되어있으니
  270. 270. 만들고 붓고 부수고 - 〈야생의 땅: 듀랑고〉 서버 관리 배포 이야기 김찬웅 님 / 4월 25일 오후 4시 30분 〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3 이흥섭 님 / 4월 25일 오후 3시 20분 〈야생의 땅: 듀랑고〉 NoSQL 위에서 MMORPG 개발하기 최호영 님 / 4월 26일 오전 11시 관심있는 분들은 많은 참석바랍니다.
  271. 271. WE'RE HIRING!http://what.studio/ 마지막으로 저희 왓 스튜디오는 다양한 직군에 대해서 채용을 진행하고 있으니
  272. 272. WE'RE HIRING!http://what.studio/ 많은 관심 부탁드립니다.
  273. 273. WE'RE HIRING!http://what.studio/ 이상으로 발표를 마치겠습니다. 감사합니다.

×