Kudu

[사례] kudu X 카카오뱅크

Sencia 2021. 3. 29. 17:28
반응형

산전수전kudu전.pdf
1.85MB

본 포스팅은 2019년 cloudera 웨비나에서 카카오뱅크 세션 발표때 사용된 자료를 정리했습니다. 
(발표자: 서동진 매니저 / 세션명: Apache Kudu 사용사례: 산전수전kudu전) 

포스팅에 사용된 모든 사진과 글은 해당 세션의 발표자료가 출처입니다. 
국내 kudu에 대한 실 운영사례를 찾아보기 힘든데 개 중 발견한 보석입니다. 

현재 클라우데라 홈페이지에서는 2020년 자료만 조회가 됩니다. 
2019년 세션에 대한 어젠다는 아래 다른 블로그에서 확인할 수 있습니다. 

blog.daum.net/luckyman717/5851

 

01. Update로 인한 폭증 

WAL* 폭증 현상

1. 특정 Kudu 테이블에 대해 초당 최대 9백만 건 가까운 초대량 Update 수행 됨

2. 여파로 해당 테이블 Tablet 들의 WAL, DeltaMemStore 사이즈 급격하게 증가
✘ WAL DISK 사용량이 임계치 근접
✘ 해당 Kudu 테이블의 Tablet 을 많이 보유한 Tablet Server의 Memory 사용률(DeltaMemStore) 증가로 Memory Hard Limit 에 근접

3. 해당 Tablet Server 에서 Memory Pressure Rejection 발생으로 Write workload Failed 및 Retry 발생

WAL 폭증 / 본문 첨부된 pdf 원본

 

1. Memory Pressure Rejection 이 발생된 Tablet Server 에서 특정 Tablet의 DeltaMemStore 사이즈가 36GB 인 것과 Flush를 못하고 있는 현상을 발견함

2. 아래 내용을 근거로 해당 Tablet 을 Bootstrapping 을 유도하기 위해 다른 Tablet Server 로 이동(move_replica)을 수행
***Bootstrap replay process 중 다음을 수행: DMS Flush -> (Compaction) -> LogGC

3. 2번 완료 후 해당 WAL 은 GC 되고, WAL DISK 가용량이 확보 됨

4. 1번의 결과를 확인 후 해당 테이블에 대해 kudu rebalance 를 수행하여 나머지 Tablet 들에 해당하는 DMS, WAL 이 차지하고 있는 DISK 사용량을 해소 시킴


02. Write Workload Drop

사건 당시의 Kudu Tablet Server Configuration 중

--rpc_num_service_threads=30 (RPC worker thread 의 수)
--rpc_service_queue_length=150 (incoming RPC request 를 위한 queue length)

사건 당시의 Kudu Tablet Server Log 중 사건 정황

 Write request on kudu.tserver.TabletServerService from :42003 dropped due to backpressure. The service queue is full; it has 150 items.
Write request on kudu.tserver.TabletServerService from :47069 dropped due to backpressure. The service queue is full; it has 150 items.
Write request on kudu.tserver.TabletServerService from :47069 dropped due to backpressure. The service queue is full; it has 150 items.
Write request on kudu.tserver.TabletServerService from :47069 dropped due to backpressure. The service queue is full; it has 150 items.
...
Call kudu.tserver.TabletServerService.Scan from :40809 (request call id 0) took 501917ms (client timeout 10000)

 501917ms (8분 36초) 동안 Scan 을 처리하고 있었다. 
30명의 일꾼이 같은 일을 수행하고 있었다. 

Trace: 0115 15:49:57.812532 (+ 0us) service_pool.cc:163] Inserting onto call queue
0115 15:49:57.812545 (+ 13us) service_pool.cc:222] Handling call 0115 15:49:57.812816 (+ 271us) tablet_service.cc:1749] Creating iterator
0115 15:49:57.812823 (+ 7us) tablet_service.cc:2075] Waiting safe time to advance 0115 15:49:57.812825 (+ 2us) tablet_service.cc:2083] Waiting for operations to commit
0115 15:49:57.812828 (+ 3us) tablet_service.cc:2097] All operations in snapshot committed. Waited for 3 microseconds
0115 15:58:19.219526 (+501406698us) tablet_service.cc:1793] Iterator init: OK
0115 15:58:19.219531 (+ 5us) tablet_service.cc:1842] has_more: true 0115 15:58:19.219533 (+ 2us) tablet_service.cc:1857] Continuing scan request
0115 15:58:19.219614 (+ 81us) tablet_service.cc:1905] Found scanner be908690d6334aabafdaca3d83e2cab2 0115 15:58:19.720981 (+501367us) tablet_service.cc:1964] Deadline expired - responding early
0115 15:58:19.730486 (+ 9505us) inbound_call.cc:157] Queueing success response

Metrics: {"cfile_cache_hit":79552,"cfile_cache_hit_bytes":4352001,"cfile_cache_miss":60626,"cfile_cache_miss_bytes ":2919765,"delta_iterators_relevant":3219,"lbm_read_time_us":500096611,"lbm_reads_1- 10_ms":37747,"lbm_reads_10- 100_ms":16957,"lbm_reads_gt_100_ms":5,"lbm_reads_lt_1ms":5917,"spinlock_wait_cycles":10983936}

 

어떤 일을 하느라 병목이 발생했는가? 

1) Metrics:

{"cfile_cache_hit":79552,"cfile_cache_hit_bytes":4352001,"cfile_cache_miss":60626,"cfile_cache_miss_bytes ":2919765,"delta_iterators_relevant":3219,"lbm_read_time_us":500096611,"lbm_reads_1- 10_ms":37747,"lbm_reads_10- 100_ms":16957,"lbm_reads_gt_100_ms":5,"lbm_reads_lt_1ms":5917,"spinlock_wait_cycles":10983936}

 

Metrics key 설명

● cfile_cache_hit: block cache 에서 hit 된 block 개수
● cfile_cache_miss: block cache 에 존재하지 않는 block 들로 on-disk 에서 read 해야하는 대상 block 개수
    ○ cfile_cache_miss = lbm_reads_1-10_ms + lbm_reads_10-100_ms + lbm_reads_gt_100_ms + lbm_reads_lt_1ms
● delta_iterators_relevant: iterator 를 통해 연관된 DeltaFile 을 반복하여 찾는 과정 횟수
● lbm_read_time_us: miss 된 block 을 on-disk 에서 read 하는데 소요된 시간
● spinlock_wait_cycles: spinlock 이란 lock을 획득할 때까지 해당 스레드가 빙빙 돌고 있다(spinning)는 것을 의미하며, 이에 해당하는 cycle 횟수

 

2) Metrics:

{"cfile_cache_hit":79552,"cfile_cache_hit_bytes":4352001,"cfile_cache_miss":60626,"cfile_cache_miss_bytes ":2919765,"delta_iterators_relevant":3219,"lbm_read_time_us":500096611,"lbm_reads_1- 10_ms":37747,"lbm_reads_10- 100_ms":16957,"lbm_reads_gt_100_ms":5,"lbm_reads_lt_1ms":5917,"spinlock_wait_cycles":10983936}

 

Metrics 해설

● Scan 에서 처리하는데 소요된 리소스
    ○ 총 block 수: 79552 + 60626 = 140,178 blocks
    ○ 총 block 크기: 4352001 + 2919765 = 7,271,766 bytes (6.9MB)
    ○ miss 된 block 을 on-disk 에서 read 하는데 소요된 시간: 500096611 us (8분33초)
● cfile_cache_miss 된 block 수 만큼 DISK 에서 read 하여 block cache 에 insert 하는 작업만큼의 리소스를 소비하 는 것을 확인할 수 있었음
    ○ CM Chart tsquery: select kudu_block_cache_misses_caching_rate, kudu_block_cache_inserts_rate

 

이유는 너무 많은 수의 block이 생겨나서 발생 

Kudu 에 저장되는 데이터의 마지막 landing zone 인 DISK 로 flush될 때 특정 조건에 의해 너무 작은 단위 block (default 32MB) 으로 저장되고 있는 상태를 확인하였고, 이에 따라 엄청난 양의 RowSet block 들이 생성된 것을 확인하였음

● 사건 당시 Kudu에 저장된 총 Tablet Size: 385.5 TiB = 7,016,342,860 blocks
● 특정조건: Tablet Server 당 할당된 총 Memory의 Percentage consumed 가 60%를 초과하면 설정된 flush의 임계 값에 관계 없이 flush가 트리거 됨
     ○ Scheduling FlushMRSOp(091362ff2abe423fba770e1d4007ebdd): under memory pressure (60.09% used, can flush 11403031 bytes)
● RowSet Compaction은 time series 형태의 PK 성격상 범위가 겹치지 않아 기대치가 낮았으며, 과거 RowSet block 들에 대한 수동 Compaction 도 제공하지 않았음

참고) 사건 당시의 Kudu Tablet Server 의 관련 Configuration 

--flush_threshold_mb=1024 (1GB)
--flush_threshold_secs=120 (2분)
--budgeted_compaction_target_rowset_size=33554432 (32MB)
--memory_limit_hard_bytes=137438953472 (128GB)
--memory_pressure_percentage=60 (76.8GB)

 

Tuning 고려사항

1. Memory Overhead 상황을 해소 시키면, Flush 주기가 길어지기 때문에 MemRowSets 의 사이즈가 증가됨에 따라 필요로 하는 Memory 사용률이 증가될 것으로 예상 됨

2. Tablet Server 별 할당된 총 Memory의 Percentage consumed 가 60%를 초과되는 상황으로 최대한 초과되지 못 하도록 수치 조정이 필요할 것으로 예상 됨

3. Flush, Compaction 시 RowSet size 는 최대한 크게 하도록 유도

적용한 Kudu Tablet Server Configuration

--flush_threshold_mb=2048 (2GB)
--flush_threshold_secs=3600 (1시간)
--budgeted_compaction_target_rowset_size=134217728 (128MB)
--memory_limit_hard_bytes=163208757248 (152GB)
--memory_pressure_percentage=70 (106.4GB) 

 

예상한 기대 효과

1. RowSet block 개수 감소
2. Scan 시 발생한 Bottleneck 구간 해소
3. Write Workload Drop 현상 해결

결과적으로 kudu의 configuration을 변경하여 이슈를 해결하였으나 
KUDU-1400(issues.apache.org/jira/browse/KUDU-1400) 업데이트 이후 configuration 없이 해소되었다고 한다. 


그 외 운영에 좋은 Tip 

본문에 첨부된 pdf에 더 자세한 Tip들이 있으니 원본 확인해보면 좋다. 

 

다중 클러스터간 Impala External Kudu table 활용 

사용시 유의점: Query 시 가용 네트워크 리소스 고려, Query에 대한 explain 확인 필요. ad-hoc query 가 많지 않은 테이블 등등.

 

예1) ‘A’ Cluster 에서 ‘B’ Cluster 의 managed 테이블 ‘TEST01’ 을 External Table 로 생성하여 조회

CREATE EXTERNAL TABLE a_cluster_test01_ext STORE AS KUDU TBLPROPERTIES (‘kudu.master_addresses’ = ‘akudumst01:7051,akudumst02:7051,akudumst03:705 1’, ‘kudu.table_name’ = ‘TEST01’);


예2) ‘B’ Cluster 에서 ‘A’ Cluster 의 managed 테이블 ‘TEST02’ 을 External Table 로 생성하여 조회


CREATE EXTERNAL TABLE b_cluster_test02_ext STORE AS KUDU TBLPROPERTIES (‘kudu.master_addresses’ = ‘bkudumst01:7051,bkudumst02:7051,bkudumst03:705 1’, ‘kudu.table_name’ = ‘TEST02’); 

 

Replication Storm 방지법 

- 목적: 특정 Tablet Server가 Stop 되더라도 해당 Tablet Server 가 보유하고 있는 엄청난 수와 크기의 Tablet 들에 대해 RF3 을 유지하기 위해 다른 Tablet Server 로 Copy 하는 Replication Storm 현상을 사전에 방지하고자 함

CDH5.16.1-kudu-1.7.0 이전
--evict_failed_followers=false

CDH5.16.1-kudu-1.7.0 이후
--evict_failed_followers=false
--raft_prepare_replacement_before_eviction=false
--unlock_experimental_flags=true

참고) raft_prepare_replacement_before_eviction 는 어디서 파생된 설정인가?
● CDH5.13.0-kudu-1.5.0 와 CDH5.16.1-kudu-1.7.0 의 주요 차이점을 보면 3-4-3 Replication Strategy 가 추가된 것을 확인할 수 있음
● 기본값: true
● 설명: true인 경우, 실패한 replica들은 교체 대상이 준비된 이후에 evict 됨

 

디스크 활용법

Q. 과연 각각의 Tablet 들은 Kudu Tablet Server 에 장착된 디스크를 모두 사용할까?

A: No
이유는 Kudu Tablet Server Configuration 중 다음의 설정에 의존합니다.
--fs_target_data_dirs_per_tablet

● 기본값: 3
● 설명: 설정값에 따라 Tablet data 가 몇개의 디렉토리에 저장될지 결정
● 설정값에 대한 판단은 어디에 focus 를 두고 운영할지 판단해서 적절한 값을 셋팅
   ○ 예1) 최대한 가용 DISK 를 활용하여 Read/Write 성능 향상 기대
   ○ 예2) 운영중 DISK 장애 시 대처해야 할 후속 작업 최소화

 

Q. 실시간으로 들어오는 write도중 디스크 한장이 full 이 됐다면 과연 Kudu는 계속해서 write를 할까?

A: Yes
다행히도 가용한 DISK 가 있다면 해당 DISK 에 계속해서 write 합니다.
예를 들어 --fs_target_data_dirs_per_tablet 설정값이 3 인 경우 1개의 DISK 가 full 상태이고, 2개의 가용한 DISK 가 있으면 2개 DISK 에 계속해서 write 합니다. 하지만 아쉽게도 Kudu 는 DISK 별 Rebalance 를 제공하지 않습니다.
--fs_target_data_dirs_per_tablet 설정값에 따라 tablet data 가 분포 되는데, 일부 write 가 많은 tablet 이 할당된 DISK 들의 경우 다른 DISK 에 비해 빠르게 차는 것을 확인할 수 있습니다.

 

반응형

'Kudu' 카테고리의 다른 글

[Kudu] Apache Ranger로 권한 제어  (0) 2021.04.13
[kudu] 3편 - Scaling cluster  (0) 2021.04.06
[kudu] 2편 - Schema, Compaction, Disk, Memory  (0) 2021.04.06
[kudu] 1편 - write, read, partition  (0) 2021.03.30
[기초] Kudu 기본 개념  (0) 2021.03.30