9. 2.1 사용 로그
2016-05-30 00:29:54 시점에,
ijkl 여자 사용자가,
blogpeople 블로그내,
게시글 220827397534을 읽음
t c i u g
2016-05-30T00:29:54+09:00 minos44 abcd http://blog.naver.com/minos44 m
2016-05-30T00:29:54+09:00 koo5025 efgh http://blog.naver.com/koo5025/210822373953
2016-05-30T00:29:54+09:00 blogpeople ijkl http://blog.naver.com/blogpeople/220827397534 f
2016-05-30T00:29:54+09:00 gpal1124 mnop http://blog.naver.com/gpal1124/2103398227534
2016-05-30T00:29:54+09:00 qpxhqm qrst http://blog.naver.com/qpxhqm/217482397534 f
2016-05-30T00:29:54+09:00 wva18pv1 uvwx http://blog.naver.com/wva18pv1/218274397534 m
2016-05-30T00:29:54+09:00 03sarang zabc http://blog.naver.com/03sarang/2089756274336
10. 2.2 통계 결과
Metric: 측정항목
• 조회수 / 방문수 / 방문자수 ...
Dimension: 측정기준
• 시간단위별 (일/주/월)
• 서비스 채널별, 게시글별
• 사용자 타입별
• 기기별 ...
여러 Dimension 조합당 Metric값들 계산
11. 2.3 계산 방식
SELECT
// dimension
u, g,
// metric
COUNT(id) view,
SUM(vt) visit,
COUNT(DISTINCT i) visitor
FROM
log
WHERE
// filter
c = ' blogpeople’
GROUP BY
// dimension
u, g
Real-time 로그 처리
• 미리 group by 결과를 계산할 수 없음
• 실시간으로 최신 로그에서 바로 계산
Batch 로그 처리
• filter 조건에 만족하는 로그 수가 커지면
group by 비용 상승
• group by 결과를 제공 시간 단위로 미리 계산
12. 2.4 사용 규모
수집 로그
• 일일 수억 로그
• 일일 수천만 이상 Unique 사용자 방문
• 일일 수천만 이상 Unique 게시글 조회
서비스 질의
• 일일 수억 질의
18. 4.1 Our Mission
Real-time 처리
• Exactly-once
• 분산/병렬 처리
Batch 처리
• 빠른 Aggregation 성능
• 쉬운 Aggregation 구현
운영
• Scale Out
• High Availability
• 통합 모니터링
서비스 query 처리
• 빠른 응답 속도
20. 4.2.1 기본 방향
Unique 로그 ID 발급 At-least-once 스트림 처리 (유실 방지) _id에 로그 ID 매핑 (중복 방지)
21. 4.2.2 Logstash Input
sincedb: input 파일의 offset 관리
• Logstash가 restart 되는 경우 sincedb에 저장된 offset 이후부터 읽기 시작함
로그 rotate시 데이터 유실 방지
• start_position=beginning 설정
• end로 설정시 신규 파일 발견 후 추가 되는 데이터만 수집
• discover_interval 만큼 유실 가능
inode major dev num minor dev num offset(byte)
805526863 0 2052 11584599
805526865 0 2052 15421467551
22. 4.2.3 Logstash Output
Kafka 전송
• Kafka 저장 중에 에러 발생하는 경우, 실패 로그 재전송 필요
• retries(5), acks(all: 모든 partition replica ack 체크) 옵션값 설정
Broker 1
topic1-partition0
(leader)
Broker 2
topic1-partition0
(follower)
① Write
② Replicate
③ ACK
④ ACK
23. 4.2.4 Storm
• 부하가 많은 경우 로그가 유실되었다
• Anchoring 도입 후 유실 횟수가 크게 줄었으나,
• 여전히 알 수 없는 문제로 소량의 로그가 유실되었음
• 원인을 더 깊이 조사했다면 log 유실이 없도록 조치할 수 있었겠지만,
• 병행 테스트했던 Spark-Streaming의 사용성이 더 좋다 판단되어,
• Storm은 사용하기 않기로 결정
도입 포기
24. 4.2.5 Spark-Streaming
At-least-once 처리
• Kafka Direct API 사용: Simple Consumer API를 통해 offset 범위를 직접 지정 가능
• Spark-Streaming 에서는 처리 중인 topic partition들의 offset을 직접 관리
• offset은 외부 저장소 Zookeeper 이용
• 오류로 중단 될 경우, offset update를 하지 않음
① Read data A
(offset A)
② Write data A
Success
③ Write offset A
Fail
③ Rewrite data A
27. 4.3.2 Kafka
Partition
하나의 topic은 다수의 partition으로 분할
각 partition이 broker(서버)로 분산
각 partition 단위로 Producer/Consumer 병렬 처리
Logstash Kafka output
kafka 전송시 message_key를 명시(id)
default hash를 이용해 균등하게 메시지 전송
Broker1
topic1-partition0
Broker2
topic1-partition1
Broker3
topic1-partition2
target partition
= hash(message_key)
28. 4.3.3 Spark-Streaming
Worker 개수 선정
• 원하는 throughput이 나올때 까지 worker 개수 증가 (topic partition 개수 이내)
• topic partition 개수 보다 더 병렬성이 더 필요한 경우,
• 데이타를 분할하는 repartition 단계 필요 (worker 개수를 더 늘릴 수 있음)
그러나 단순히 worker 숫자를 늘린다고 될까?
• Spark 구간이 병목이라면 (CPU bound)
• worker를 늘릴 수록 throughput 증가
• 저장 시스템이 병목이라면 (IO bound)
• Worker를 늘려도 성능 변화 없음
31. 4.4.1 M/R vs. Spark SQL
• 개발 기간은 짧고, 다양한 플랫폼을 테스트를 할 수 없었음
• 그래도 사용 경험이 있고, 대용량에서도 안정적으로 동작하는 M/R 선택
최초 M/R로 구현하게 된 계기
그러나...
32. 4.4.1 M/R vs. Spark SQL
개발이 쉽다! (적은 코딩량 + 편리한 REPL)
sqlContext.sql("
SELECT s, c, u, COUNT(*)
FROM logs
GROUP BY s, c, u
").saveToEs("index/type")
Mapper
의 일부
33. 4.4.1 M/R vs. Spark SQL
탁월한 퍼포먼스 (3x faster using 10x fewer machines)
출처(peta-byte sorting): https://databricks.com/blog/2014/10/10/spark-petabyte-sort.html
34. 4.4.1 M/R vs. Spark SQL
• M/R을 일반적으로 집계용으로 사용하다보니 GROUP BY로 변환 가능
대부분의 M/R code는 SQL로 표현 가능
난관: 재방문율을 SQL로 표현
• 9월 재방문율 정의: 9월 방문자들 중에 8월에 방문한 방문자들의 비율
SELECT 블로그id, 방문자id
FROM logs
WHERE ts BETWEEN '2016-08-01' AND '2016-09-30'
GROUP BY 블로그id, 방문자id
HAVING MIN(ts) < '2016-08-31' and MAX(ts) >= '2016-09-01'
35. 4.4.2 Elasticsearch vs. Parquet
Batch 처리의 Source로는 Parquet 형태가 더 적합
Data node
Parquet
(16GB)
Worker
file read
ES node
ES Data File
(100GB)
ES
file readYarn Node
Worker
HTTP
통신
• I/O 및 각종 부가 비용으로 인해 ES의 Read가 느림
39. 4.5.2 Elasticsearch: 복합 필드
query: {
...
{
"term": {
"c": "blogpeople"
}
},
{
"term": {
"type": "channel_uv"
}
}
...
}
query: {
...
{
"term": {
"ctype": "blogpeople:channel_uv "
}
}
...
}
cache miss 질의 응답 속도가
약 40% 정도 단축되었음
질의 응답 시간을 줄이려면?
검색용 단일 필드 추가
40. 4.5.3 Elasticsearch: execution_hint
// SQL : group by u
{
"aggregation": {
"terms": {
"field": "u",
"execution_hint": "map"
}
}
}
별도로 지정하지 않으면
ordinal 방식으로 설정됨
41. 4.5.3 Elasticsearch: execution_hint
처리 시간 비교
0
1000
2000
3000
4000
5000
6000
7000
8000
5262 96879 11039267 128250817
응답시간(ms)
matched document
map ordinal
43. 4.5.4 Elasticsearch: Java GC
• Compaction 때는 짧더라도 STW(Stop The World) 발생할 수 밖에 없다
CMS(Concurrent Mark & Sweep) GC를 쓰지만...
해결 방법
• Full GC 횟수 최소화
44. 4.5.4 Elasticsearch: Java GC
• 대량 결과 색인시, 자주 heap의 객체들이 young 영역에서 old 영역으로 이동
• survivor 영역의 크기가 작아서, 강제 promotion이 발생 (aging에 의한 이동이 아님)
• survivor 영역의 크기를 적당히 늘려서 해결
Full GC 횟수 줄이기
45. 4.5.4 Elasticsearch: Java GC
• 월간과 같은 대량의 데이터 입수 후에는 간간히 Full GC가 오래 걸림
• 역시 OLD 영역 compaction 이 오래 걸리는 것이 문제
• 월간 데이터 입수 후에는 ES rolling restart 를 통해, OLD 영역 메모리 상태를 초기화
Full GC 제거
46. 4.5.5 API: 캐시 정책
• 한번 생성되면 변경되지 않음
• 조회 성능을 위해 비교적 긴 시간 캐시
Batch 결과 캐시
Real-time 결과 캐시
• 실시간으로 계속 로그 추가
• 캐시 기간 동안 실시간 반영이 안되므로 짧은 시간 캐시
47. 4.5.6 API: 실시간 조회수
Scoreboard
• Real-time에서 매번 실시간 집계하기엔 매칭된 로그 수가 너무 많음
• nbase-arc(메모리 DB)를 이용해서 조회수++
49. 4.6.1 Scale Out
L4 하위에 서버 투입
Kafka Cluster에
Broker 추가
ES Cluster에 Node 추가HDFS Cluster에 Node 추가
Yarn Cluster에 Node 추가
Yarn Cluster에 Node 추가
L4 하위에 서버 투입
저장 관점
처리 관점
51. 4.6.3 High Availability
L7
Partition 이중화
Leader Election
Shard 이중화
Primary Election
Master Node 이중화
Master Node Election
Data 이중화
실패 시 자동 재실행
실패 시 자동 재실행
Backpressure
L7
저장 관점
처리 관점
53. 4.7.1 CDH 배포본 사용
출처: https://www.cloudera.com/documentation/enterprise/latest/topics/cm_qs_quick_start.html
54. 4.7.2 Data 흐름별 Lag 모니터링
Access log
Lag
(10)
since_db offsetFile offset Broker offset
Lag
(9)
Direct API offset
55. 4.7.3 Spark 메모리 오류
Off-heap 메모리 활용 (YARN)
• spark.yarn.executor.memoryOverhead
• 충분히 큰 값으로 설정하면 Driver/Executor 메모리 초과 오류가 적어짐
• Heap 10G + Overhead (400M) vs. Heap 6G + Overhead (2G)
56. 4.7.4 Elasticsearch: Alias
• Symbolic link와 같은 개념
• Batch Aggregation 결과 작업이 끝난 index에 대해서만 서비스 노출 가능
• 버전별 Index 관리가 되는 경우, 잘못되었을 경우 rollback도 쉽게 가능
장점
57. 4.7.5 Elasticsearch: Shard 분할 기준
몇 개의 Shard가 좋을까?
용량
10GB
용량
10GB
용량
10GB
용량
10GB
초기 Sharding 기준: Doc 개수 현재 Sharding 기준: Shard 크기
전체 Shard 수를 줄임
Shard
문서
1000만개
문서
1000만개
문서
1000만개
문서
1000만개
문서
1000만개
... ...
... ...
61. 5.1 마무리
• 모든 문제를 하나의 솔루션만으로 해결할 수는 없습니다
• 그리고 특정 상황에 맞는 유일한 정답은 없습니다
• Use-case에 적합한 테스트들을 통해 필요한 사항들을 검증해야 합니다
수많은 오픈 소스 플랫폼들이 있지만...
그래서 유용한 경험들이 많이 공유되었으면 합니다
65. Elasticsearch: Java GC Options
HEAP size 20G 기준
옵션 기본값 설정값 용도
NewRatio 2 6 Heap의 1/(6+1) = 약 2.86G
SurvivorRatio 8 3 New 영역의 1/(3+1) = 약 715M
MaxTenuringThreshod 15 15 최대한 aging 후에 old로 넘어가도록
-UseAdaptiveSizePolicy true false young, old 영역의 크기를 고정
+NeverTenure 적용 안됨 (Parallel GC에서만 가능)
CMSInitiatingOccupancyFraction 적용 안됨
66. Spark-Streaming: 서버 대수 산정
• 여러 Worker들이 모여서 하나의 Executor 단위(JVM)로 실행
• Executor당 Core(=Worker) 개수(C)는 4~6개가 적당
• HDFS 성능과 처리 데이터 공유 관점에서 경험치
• Executor당 메모리 크기(S) 결정
• 작업의 종류에 따라, 필요한 크기를 결정
• 서버 Spec에 맞춰서 서버당 실행할 수 있는 Executor 수(E) 결정
• E = Min(Available Cores/C, Available RAM/S)
• 필요한 Worker 개수(W) 기준으로, 서버 대수(N)가 최종적으로 결정됨
• N = W / (C x E)
...