태블릿 서버 로그에 다음이 반복된다.
Remote error: failed to send heartbeat to master: Service unavailable:
TSHeartbeat request on kudu.master.MasterService from 10.0.0.11:47182
dropped due to backpressure. The service queue is full; it has 50 items.
함께 나타나는 메시지도 있다.
RPC call timeout handler was delayed by 12s! This may be due to a process-wide
pause such as swapping, logging-related delays, or allocator lock contention.
Will allow an additional 0.150s for a response.
Leader pre-election vote request: denied by peer with higher term
Kudu 의 각 RPC 서비스에는 처리를 기다리는 요청을 담는 큐가 있고, 그 큐를 비우는 워커 스레드 풀이 붙어 있다. 큐가 가득 차면 새 요청을 기다리게 하지 않고 즉시 거절한다. 이것이 backpressure 다. 쌓아 두다 한꺼번에 무너지는 대신 빨리 실패시켜 호출자가 재시도하게 만드는 설계다.
따라서 이 메시지는 원인이 아니라 증상이다. 큐가 찼다는 것은 워커가 요청을 제때 못 비웠다는 뜻이고, 그 이유를 찾아야 한다. 큐 크기만 늘리면 지연이 늘어날 뿐 처리량은 그대로다.
주요 플래그는 다음과 같다.
| 플래그 | 뜻 | 기본 |
|---|---|---|
--rpc_service_queue_length |
큐에 담을 요청 수 | 50 |
--rpc_num_service_threads |
요청을 처리할 워커 스레드 수 | 20 |
--master_ts_rpc_timeout_ms |
마스터↔태블릿 서버 RPC 시간 제한 | |
--heartbeat_interval_ms |
하트비트 주기 | 1000 |
하트비트는 마스터가 받는다. 마스터 쪽 큐가 찼다면 마스터가 바쁘다는 뜻이다. 태블릿 수가 많아지면 하트비트 한 번에 실리는 정보가 커지고 마스터 부담이 늘어난다.
curl -s http://kudu-master:8051/metrics | grep -E 'rpcs_queue_overflow|queue_length'
curl -s http://kudu-master:8051/metrics | grep -E 'threadpool.*queue_time'
rpcs_queue_overflow 가 계속 늘어나는지 본다.
timeout handler was delayed by 12s 는 프로세스가 12초 동안 멈춰 있었다는 말이다. 12초는 네트워크 지연으로 설명되지 않는 크기다. 원인은 셋 중 하나다.
스와핑이다. Kudu 는 스왑에 극도로 민감하다. vm.swappiness 를 낮추고, 가능하면 스왑을 끈다.
vmstat 1 10
sysctl vm.swappiness
grep -i swap /proc/meminfo
메모리 압박이다. 태블릿 서버가 메모리 한계에 가까워지면 할당 락 경합이 심해진다.
curl -s http://kudu-tserver:8050/metrics | grep -E 'memory_usage|memory_limit'
디스크나 로그 쓰기가 느리다. WAL 디스크가 데이터 디스크와 같은 스핀들에 있거나, 로그 수준을 높여 둔 경우다.
iostat -xz 1 5
하트비트가 끊기면 마스터는 그 서버를 죽은 것으로 보고, 리더 선출이 다시 일어난다. term changed · leader pre-election vote denied 가 함께 나오는 이유다. 시계가 어긋나면 이 판단이 더 자주 뒤집힌다.
chronyc tracking
chronyc sources -v
부분 증상만 보지 말고 클러스터 전체를 한 번 훑는다.
kudu cluster ksck kudu-master1:7051,kudu-master2:7051,kudu-master3:7051
ksck 는 태블릿별 복제 상태 · 리더 유무 · 서버 생존 여부를 한꺼번에 보여 준다. UNAVAILABLE 이나 UNDER_REPLICATED 태블릿이 얼마나 되는지로 심각도를 가늠한다. 대규모 클러스터에서는 시간이 걸리므로 범위를 좁혀 쓴다.
kudu cluster ksck <마스터목록> --tables=db.tbl
kudu cluster ksck <마스터목록> --ksck_format=json_compact
근본 원인을 고치는 것이 먼저다. 메모리를 늘리거나, 스왑을 끄거나, WAL 을 별도 디스크로 옮기거나, 태블릿 수를 줄인다.
그 위에서 스레드와 큐를 조정한다. 스레드를 먼저 늘리고, 그래도 순간 폭주가 있으면 큐를 늘린다.
--rpc_num_service_threads=32
--rpc_service_queue_length=100
태블릿이 너무 많으면 어떤 조정도 부족하다. 서버당 태블릿 수를 권장 범위 안으로 줄이는 설계 변경이 답이다. 파티션 수를 과하게 잡은 테이블이 흔한 원인이다.
큐 길이를 크게 올리면 거절 대신 대기가 늘어난다. 호출자 쪽 타임아웃이 짧으면 결국 같은 실패가 나면서 자원만 더 쓴다.
ZooKeeper 는 Kudu 의 리더 선출과 무관하다. Kudu 는 자체 Raft 합의로 리더를 정한다. ZooKeeper 설정을 손봐도 이 문제는 달라지지 않는다.