ClickHouse replica 장애 — 승격 대신 무엇을 확인하나
PostgreSQL 장애 대응에 익숙하면 replica가 죽었을 때 “누구를 primary로 승격할까”부터 떠올리기 쉽습니다.
ReplicatedMergeTree에서는 질문이 다릅니다.
살아 있는 replica는 이미 쓰기를 받을 수 있고 복제 로그를 각자 따라갑니다.
따라서 장애 때 확인할 것은 승격 대상이 아니라 쓰기 주소, Keeper 연결, 미처리 복제 작업입니다.
is_leader는 쓰기 primary가 아니다
system.replicas.is_leader는 머지와 mutation 같은 작업을 배정하는 역할을 보여줍니다.
클라이언트가 쓰기를 보내야 할 단일 primary를 뜻하지 않습니다.
ClickHouse는 과거 하나의 replica가 leader 역할을 양보하던 구조에서 여러 replica가 동시에 leader가 될 수 있는 구조로 바뀌었습니다.
그 이력은 ClickHouse issue #10367 , PR #11639 , PR #11795 에서 확인할 수 있습니다.
모든 replica가 is_leader=0이면 쓰기 primary가 없어진 것이 아니라 머지 스케줄링이 멈춘 조정 이상을 의심해야 합니다.
저사양 replica를 머지 할당에서 제외하는 replicated_can_become_leader도 같은 맥락의 설정입니다.
복제는 pull로 진행된다
새 파트 정보는 Keeper의 공용 복제 로그에 등록됩니다.
각 replica는 그 로그를 자기 queue로 가져오고 필요한 파트를 다른 replica에서 fetch하거나 같은 입력으로 머지를 실행합니다.
잠깐 내려갔던 replica는 돌아온 뒤 자기 log_pointer 다음부터 다시 따라갑니다.
다른 노드가 그 replica로 모든 파트를 밀어 넣는 구조가 아닙니다.
이 차이 때문에 장애 복구는 프로세스를 띄우는 일과 queue가 비워지는 일을 나눠 봐야 합니다.
첫 쿼리는 상태 요약이다
SELECT
database,
table,
replica_name,
is_leader,
is_readonly,
absolute_delay,
queue_size,
inserts_in_queue,
merges_in_queue,
log_pointer,
log_max_index,
log_max_index - log_pointer AS log_behind,
total_replicas,
active_replicas
FROM system.replicas
ORDER BY database, table, replica_name;컬럼은 묶어서 읽어야 합니다.
| 신호 | 해석 |
|---|---|
is_readonly=1 | Keeper 세션이나 메타데이터 경로 문제로 쓰기 불가 |
absolute_delay 증가 | replica의 데이터 시각이 뒤처짐 |
log_behind 증가 | 공용 로그에서 아직 가져오지 못한 범위 증가 |
queue_size 증가 | 가져왔지만 실행하지 못한 작업 증가 |
active_replicas < total_replicas | 현재 연결 가능한 사본 감소 |
queue_size=0 하나만으로 정상 판정을 내리면 안 됩니다.
Keeper에서 새 로그를 읽지 못해 queue 자체가 늘지 않는 경우에는 log_behind와 is_readonly가 더 직접적인 신호입니다.
막힌 작업은 타입과 마지막 오류로 본다
SELECT
database,
table,
replica_name,
type,
source_replica,
new_part_name,
num_tries,
last_attempt_time,
last_exception
FROM system.replication_queue
WHERE num_tries > 0
ORDER BY last_attempt_time DESC
LIMIT 100;자주 보는 type은 다음과 같습니다.
| type | 하는 일 |
|---|---|
GET_PART | 다른 replica에서 part 받기 |
MERGE_PARTS | 같은 입력 part를 로컬에서 병합 |
MUTATE_PART | mutation 적용 |
DROP_RANGE / REPLACE_RANGE | 파티션 범위 변경 |
ATTACH_PART | part 연결 |
ALTER_METADATA | 스키마 메타데이터 반영 |
num_tries가 큰 항목의 last_exception부터 읽으면 네트워크, 없는 source part, 디스크 부족을 빠르게 가를 수 있습니다.
복구 명령 네 개는 서로 대체재가 아니다
SYSTEM SYNC REPLICA db.table;
SYSTEM RESTART REPLICA db.table;
SYSTEM RESTORE REPLICA db.table;
SYSTEM DROP REPLICA 'replica_name' FROM TABLE db.table;| 명령 | 사용할 때 | 주의 |
|---|---|---|
SYNC REPLICA | 현재 queue 처리를 기다려 완료 판정 | 원인을 고치지 않음 |
RESTART REPLICA | Keeper 상태와 queue를 다시 초기화 | 데이터 복원 명령이 아님 |
RESTORE REPLICA | Keeper 메타데이터를 잃었고 로컬 part로 등록 복원 | 선행 조건과 테이블 상태 확인 필수 |
DROP REPLICA | 영구 폐기한 replica의 Keeper 등록 정리 | 살아 있는 replica 이름에 실행 금지 |
복구 명령을 순서대로 모두 실행하는 런북은 위험합니다.
증상과 데이터 위치를 확인한 뒤 필요한 명령 하나를 골라야 합니다.
특히 DROP REPLICA는 대상 이름이 현재 동작 중인 replica가 아닌지 다른 노드에서 먼저 확인해야 합니다.
Keeper 과반을 잃으면 주소 변경으로 해결되지 않는다
Keeper 세 대 중 두 대를 잃으면 데이터 replica가 모두 살아 있어도 복제 테이블은 read-only가 될 수 있습니다.
기존 로컬 part를 읽는 SELECT는 계속될 수 있지만, 신규 part 등록이 필요한 INSERT와 DDL, 복제 조정은 진행할 수 없습니다.
소수파의 쓰기를 막는 것은 장애가 아니라 split-brain을 피하는 보호 동작입니다.
이 상태에서는 Service가 다른 ClickHouse Pod를 가리키게 해도 그 Pod 역시 Keeper 과반에 닿지 못한다면 쓰기는 살아나지 않습니다.
Service readiness는 쓰기 가능 여부까지 봐야 한다
Kubernetes Service는 readiness에 실패한 Pod를 새 연결의 endpoint에서 뺍니다.
하지만 /ping만 성공한다고 복제 테이블에 쓸 수 있다는 뜻은 아닙니다.
프로세스는 살아 있고 SELECT도 되지만 is_readonly=1일 수 있습니다.
쓰기 endpoint의 readiness나 별도 헬스 체크는 다음 조건을 구분해야 합니다.
프로세스 생존
Keeper 연결 가능
대상 ReplicatedMergeTree가 read-write
replication lag가 허용 범위native TCP 9000 연결을 일반 L4 프록시가 쿼리 단위로 다른 replica에 나눠 주는 것도 아닙니다.
한 번 열린 연결은 한 backend에 붙어 있으므로 드라이버의 재접속과 host failover 설정을 확인해야 합니다.
RF2 정비는 단일 사본 운영 시간이다
RF2에서 한 replica를 정비하면 남은 한 벌이 읽기와 쓰기를 담당합니다.
그동안 두 번째 장애가 나면 PDB가 구해주지 못합니다.
PDB는 계획된 eviction을 제한할 뿐 하드웨어 고장을 막지 않습니다.
따라서 정비 완료 조건은 Pod Running이 아닙니다.
active_replicas가 목표 RF로 복귀
log_behind가 0 또는 허용 범위
queue_size와 inserts_in_queue 정상화
absolute_delay 정상화
실제 INSERT와 SELECT 성공이 다섯 조건이 채워진 뒤 다음 replica를 건드려야 합니다.
장애 때의 확인 순서
- 클라이언트의 새 연결이 살아 있는 replica로 향하는지 봅니다.
system.replicas에서is_readonly, active 수, log와 queue 지연을 확인합니다.- Keeper 과반과 ClickHouse의 Keeper 세션을 확인합니다.
system.replication_queue의 반복 오류를 원인별로 분류합니다.- 로컬 볼륨이 남았는지, 새 볼륨에 재수화해야 하는지 구분합니다.
- 필요한 복구 명령만 실행합니다.
- queue와 지연이 정상화된 뒤 서비스 endpoint에 복귀시킵니다.
결론 : ReplicatedMergeTree 장애 대응의 중심은 primary 승격이 아니라 Keeper에 닿는 쓰기 경로와 각 replica의 pull queue입니다.
동작과 링크 상태는 2026-09-15 스냅샷입니다.