ClickHouse Keeper

ClickHouse Keeper

이 페이지는 ClickHouse Cloud에는 적용되지 않아요. 여기 문서화된 절차는 ClickHouse Cloud 서비스에서 자동화되어 있어요.

출처: 문서

본문

ClickHouse Keeper는 데이터 복제분산 DDL 쿼리 실행을 위한 조정 시스템을 제공해요. ClickHouse Keeper는 ZooKeeper와 호환돼요.

구현 세부사항 (Implementation details)

ZooKeeper는 가장 처음 널리 알려진 오픈소스 조정 시스템 중 하나예요. Java로 구현되었고 단순하고 강력한 데이터 모델을 가져요. ZooKeeper의 조정 알고리즘인 ZooKeeper Atomic Broadcast(ZAB)는 각 ZooKeeper 노드가 로컬로 읽기를 제공하므로 읽기에 대한 선형화(linearizability) 보장을 제공하지 않아요. ZooKeeper와 달리 ClickHouse Keeper는 C++로 작성되었고 RAFT 알고리즘 구현을 사용해요. 이 알고리즘은 읽기·쓰기에 선형화를 허용하고 다양한 언어로 된 여러 오픈소스 구현이 있어요. 기본적으로 ClickHouse Keeper는 ZooKeeper와 같은 보장을 제공해요: 선형화 가능한 쓰기와 비선형화 가능한 읽기. 호환되는 클라이언트-서버 프로토콜을 가지므로 표준 ZooKeeper 클라이언트를 사용해 ClickHouse Keeper와 상호작용할 수 있어요. 스냅샷과 로그는 ZooKeeper와 호환되지 않는 형식이지만, clickhouse-keeper-converter 도구가 ZooKeeper 데이터를 ClickHouse Keeper 스냅샷으로 변환할 수 있게 해줘요. ClickHouse Keeper의 interserver 프로토콜도 ZooKeeper와 호환되지 않으므로 혼합 ZooKeeper / ClickHouse Keeper 클러스터는 불가능해요. ClickHouse Keeper는 ZooKeeper와 같은 방식으로 Access Control Lists(ACL)를 지원해요. ClickHouse Keeper는 같은 권한 집합을 지원하고 동일한 내장 스킴을 가져요: world, auth, digest. digest 인증 스킴은 username:password 쌍을 사용하고, 비밀번호는 Base64로 인코딩돼요.

외부 통합은 지원되지 않아요.

구성 (Configuration)

ClickHouse Keeper는 ZooKeeper의 독립 실행 대체품으로 사용될 수도 있고, ClickHouse 서버의 내부 부분으로 사용될 수도 있어요. 두 경우 모두 구성은 거의 같은 .xml 파일이에요.

Keeper 구성 설정 (Keeper configuration settings)

주요 ClickHouse Keeper 구성 태그는 <keeper_server>이며 다음 매개변수를 가져요:

매개변수 설명 기본 값
tcp_port 클라이언트가 연결할 포트. 2181
tcp_port_secure 클라이언트와 keeper-server 사이의 SSL 연결을 위한 보안 포트. -
server_id 고유 서버 ID, ClickHouse Keeper 클러스터의 각 참가자는 고유한 번호(1, 2, 3…)를 가져야 해요. -
log_storage_path 조정 로그 경로, ZooKeeper처럼 로그는 여유가 있는 노드에 저장하는 것이 가장 좋아요. -
snapshot_storage_path 조정 스냅샷 경로. -
enable_reconfiguration reconfig를 통한 동적 클러스터 재구성 활성화. False
max_memory_usage_soft_limit keeper 최대 메모리 사용량의 스프트 제한(바이트). max_memory_usage_soft_limit_ratio * physical_memory_amount
max_memory_usage_soft_limit_ratio max_memory_usage_soft_limit이 설정되지 않았거나 0이면 이 값을 사용해 기본 소프트 제한을 정의해요. 0.9
cgroups_memory_observer_wait_time max_memory_usage_soft_limit이 설정되지 않았거나 0이면 이 간격으로 물리 메모리 양을 관찰해요. 메모리 양이 변경되면 max_memory_usage_soft_limit_ratio로 Keeper의 메모리 소프트 제한을 다시 계산해요. 15
http_control HTTP 제어 인터페이스 구성. -
digest_enabled 실시간 데이터 일관성 검사 활성화 True
create_snapshot_on_exit 종료 중 스냅샷 생성 -
hostname_checks_enabled 클러스터 구성을 위한 호스트 이름 온전성 검사 활성화 (예: localhost가 원격 엔드포인트와 사용됨) True
four_letter_word_white_list 4lw 명령의 화이트리스트. conf, cons, crst, envi, ruok, srst, srvr, stat, wchs, dirs, mntr, isro, rcvr, apiv, csnp, lgif, rqld, rclc, clrs, ftfl, ydld, bpon, bpof, pfev, lgrq, jemalloc이 있는 빌드에서는 jmst, jmfp, jmep, jmdp 추가
enable_ipv6 IPv6 활성화 True

다른 공통 매개변수는 ClickHouse 서버 구성에서 상속돼요(listen_host, logger 등).

내부 조정 설정 (Internal coordination settings)

내부 조정 설정은 <keeper_server>.<coordination_settings> 섹션에 있으며 다음 매개변수를 가져요:

매개변수 설명 기본 값
operation_timeout_ms 단일 클라이언트 연산의 타임아웃 (ms) 10000
min_session_timeout_ms 클라이언트 세션의 최소 타임아웃 (ms) 10000
session_timeout_ms 클라이언트 세션의 최대 타임아웃 (ms) 100000
dead_session_check_period_ms ClickHouse Keeper가 죽은 세션을 검사하고 제거하는 주기 (ms) 500
heart_beat_interval_ms ClickHouse Keeper 리더가 팔로워에게 하트비트를 보내는 주기 (ms) 500
election_timeout_lower_bound_ms 팔로워가 이 간격 동안 리더로부터 하트비트를 받지 못하면 리더 선거를 시작할 수 있어요. election_timeout_upper_bound_ms보다 작거나 같아야 해요. 이상적으로는 같지 않아야 해요. 1000
election_timeout_upper_bound_ms 팔로워가 이 간격 동안 리더로부터 하트비트를 받지 못하면 반드시 리더 선거를 시작해야 해요. 2000
rotate_log_storage_interval 단일 파일에 저장할 로그 레코드 수. 100000
reserved_log_items 압축 전에 저장할 조정 로그 레코드 수. 100000
snapshot_distance ClickHouse Keeper가 새 스냅샷을 만들 주기 (로그 레코드 수). 100000
snapshots_to_keep 유지할 스냅샷 수. 3
compress_snapshots_with_zstd_format 사용자 지정 ClickHouse LZ4 블록 형식 대신 ZSTD 형식으로 스냅샷을 씁니다. true
snapshot_zstd_compression_level 스냅샷용 ZSTD 압축 수준. 낮은 수준은 CPU를 덜 사용하지만 더 큰 스냅샷을 만들어요. 음수 값은 ZSTD fast 수준을 선택해요. compress_snapshots_with_zstd_format이 활성화된 경우에만 사용돼요. 3
stale_log_gap 리더가 팔로워를 오래된 것으로 간주하고 로그 대신 스냅샷을 보내는 임계값. 10000
fresh_log_gap 노드가 최신 상태가 되는 때. 200
max_requests_batch_size RAFT로 보내기 전 요청 개수의 배치 최대 크기. 100
force_sync 조정 로그에 대한 각 쓰기마다 fsync 호출. true
quorum_reads 읽기 요청을 비슷한 속도의 전체 RAFT 합의를 통한 쓰기로 실행. false
raft_logs_level 조정에 대한 텍스트 로깅 수준 (trace, debug 등). system default
auto_forwarding 팔로워에서 리더로 쓰기 요청을 전달 허용. true
shutdown_timeout 내부 연결을 끝내고 종료할 때까지 기다림 (ms). 5000
startup_timeout 서버가 지정된 타임아웃 안에 다른 쿼럼 참가자에 연결하지 못하면 종료해요 (ms). 30000
async_replication 비동기 복제 활성화. 더 나은 성능을 얻으면서 모든 쓰기·읽기 보장이 보존돼요. 이 설정은 하위 호환성을 깨지 않도록 기본적으로 비활성화돼요 false
latest_logs_cache_entry_count_threshold 최신 로그 항목의 인메모리 캐시 최대 항목 수 200000
latest_logs_cache_size_threshold 최신 로그 항목의 인메모리 캐시가 보유하는 최대 메모리 (항목별 할당 오버헤드 포함) 1GiB
commit_logs_cache_size_threshold 사용 중단됨. 이 설정 자체가 설정되지 않았으면 log_readahead_commit_window_bytes에 사용됨. -
commit_logs_cache_entry_count_threshold 사용 중단됨, 효과 없음. 대신 log_readahead_commit_window_bytes를 사용. -
log_readahead_enabled changelog 따라잡기 읽기(팔로워 복제)를 위한 피어별 디코딩된 read-ahead 활성화. false
log_readahead_window_bytes 피어 리더당 버퍼링되는 디코딩된 항목의 최대 바이트. 일반적인 append-entries 배치만큼 크거나 같아야 해요. 64MiB
log_readahead_max_peer_readers 동시 피어별 read-ahead 리더의 최대 수. 8
log_readahead_eviction_timeout_ms 비활성 피어별 read-ahead 리더 또는 커밋 리더를 퇴거시키는 유휴 타임아웃 (이후 놓치면 재생성됨). 30000
log_readahead_pool_threads 전용 read-ahead 스레드 풀의 스레드 수. 0log_readahead_max_peer_readers에서 값을 파생해요. 0
log_readahead_serve_wait_timeout_ms 직접 읽기로 폴백하기 전에 백그라운드 read-ahead 채움을 기다리는 최대 시간. 200
log_readahead_chunk_size read-ahead 채움 작업에서 청크당 디코딩되는 로그 항목 수. 16
log_readahead_commit_window_bytes 커밋 스레드 앞에 버퍼링되는 디코딩된 로그 항목의 최대 총 크기. 이것은 프리페치 윈도우이지 캐시가 아니에요: 항목은 하나의 채움 작업에 의해 순서대로 생성되고 커밋 스레드가 소비할 때 팝되므로, 윈도우는 두 속도 사이의 간격만 커버하면 돼요. 0은 커밋 read-ahead를 비활성화해요(커밋은 디스크에서 항목을 하나씩 읽음). 16MiB
log_startup_read_max_streams Keeper 시작 중 동시에 읽는 changelog 파일 최대 수. 0 = CPU 코어 수를 자동 사용. 1 = 직렬(병렬화 전) 시작 읽기 사용. 유효 병렬성은 읽어야 할 changelog 파일 수로 제한되며, 탐색 중심 저장소(HDD, IOPS 제한 볼륨)에서는 낮추는 것을 고려. 0
log_startup_read_buffer_size Keeper 시작 시 changelog를 읽는 동안 사용되는 스트림별 읽기 버퍼 크기(바이트). 0보다 커야 해요. 버퍼는 추가로 파일 크기로 제한돼요. 8MiB
disk_move_retries_wait_ms 파일이 디스크 간 이동되는 동안 실패 후 재시도 사이 기다리는 시간 1000
disk_move_retries_during_init 초기화 중 디스크 간 파일 이동 동안 실패 후 재시도 횟수 100
experimental_use_rocksdb 백엔드 저장소로 rocksdb 사용 0
slow_member_backpressure_no_progress_timeout_ms 리더가 bpon으로 백프레셔가 켜진 동안 전혀 진행하지 않는 레플리카를 뒤에 남겨두기까지 기다리는 시간. 진행이란 수락된 응답(로그 항목 취득 또는 스냅샷 객체 저장)을 의미하므로, 단순히 느린 레플리카는 이 타이머를 계속 리셋하고 멈춘 레플리카만 대기 대상에서 제외돼요. 기본 값은 5분이며 그만큼 크도록 의도되었어요: 큰 스냅샷을 적용하는 레플리카는 몇 분간 조용할 수 있고 바로 기다릴 가치가 있는 것이므로. 리더가 전혀 도달할 수 없는 레플리카는 이 값을 기다리지 않고 즉시 버려져요. 0은 리더가 도달할 수 있는 레플리카를 절대 기다리지 않는 것을 의미해요. 300000
slow_member_backpressure_max_uncommitted_log_entries bpon으로 slow member backpressure가 켜진 동안 nuraft_max_uncommitted_log_entries가 취하는 값. 커밋 인덱스를 보류해도 리더가 추가하는 것은 막지 않으므로, 더 타이트한 제한이 없으면 로그가 더 앞서 나가고 레플리카는 따라잡는 대신 더 뒤처져요. 더 타이트하게 만들면 레플리카가 간격을 좁힐 때까지 리더가 재시도 가능한 오류로 새 요청을 거부해요. 0은 제한을 변경하지 않음. 0

쿼럼 구성은 <keeper_server>.<raft_configuration> 섹션에 있으며 서버 설명을 포함해요. 전체 쿼럼에 대한 유일한 매개변수는 secure로, 쿼럼 참가자 간 통신을 위한 암호화 연결을 활성화해요. 노드 간 내부 통신에 SSL 연결이 필요하면 true로 설정할 수 있고, 그렇지 않으면 지정하지 않고 둘 수 있어요. 각 <server>의 주요 매개변수는 다음과 같아요:

  • id — 쿼럼에서의 서버 식별자.
  • hostname — 이 서버가 위치한 호스트 이름.
  • port — 이 서버가 연결을 수신하는 포트.
  • can_become_leader — 서버를 learner로 설정하려면 false로 설정. 생략하면 값은 true.

ClickHouse Keeper 클러스터의 토폴로지가 변경되면(예: 서버 교체) server_id에서 hostname으로의 매핑이 일관되게 유지되고 기존 server_id를 다른 서버에 섞거나 재사용하지 않도록 해요(예: ClickHouse Keeper 배포에 자동화 스크립트를 의존하면 발생할 수 있음). Keeper 인스턴스의 호스트가 변경될 수 있다면 원시 IP 주소 대신 호스트 이름을 정의하고 사용할 것을 권장해요. 호스트 이름 변경은 서버를 제거했다가 다시 추가하는 것과 같으며, 어떤 경우에는 불가능할 수 있어요(예: 쿼럼에 충분한 Keeper 인스턴스가 없는 경우).

async_replication은 하위 호환성을 깨지 않도록 기본적으로 비활성화돼요. 클러스터의 모든 Keeper 인스턴스가 async_replication을 지원하는 버전(v23.9+)을 실행 중이라면 단점 없이 성능을 개선할 수 있으므로 활성화를 권장해요.

3-노드 쿼럼 구성의 예는 통합 테스트에서 test_keeper_ 접두사로 찾을 수 있어요. 서버 #1의 예제 구성:

<keeper_server>
    <tcp_port>2181</tcp_port>
    <server_id>1</server_id>
    <log_storage_path>/var/lib/clickhouse/coordination/log</log_storage_path>
    <snapshot_storage_path>/var/lib/clickhouse/coordination/snapshots</snapshot_storage_path>

    <coordination_settings>
        <operation_timeout_ms>10000</operation_timeout_ms>
        <session_timeout_ms>30000</session_timeout_ms>
        <raft_logs_level>trace</raft_logs_level>
    </coordination_settings>

    <raft_configuration>
        <server>
            <id>1</id>
            <hostname>zoo1</hostname>
            <port>9234</port>
        </server>
        <server>
            <id>2</id>
            <hostname>zoo2</hostname>
            <port>9234</port>
        </server>
        <server>
            <id>3</id>
            <hostname>zoo3</hostname>
            <port>9234</port>
        </server>
    </raft_configuration>
</keeper_server>

실행 방법 (How to run)

ClickHouse Keeper는 ClickHouse 서버 패키지에 번들되어 있어요. /etc/your_path_to_config/clickhouse-server/config.xml<keeper_server> 구성을 추가하고 평소처럼 ClickHouse 서버를 시작하면 돼요. 독립 실행형 ClickHouse Keeper를 실행하려면 비슷한 방식으로 다음으로 시작할 수 있어요:

clickhouse-keeper --config /etc/your_path_to_config/config.xml

심링크(clickhouse-keeper)가 없으면 심링크를 만들거나 clickhouse에 인자로 keeper를 지정할 수 있어요:

clickhouse keeper --config /etc/your_path_to_config/config.xml

네 글자 단어 명령 (Four letter word commands)

ClickHouse Keeper는 Zookeeper와 거의 같은 4lw 명령도 제공해요. 각 명령은 mntr, stat 같은 네 글자로 구성돼요. 더 흥미로운 명령이 몇 가지 있어요: stat은 서버와 연결된 클라이언트에 대한 일반 정보를 주고, srvr은 서버에 대한 확장된 세부사항을 주며, cons는 연결에 대한 확장된 세부사항을 줘요. 4lw 명령에는 기본 값이 conf,cons,crst,envi,ruok,srst,srvr,stat,wchs,dirs,mntr,isro,rcvr,apiv,csnp,lgif,rqld,rclc,clrs,ftfl,ydld,bpon,bpof,pfev,lgrqfour_letter_word_white_list 구성이 있으며, jemalloc이 있는 빌드에서는 jmst,jmfp,jmep,jmdp가 추가돼요. 목록은 시작 시 한 번 읽히므로 변경하려면 재시작이 필요해요. 클라이언트 포트에서 telnet이나 nc로 ClickHouse Keeper에 명령을 보낼 수 있어요.

echo mntr | nc localhost 9181

자세한 4lw 명령은 다음과 같아요:

  • ruok: 서버가 오류가 없는 상태로 실행 중인지 테스트. 실행 중이면 서버가 imok로 응답해요. 그렇지 않으면 전혀 응답하지 않아요. imok 응답은 반드시 서버가 쿼럼에 참여했다는 뜻은 아니고, 서버 프로세스가 활성 상태이고 지정된 클라이언트 포트에 바인딩되어 있다는 뜻뿐이에요. 쿼럼과 클라이언트 연결 정보에 대한 상태 세부사항은 "stat"을 사용하세요.
imok
  • mntr: 클러스터의 상태를 모니터링하는 데 사용할 수 있는 변수 목록을 출력해요.
zk_version      v21.11.1.1-prestable-7a4a0b0edef0ad6e0aa662cd3b90c3f4acf796e7
zk_avg_latency  0
zk_max_latency  0
zk_min_latency  0
zk_packets_received     68
zk_packets_sent 68
zk_num_alive_connections        1
zk_outstanding_requests 0
zk_server_state leader
zk_slow_member_backpressure      0
zk_znode_count  4
zk_watch_count  1
zk_ephemerals_count     0
zk_approximate_data_size        723
zk_open_file_descriptor_count   310
zk_max_file_descriptor_count    10240
zk_leader_uptime        1234
zk_sum_leader_unavailable_time 1234
zk_cnt_leader_unavailable_time 1
zk_sum_election_time 1234
zk_cnt_election_time 1
zk_followers    0
zk_synced_followers     0

zk_sum_leader_unavailable_time, zk_cnt_leader_unavailable_time, zk_sum_election_time, zk_cnt_election_time은 리더 전용 누적 지표예요. 이들은 서버별 관측치이지 클러스터 전체 가용성 측정이 아니에요: 추적은 해당 서버가 살아있는 리더를 관찰하기를 멈출 때 시작돼요. 네트워크 분할 중에는 오래된 리더가 다른 파티션에서 살아 있는 시간이 포함될 수 있어요. 선거 지표는 로컬에서 관찰된 무-리더 윈도우를 따른 성공적인 선거만 기록하며, 샘플링된 무-리더 상태를 노출하지 않는 리더십 전이는 의도적으로 계산되지 않아요. Keeper는 로컬 NuRaft 리더 상태를 heart_beat_interval_ms마다 한 번 샘플링하지만 100밀리초보다 자주는 아니에요. 각 경계는 유효 간격만큼 로컬 상태 전이와 다를 수 있고, 그 간격보다 짧은 윈도우는 놓칠 수 있어요. Keeper는 NuRaft BecomeLeader에서 로컬 선거 완료를 기록해요. srst는 네 값을 모두 리셋해요. 각 종류의 가장 최근 로컬 관찰 윈도우 지속 시간도 KeeperLastLeaderElectionTimeKeeperLastLeaderUnavailableTime으로 system.asynchronous_metrics에 밀리초 단위로 내보내져요. KeeperLastLeaderElectionTimezk_sum_election_time과 같은 무-리더 윈도우 정의를 따라요. 둘 다 리더 전용이에요: 활성 리더가 아니거나 아직 윈도우를 완료하지 않은 노드는 0을 보고해요. srst는 누적 카운터와 함께 이것들을 리셋해요.

  • srvr: 서버에 대한 전체 세부사항을 나열해요.
ClickHouse Keeper version: v21.11.1.1-prestable-7a4a0b0edef0ad6e0aa662cd3b90c3f4acf796e7
Latency min/avg/max: 0/0/0
Received: 2
Sent : 2
Connections: 1
Outstanding: 0
Zxid: 34
Mode: leader
Node count: 4
  • stat: 서버와 연결된 클라이언트에 대한 간략한 세부사항을 나열해요.
ClickHouse Keeper version: v21.11.1.1-prestable-7a4a0b0edef0ad6e0aa662cd3b90c3f4acf796e7
Clients:
 192.168.1.1:52852(recved=0,sent=0)
 192.168.1.1:52042(recved=24,sent=48)
Latency min/avg/max: 0/0/0
Received: 4
Sent : 4
Connections: 1
Outstanding: 0
Zxid: 36
Mode: leader
Node count: 4
  • srst: 서버 통계를 리셋해요. 명령은 srvr, mntr, stat의 결과에 영향을 줘요.
Server stats reset.
  • conf: 서빙 구성에 대한 세부사항을 출력해요.
server_id=1
tcp_port=2181
four_letter_word_white_list=*
log_storage_path=./coordination/logs
snapshot_storage_path=./coordination/snapshots
max_requests_batch_size=100
session_timeout_ms=30000
operation_timeout_ms=10000
dead_session_check_period_ms=500
heart_beat_interval_ms=500
election_timeout_lower_bound_ms=1000
election_timeout_upper_bound_ms=2000
reserved_log_items=1000000000000000
snapshot_distance=10000
auto_forwarding=true
shutdown_timeout=5000
startup_timeout=240000
raft_logs_level=information
snapshots_to_keep=3
rotate_log_storage_interval=100000
stale_log_gap=10000
fresh_log_gap=200
max_requests_batch_size=100
quorum_reads=false
force_sync=false
compress_logs=true
compress_snapshots_with_zstd_format=true
snapshot_zstd_compression_level=3
configuration_change_tries_count=20
  • cons: 이 서버에 연결된 모든 클라이언트의 전체 연결/세션 세부사항을 나열해요. 받은/보낸 패킷 수, 세션 ID, 연산 지연, 마지막 수행된 연산 등의 정보를 포함해요.
 192.168.1.1:52163(recved=0,sent=0,sid=0xffffffffffffffff,lop=NA,est=1636454787393,to=30000,lzxid=0xffffffffffffffff,lresp=0,llat=0,minlat=0,avglat=0,maxlat=0)
 192.168.1.1:52042(recved=9,sent=18,sid=0x0000000000000001,lop=List,est=1636454739887,to=30000,lcxid=0x0000000000000005,lzxid=0x0000000000000005,lresp=1636454739892,llat=0,minlat=0,avglat=0,maxlat=0)
  • crst: 모든 연결의 연결/세션 통계를 리셋해요.
Connection stats reset.
  • envi: 서빙 환경에 대한 세부사항을 출력해요.
Environment:
clickhouse.keeper.version=v21.11.1.1-prestable-7a4a0b0edef0ad6e0aa662cd3b90c3f4acf796e7
host.name=ZBMAC-C02D4054M.local
os.name=Darwin
os.arch=x86_64
os.version=19.6.0
cpu.count=12
user.name=root
user.home=/Users/JackyWoo/
user.dir=/Users/JackyWoo/project/jd/clickhouse/cmake-build-debug/programs/
user.tmp=/var/folders/b4/smbq5mfj7578f2jzwn602tt40000gn/T/
  • dirs: 스냅샷과 로그 파일의 총 크기를 바이트로 표시해요.
snapshot_dir_size: 0
log_dir_size: 3875
  • isro: 서버가 읽기 전용 모드로 실행되는지 테스트. 읽기 전용이면 ro, 아니면 rw로 응답해요.
rw
  • wchs: 서버에 대한 감시자(watch)의 간략한 정보를 나열해요.
1 connections watching 1 paths
Total watches:1
  • wchc: 세션별로 서버에 대한 감시자의 자세한 정보를 나열해요. 연관된 감시자(경로)가 있는 세션(연결) 목록을 출력해요. 감시자 수에 따라 이 연산은 비쌀 수 있고(서버 성능에 영향), 주의해서 사용하세요.
0x0000000000000001
    /clickhouse/task_queue/ddl
  • wchp: 경로별로 서버에 대한 감시자의 자세한 정보를 나열해요. 연관된 세션이 있는 경로(znode) 목록을 출력해요. 감시자 수에 따라 이 연산은 비쌀 수 있고(즉, 서버 성능에 영향), 주의해서 사용하세요.
/clickhouse/task_queue/ddl
    0x0000000000000001
  • dump: 미결 세션과 임시 노드를 나열해요. 리더에서만 작동해요.
Sessions dump (2):
0x0000000000000001
0x0000000000000002
Sessions with Ephemerals (1):
0x0000000000000001
 /clickhouse/task_queue/ddl
  • csnp: 스냅샷 생성 작업을 예약해요. 성공하면 예약된 스냅샷의 마지막 커밋된 로그 인덱스를, 실패하면 Failed to schedule snapshot creation task.를 반환해요. lgif 명령으로 스냅샷 완료 여부를 확인할 수 있어요.
100
  • lgif: Keeper 로그 정보. first_log_idx : 로그 저장소의 첫 로그 인덱스; first_log_term : 첫 로그 용어; last_log_idx : 로그 저장소의 마지막 로그 인덱스; last_log_term : 마지막 로그 용어; last_committed_log_idx : 상태 머신의 마지막 커밋된 로그 인덱스; leader_committed_log_idx : 내 관점에서 리더의 커밋된 로그 인덱스; target_committed_log_idx : 커밋되어야 하는 대상 로그 인덱스; last_snapshot_idx : 마지막 스냅샷의 가장 큰 커밋된 로그 인덱스.
first_log_idx   1
first_log_term  1
last_log_idx    101
last_log_term   1
last_committed_log_idx  100
leader_committed_log_idx    101
target_committed_log_idx    101
last_snapshot_idx   50
  • rqld: 새 리더가 되도록 요청. 요청이 보내졌으면 Sent leadership request to leader.를, 보내지지 않았으면 Failed to send leadership request to leader.를 반환해요. 노드가 이미 리더면 결과는 요청이 보내졌을 때와 같아요.
Sent leadership request to leader.
  • ftfl: Keeper 인스턴스에 대해 모든 기능 플래그와 활성화 여부를 나열해요.
filtered_list   1
multi_read  1
check_not_exists    0
  • bpon: 따라잡지 못하는 레플리카를 리더가 기다리도록 요청. 켜져 있는 동안 리더는 도달할 수 있는 어떤 레플리카 너머로도 커밋 인덱스를 전진시키지 않으므로, 뒤처진 레플리카가 스냅샷이 필요할 때까지 드리프트하는 대신 간격을 좁힐 수 있어요. 쓰기는 가장 느린 도달 가능한 투표 레플리카의 속도로 커밋되므로 의도적으로 켜고 레플리카가 따라잡는 것을 지켜본 다음 bpof로 다시 꺼요. 지연 임계값은 없어요: 리더가 도달할 수 있는 모든 투표 레플리카는 아무리 뒤처져 있어도, 스냅샷을 받는 중이라도 기다려져요. can_become_leaderfalse로 설정된 레플리카는 투표하지 않고 결코 기다려지지 않으므로, 여전히 스냅샷이 필요할 때까지 드리프트할 수 있어요 - 백프레셔가 그것을 보호하지 않아요. 의도가 단지 레플리카가 리더가 되지 않게 하는 것이라면 대신 priority 0을 주세요: 그것은 여전히 투표하므로 여전히 기다려지고, can_become_leader false는 그것을 쿼럼에서 완전히 제외시켜요. 레플리카가 기다려서 얻을 것이 없을 때만 제외돼요 - 마지막 요청이 실패했거나, slow_member_backpressure_no_progress_timeout_ms 안에 진행을 하지 않았거나, 새 연결에 아직 전혀 응답하지 않았고 그것에게 보내는 스냅샷도 없어서 리더가 어디에 있는지 알 수 없거나, 리더의 로그 범위 밖에 있어 로그나 스냅샷 모두 회복할 수 없을 때 - 왜냐하면 그런 레플리카를 기다리면 커밋 인덱스를 헛되이 고정시키기 때문이에요. slow_member_backpressure_max_uncommitted_log_entries도 설정하세요. 커밋 인덱스를 보류해도 리더가 추가하는 것을 막지 않으므로, 그게 없으면 레플리카가 기다려지는 동안 로그가 계속 커져요. 명령은 어떤 노드에든 보낼 수 있어요: 팔로워는 현재 리더에게 전달하고 로컬로는 아무것도 전환하지 않으므로, 설정을 보유하는 것은 리더뿐이에요. 리더에서 mntrzk_slow_member_backpressure로 확인해요(리더는 zk_server_state가 식별). 설정은 한 번의 리더십 동안 지속돼요 - 리더 변경 시 꺼집니다.
$ echo bpon | nc localhost 9181   # sent to the leader
Slow member backpressure is ON. Writes now commit at the speed of the slowest reachable voting replica; replicas with `can_become_leader` off are not waited for. A new leader switches it off, so send this again after a leader change.

$ echo bpon | nc localhost 9181   # sent to a follower
Sent slow member backpressure ON request to leader. Check `zk_slow_member_backpressure` in `mntr` on the leader, which `zk_server_state` identifies, to see whether it took effect.
  • bpof: 따라잡지 못하는 레플리카를 기다리지 말라고 리더에게 요청. 어떤 노드에든 보낼 수 있고 bpon과 같은 방식으로 리더에 도달해요.
$ echo bpof | nc localhost 9181
Sent slow member backpressure OFF request to leader.
  • ydld: 리더십을 양보하고 팔로워가 되도록 요청. 요청을 받는 서버가 리더면 먼저 쓰기 작업을 일시 중지하고, 후임자(현재 리더는 후임자가 될 수 없음)가 최신 로그의 따라잡기를 끝낼 때까지 기다린 다음 사임해요. 후임자는 자동으로 선택돼요. 요청이 보내졌으면 Sent yield leadership request to leader.를, 보내지지 않았으면 Failed to send yield leadership request to leader.를 반환해요. 노드가 이미 팔로워면 결과는 요청이 보내졌을 때와 같아요.
Sent yield leadership request to leader.
  • pfev: 수집된 모든 이벤트의 값을 반환해요. 각 이벤트에 대해 이벤트 이름, 이벤트 값, 이벤트 설명을 반환해요.
FileOpen        62      Number of files opened.
Seek    4       Number of times the 'lseek' function was called.
ReadBufferFromFileDescriptorRead        126     Number of reads (read/pread) from a file descriptor. Does not include sockets.
ReadBufferFromFileDescriptorReadFailed  0       Number of times the read (read/pread) from a file descriptor have failed.
ReadBufferFromFileDescriptorReadBytes   178846  Number of bytes read from file descriptors. If the file is compressed, this will show the compressed data size.
WriteBufferFromFileDescriptorWrite      7       Number of writes (write/pread) to a file descriptor. Does not include sockets.
WriteBufferFromFileDescriptorWriteFailed        0       Number of times the write (write/pwrite) to a file descriptor have failed.
WriteBufferFromFileDescriptorWriteBytes 153     Number of bytes written to file descriptors. If the file is compressed, this will show compressed data size.
FileSync        2       Number of times the F_FULLFSYNC/fsync/fdatasync function was called for files.
DirectorySync   0       Number of times the F_FULLFSYNC/fsync/fdatasync function was called for directories.
FileSyncElapsedMicroseconds     12756   Total time spent waiting for F_FULLFSYNC/fsync/fdatasync syscall for files.
DirectorySyncElapsedMicroseconds        0       Total time spent waiting for F_FULLFSYNC/fsync/fdatasync syscall for directories.
ReadCompressedBytes     0       Number of bytes (the number of bytes before decompression) read from compressed sources (files, network).
CompressedReadBufferBlocks      0       Number of compressed blocks (the blocks of data that are compressed independent of each other) read from compressed sources (files, network).
CompressedReadBufferBytes       0       Number of uncompressed bytes (the number of bytes after decompression) read from compressed sources (files, network).
AIOWrite        0       Number of writes with Linux or FreeBSD AIO interface
AIOWriteBytes   0       Number of bytes written with Linux or FreeBSD AIO interface
...

HTTP 제어 (HTTP control)

ClickHouse Keeper는 레플리카가 트래픽을 받을 준비가 되었는지 확인하는 HTTP 인터페이스를 제공해요. Kubernetes 같은 클라우드 환경에서 사용될 수 있어요. /ready 엔드포인트를 활성화하는 구성 예제:

<clickhouse>
    <keeper_server>
        <http_control>
            <port>9182</port>
            <readiness>
                <endpoint>/ready</endpoint>
            </readiness>
        </http_control>
    </keeper_server>
</clickhouse>

기능 플래그 (Feature flags)

Keeper는 ZooKeeper와 그 클라이언트와 완전히 호환되지만, ClickHouse 클라이언트가 사용할 수 있는 몇 가지 고유 기능과 요청 유형도 도입해요. 이러한 기능은 하위 호환성이 없는 변경을 도입할 수 있으므로 대부분 기본적으로 비활성화되어 있고 keeper_server.feature_flags 구성으로 활성화할 수 있어요. 모든 기능은 명시적으로 비활성화할 수 있어요. Keeper 클러스터에 새 기능을 활성화하려면 먼저 클러스터의 모든 Keeper 인스턴스를 기능을 지원하는 버전으로 업데이트한 다음 기능 자체를 활성화할 것을 권장해요. multi_read를 비활성화하고 check_not_exists를 활성화하는 기능 플래그 구성 예제:

<clickhouse>
    <keeper_server>
        <feature_flags>
            <multi_read>0</multi_read>
            <check_not_exists>1</check_not_exists>
        </feature_flags>
    </keeper_server>
</clickhouse>

다음 기능을 사용할 수 있어요:

기능 설명 기본 값
multi_read 읽기 multi 요청 지원 1
filtered_list 노드 유형(임시 또는 영구)으로 결과를 필터링하는 list 요청 지원 1
check_not_exists 노드가 존재하지 않음을 단언하는 CheckNotExists 요청 지원 1
create_if_not_exists 노드가 없으면 만들려고 시도하는 CreateIfNotExists 요청 지원. 있으면 변경이 적용되지 않고 ZOK가 반환됨 1
remove_recursive 노드를 하위 트리와 함께 제거하는 RemoveRecursive 요청 지원 1

일부 기능 플래그는 버전 25.7부터 기본적으로 활성화돼요. Keeper를 25.7+로 업그레이드하는 권장 방법은 먼저 24.9+ 버전으로 업그레이드하는 것이에요.

ZooKeeper에서 마이그레이션하기 (Migration from ZooKeeper)

ZooKeeper에서 ClickHouse Keeper로의 원활한 마이그레이션은 불가능해요. ZooKeeper 클러스터를 중지하고, 데이터를 변환하고, ClickHouse Keeper를 시작해야 해요. clickhouse-keeper-converter 도구는 ZooKeeper 로그와 스냅샷을 ClickHouse Keeper 스냅샷으로 변환해요. ZooKeeper 3.4 이상이 필요해요.

마이그레이션 전 준비 (Pre-migration preparation)

마이그레이션은 데이터 수집 중지를 요구해요. 시작하기 전에 유지보수 창을 계획해요. ZooKeeper를 중지하기 전에 조정 메타데이터를 수정하는 ClickHouse 백그라운드 작업을 중지해요. 예를 들면:

SYSTEM STOP MERGES;

나중에 일관성을 검증할 수 있도록 마이그레이션 전에 비교 지표를 기록해요.

마이그레이션 단계 (Migration steps)

  1. 모든 ClickHouse 노드로의 데이터 수집을 중지해요.
  2. 모든 ClickHouse 노드에서 모든 백그라운드 작업을 중지해요(위 참조).
  3. 모든 ZooKeeper 노드를 중지해요.
  4. 선택 사항이지만 권장: ZooKeeper 리더 노드를 찾아 다시 시작하고 중지해요. 이렇게 하면 변환 전에 ZooKeeper가 일관된 스냅샷을 디스크에 쓰도록 강제해요.
  5. 리더 노드에서 clickhouse-keeper-converter를 실행해요. 전체 ClickHouse 바이너리가 설치되어 있으면 대신 keeper-converter 하위 명령(clickhouse keeper-converter)을 사용해요. 둘 다 없으면 바이너리를 다운로드하세요.
clickhouse-keeper-converter \
  --zookeeper-logs-dir /var/lib/zookeeper/version-2 \
  --zookeeper-snapshots-dir /var/lib/zookeeper/version-2 \
  --output-dir /path/to/clickhouse/keeper/snapshots
  1. 스냅샷을 모든 ClickHouse Keeper 노드에 복사해요. 어떤 노드도 시작하기 전에 스냅샷이 모든 노드에 있어야 해요 — 스냅샷 없이 시작하면 노드가 빈 상태로 스스로 리더를 선출할 수 있어요.
  2. ClickHouse 구성을 업데이트해 새 Keeper 클러스터를 가리키게 해요.
  3. 모든 노드에서 ClickHouse Keeper를 시작한 다음 ClickHouse를 재시작해요.
  4. 지표를 마이그레이션 전 기준선과 비교해 일관성을 검증해요.
  5. 백그라운드 작업을 재개하고 데이터 수집을 재시작해요.

여러 ZooKeeper 클러스터 통합하기 (Consolidating multiple ZooKeeper clusters)

여러 ZooKeeper 클러스터를 실행한다면 — 예를 들어 샤드 그룹당 하나 — 단일 ClickHouse Keeper 클러스터로 통합할 수 있어요. 공식 clickhouse-keeper-converter 도구는 일대일 변환(하나의 ZooKeeper 클러스터를 하나의 Keeper 스냅샷으로)만 지원하므로, 통합하려면 여러 스냅샷을 병합하도록 컨버터 소스 코드를 수정해야 해요:

  1. 각 ZooKeeper 클러스터에서 clickhouse-keeper-converter를 별도로 실행해 각 출력을 별도의 디렉터리에 써요.
  2. 스냅샷 파일을 순차적으로 역직렬화해요. 병합할 때 다른 원본 클러스터의 네임스페이스 간 노드 ID 충돌을 피하도록 numChildren 값을 다시 계산해요.
  3. 병합된 출력을 대상 ClickHouse Keeper 스냅샷 디렉터리에 써요.

암호화와 ACL 처리 (Handling encryption and ACLs)

ClickHouse Keeper는 ZooKeeper와 같은 ACL 스킴(world, auth, digest)을 지원해요. 변환 중 ACL을 처리하는 방법은 ZooKeeper 설정에 따라 달라져요:

  • 완전히 암호화 또는 완전히 비암호화: 직접 변환해요. 컨버터가 기존 ACL 정보를 보존해요.
  • 부분적으로 암호화: 변환 전에 슈퍼 관리자 계정을 부여하고 영향받는 경로에서 setAcl -R로 ACL을 지워요. 변환한 다음 필요하면 ClickHouse Keeper에서 암호화를 다시 활성화해요.

마이그레이션 검증 (Verifying the migration)

ClickHouse Keeper를 시작하고 ClickHouse를 재시작한 후, 핵심 지표를 마이그레이션 전 기준선과 비교해 마이그레이션이 성공했는지 확인해요. 여러 ZooKeeper 클러스터를 통합할 때 다음을 구분해요:

  • 공통 경로: 여러 원본 클러스터에 동일한 데이터로 존재하는 경로 — 병합된 출력에서 중복 제거되어야 해요.
  • 차별화된 경로: 특정 클러스터 아래에만 존재하는 경로(예: 각 샤드 그룹에 대해 /clickhouse/tables 아래) — 올바른 원본에서 보존되어야 해요.

비교를 위해 대규모 ZooKeeper 트리를 직접 순회하지 마세요. 대신 변환 중 모든 변환된 경로를 파일에 출력하세요.

마이그레이션 후 튜닝 (Post-migration tuning)

마이그레이션 후 더 큰 클러스터나 더 높은 처리량을 위해 다음 설정을 튜닝하는 것을 고려해보세요:

설정 기본 값 권장 참고
max_requests_batch_size 100 10000 part 수가 많거나 샤드가 많은 클러스터에서 증가
force_sync true false 비동기 로그 쓰기가 처리량을 개선
compress_logs false true Raft 로그 파일을 압축해 디스크 I/O 감소
compress_snapshots_with_zstd_format true 이미 기본 활성화; 스냅샷을 zstd로 압축
snapshot_zstd_compression_level 3 낮은 값은 더 큰 스냅샷을 희생해 스냅샷 압축 CPU를 줄임

이 설정들은 Keeper 구성의 coordination_settings 아래에 구성돼요.

쿼럼 손실 후 복구 (Recovering after losing quorum)

ClickHouse Keeper는 Raft를 사용하므로 클러스터 크기에 따라 일정량의 노드 크래시를 견딜 수 있어요. 예를 들어 3-노드 클러스터에서는 1개 노드만 크래시하면 계속 올바르게 작동해요. 클러스터 구성은 동적으로 구성될 수 있지만 몇 가지 제한이 있어요. 재구성도 Raft에 의존하므로 클러스터에서 노드를 추가/제거하려면 쿼럼이 필요해요. 클러스터에서 너무 많은 노드를 다시 시작할 기회 없이 동시에 잃으면 Raft가 작동을 멈추고 일반적인 방식으로 클러스터를 재구성할 수 없게 해요. 그럼에도 ClickHouse Keeper에는 1개 노드만으로 클러스터를 강제로 재구성할 수 있는 복구 모드가 있어요. 이것은 노드를 다시 시작할 수 없거나 같은 엔드포인트에 새 인스턴스를 시작할 수 없을 때의 최후의 수단으로만 해야 해요. 계속하기 전에 주의할 중요한 사항:

  • 실패한 노드가 클러스터에 다시 연결할 수 없는지 확인해요.
  • 단계에 지정되기 전에는 새 노드를 시작하지 마세요.

위 사항이 사실인지 확인한 후 다음을 수행해야 해요:

  1. 새 리더가 될 단일 Keeper 노드를 선택해요. 그 노드의 데이터가 전체 클러스터에 사용된다는 점을 인지하세요. 가장 최신 상태의 노드를 사용할 것을 권장해요.
  2. 다른 무엇보다 먼저 선택한 노드의 log_storage_pathsnapshot_storage_path 폴더의 백업을 만들어요.
  3. 사용할 모든 노드에서 클러스터를 재구성해요.
  4. 선택한 노드에 rcvr 네 글자 명령을 보내 노드를 복구 모드로 전환하거나, 선택한 노드에서 Keeper 인스턴스를 중지하고 --force-recovery 인자로 다시 시작해요.
  5. 다음 노드를 시작하기 전에 mntrzk_server_state에 대해 follower를 반환하는지 확인하면서 새 노드에서 Keeper 인스턴스를 하나씩 시작해요.
  6. 복구 모드 동안 리더 노드는 새 노드와 쿼럼을 달성할 때까지 mntr 명령에 오류 메시지를 반환하고 클라이언트와 팔로워의 모든 요청을 거부해요.
  7. 쿼럼이 달성된 후 리더 노드는 정상 작동 모드로 돌아가 Raft로 모든 요청을 수락하며, zk_server_state에 대해 leader를 반환해야 하는 mntr로 확인해요.

Keeper와 디스크 사용 (Using disks with Keeper)

Keeper는 스냅샷, 로그 파일, 상태 파일 저장을 위해 외부 디스크의 부분집합을 지원해요. 지원되는 디스크 유형:

  • s3_plain
  • s3
  • local

구성 안에 포함된 디스크 정의 예제는 다음과 같아요.

<clickhouse>
    <storage_configuration>
        <disks>
            <log_local>
                <type>local</type>
                <path>/var/lib/clickhouse/coordination/logs/</path>
            </log_local>
            <log_s3_plain>
                <type>s3_plain</type>
                <endpoint>https://some_s3_endpoint/logs/</endpoint>
                <access_key_id>ACCESS_KEY</access_key_id>
                <secret_access_key>SECRET_KEY</secret_access_key>
            </log_s3_plain>
            <snapshot_local>
                <type>local</type>
                <path>/var/lib/clickhouse/coordination/snapshots/</path>
            </snapshot_local>
            <snapshot_s3_plain>
                <type>s3_plain</type>
                <endpoint>https://some_s3_endpoint/snapshots/</endpoint>
                <access_key_id>ACCESS_KEY</access_key_id>
                <secret_access_key>SECRET_KEY</secret_access_key>
            </snapshot_s3_plain>
            <state_s3_plain>
                <type>s3_plain</type>
                <endpoint>https://some_s3_endpoint/state/</endpoint>
                <access_key_id>ACCESS_KEY</access_key_id>
                <secret_access_key>SECRET_KEY</secret_access_key>
            </state_s3_plain>
        </disks>
    </storage_configuration>
</clickhouse>

로그에 디스크를 사용하려면 keeper_server.log_storage_disk 구성을 디스크 이름으로 설정해야 해요. 스냅샷에 디스크를 사용하려면 keeper_server.snapshot_storage_disk 구성을 디스크 이름으로 설정해야 해요. 추가로 keeper_server.latest_log_storage_disk를 최신 로그에, keeper_server.latest_snapshot_storage_disk를 최신 스냅샷에 사용할 수 있어요. 그 경우 Keeper는 새 로그나 스냅샷이 생성될 때 파일을 올바른 디스크로 자동 이동해요. 상태 파일에 디스크를 사용하려면 keeper_server.state_storage_disk 구성을 디스크 이름으로 설정해야 해요. 디스크 간 파일 이동은 안전하며, Keeper가 전송 중간에 중지되어도 데이터 손실 위험이 없어요. 파일이 새 디스크로 완전히 이동될 때까지는 이전 디스크에서 삭제되지 않아요. keeper_server.coordination_settings.force_synctrue(기본 true)로 설정된 Keeper는 모든 디스크 유형에 대해 일부 보장을 충족할 수 없어요. 현재 local 유형의 디스크만 영구 동기화를 지원해요. force_sync를 사용하면 latest_log_storage_disk를 사용하지 않을 때 log_storage_disklocal 디스크여야 해요. latest_log_storage_disk를 사용하면 항상 local 디스크여야 해요. force_sync가 비활성화되면 모든 유형의 디스크를 어떤 설정에서든 사용할 수 있어요. Keeper 인스턴스의 가능한 저장 설정은 다음과 같을 수 있어요:

<clickhouse>
    <keeper_server>
        <log_storage_disk>log_s3_plain</log_storage_disk>
        <latest_log_storage_disk>log_local</latest_log_storage_disk>

        <snapshot_storage_disk>snapshot_s3_plain</snapshot_storage_disk>
        <latest_snapshot_storage_disk>snapshot_local</latest_snapshot_storage_disk>
    </keeper_server>
</clickhouse>

이 인스턴스는 최신 로그를 제외한 모든 로그를 log_s3_plain 디스크에 저장하고, 최신 로그는 log_local 디스크에 저장해요. 스냅샷에도 같은 논리가 적용돼요. 최신 스냅샷을 제외한 모든 스냅샷은 snapshot_s3_plain에, 최신 스냅샷은 snapshot_local 디스크에 저장돼요.

디스크 설정 변경 (Changing disk setup)

새 디스크 설정을 적용하기 전에 모든 Keeper 로그와 스냅샷을 수동으로 백업해요.

계층형 디스크 설정(최신 파일용 별도 디스크)이 정의되면 Keeper는 시작 시 파일을 올바른 디스크로 자동 이동하려고 해요. 이전과 같은 보장이 적용돼요; 파일이 새 디스크로 완전히 이동될 때까지는 이전 디스크에서 삭제되지 않으므로 여러 번 재시작해도 안전해요. 파일을 완전히 새 디스크로 이동해야 한다면(또는 2-디스크 설정에서 단일 디스크 설정으로 이동), keeper_server.old_snapshot_storage_diskkeeper_server.old_log_storage_disk의 여러 정의를 사용할 수 있어요. 다음 구성은 이전 2-디스크 설정에서 완전히 새 단일-디스크 설정으로 이동하는 방법을 보여줘요:

<clickhouse>
    <keeper_server>
        <old_log_storage_disk>log_local</old_log_storage_disk>
        <old_log_storage_disk>log_s3_plain</old_log_storage_disk>
        <log_storage_disk>log_local2</log_storage_disk>

        <old_snapshot_storage_disk>snapshot_s3_plain</old_snapshot_storage_disk>
        <old_snapshot_storage_disk>snapshot_local</old_snapshot_storage_disk>
        <snapshot_storage_disk>snapshot_local2</snapshot_storage_disk>
    </keeper_server>
</clickhouse>

시작 시 모든 로그 파일이 log_locallog_s3_plain에서 log_local2 디스크로 이동돼요. 또한 모든 스냅샷 파일이 snapshot_localsnapshot_s3_plain에서 snapshot_local2 디스크로 이동돼요.

로그 캐시 구성 (Configuring logs cache)

디스크에서 읽는 데이터 양을 최소화하기 위해 Keeper는 로그 항목을 메모리에 캐시해요. 요청이 크면 로그 항목이 너무 많은 메모리를 차지하므로 캐시되는 로그 양이 제한돼요. 최신 로그 캐시의 한계는 다음으로 제어돼요:

  • latest_logs_cache_size_threshold — 캐시된 최신 로그가 보유하는 메모리
  • latest_logs_cache_entry_count_threshold — 캐시가 보유할 수 있는 항목 수

어느 한계든 먼저 도달하는 쪽이 캐시를 제한하고, 하나를 0으로 설정하면 나머지만 효력이 있어요. 크기 임계값은 캐시된 항목이 실제로 차지하는 메모리를 세지, 로그 항목 자체의 크기만 세는 것이 아니에요: 모든 캐시된 항목은 그것을 도달 가능하게 유지하는 객체도 함께 지니고, 그 할당은 할당자 크기 클래스로 반올림돼요. 작은 항목의 경우 그 오버헤드가 항목의 몇 배이므로 캐시가 보유하는 항목 수는 임계값을 항목 크기로 나눈 것보다 훨씬 적을 수 있어요. KeeperLatestLogsCacheSize는 임계값이 제한하는 것과 같은 양을 보고해요. 기본 값이 너무 크면 이 구성을 줄여 메모리 사용량을 줄일 수 있어요. 커밋에 필요한 다음 로그 항목은 디코딩된 read-ahead 리더가 제공하며, 크기는 log_readahead_commit_window_bytes로 정해져요(0은 커밋 read-ahead를 비활성화). 이 설정은 사용 중단된 commit_logs_cache_size_thresholdcommit_logs_cache_entry_count_threshold 설정을 대체하며, 후자는 구성 호환성만을 위해 유지돼요(전자는 여전히 설정되지 않으면 log_readahead_commit_window_bytes에 매핑되고, 후자는 효과가 없어요). 같은 read-ahead 메커니즘이 log_readahead_enabledtrue일 때 팔로워 복제 따라잡기 읽기도 제공해요; 피어 측 튜닝 노브는 내부 조정 설정에서 log_readahead_window_bytes, log_readahead_max_peer_readers, log_readahead_eviction_timeout_ms, log_readahead_pool_threads, log_readahead_serve_wait_timeout_ms, log_readahead_chunk_size를 참조하세요.

pfev 명령으로 각 캐시와 파일에서 읽은 로그 양을 확인할 수 있어요. Prometheus 엔드포인트의 지표로도 두 캐시의 현재 크기를 추적할 수 있어요.

Prometheus

Keeper는 Prometheus에서 스크레이핑할 수 있도록 지표 데이터를 노출할 수 있어요. 설정:

  • endpoint – Prometheus 서버가 지표를 스크레이핑하는 HTTP 엔드포인트. '/'로 시작.
  • portendpoint의 포트.
  • metricssystem.metrics 테이블의 지표를 노출할지 설정하는 플래그.
  • eventssystem.events 테이블의 지표를 노출할지 설정하는 플래그.
  • asynchronous_metricssystem.asynchronous_metrics 테이블의 현재 지표 값을 노출할지 설정하는 플래그.

예제

<clickhouse>
    <listen_host>0.0.0.0</listen_host>
    <http_port>8123</http_port>
    <tcp_port>9000</tcp_port>
    <prometheus>
        <endpoint>/metrics</endpoint>
        <port>9363</port>
        <metrics>true</metrics>
        <events>true</events>
        <asynchronous_metrics>true</asynchronous_metrics>
    </prometheus>
</clickhouse>

확인(127.0.0.1을 ClickHouse 서버의 IP 주소나 호스트 이름으로 교체):

curl 127.0.0.1:9363/metrics

ClickHouse Cloud Prometheus 통합도 참고하세요.

ClickHouse Keeper 사용자 가이드

이 가이드는 분산 연산을 테스트하는 방법의 예제와 함께 ClickHouse Keeper를 구성하기 위한 간단하고 최소한의 설정을 제공해요. 이 예제는 Linux에서 3개 노드를 사용해 수행돼요.

1. Keeper 설정으로 노드 구성하기

  1. 3개 호스트(chnode1, chnode2, chnode3)에 3개의 ClickHouse 인스턴스를 설치해요. (ClickHouse 설치에 대한 자세한 내용은 Quick Start를 참고.)
  2. 각 노드에서 네트워크 인터페이스를 통한 외부 통신을 허용하려면 다음 항목을 추가해요.
<listen_host>0.0.0.0</listen_host>
  1. 세 서버 모두에 다음 ClickHouse Keeper 구성을 추가하고 각 서버의 <server_id> 설정을 업데이트해요; chnode1이면 1, chnode2이면 2 등.
<keeper_server>
    <tcp_port>9181</tcp_port>
    <server_id>1</server_id>
    <log_storage_path>/var/lib/clickhouse/coordination/log</log_storage_path>
    <snapshot_storage_path>/var/lib/clickhouse/coordination/snapshots</snapshot_storage_path>

    <coordination_settings>
        <operation_timeout_ms>10000</operation_timeout_ms>
        <session_timeout_ms>30000</session_timeout_ms>
        <raft_logs_level>warning</raft_logs_level>
    </coordination_settings>

    <raft_configuration>
        <server>
            <id>1</id>
            <hostname>chnode1.domain.com</hostname>
            <port>9234</port>
        </server>
        <server>
            <id>2</id>
            <hostname>chnode2.domain.com</hostname>
            <port>9234</port>
        </server>
        <server>
            <id>3</id>
            <hostname>chnode3.domain.com</hostname>
            <port>9234</port>
        </server>
    </raft_configuration>
</keeper_server>

위에서 사용한 기본 설정:

매개변수 설명 예제
tcp_port keeper 클라이언트가 사용할 포트 9181, zookeeper의 2181과 동등한 기본
server_id raft 구성에 사용되는 각 ClickHouse Keeper 서버의 식별자 1
coordination_settings 타임아웃 같은 매개변수 섹션 타임아웃: 10000, 로그 수준: trace
server 참여하는 서버의 정의 각 서버 정의 목록
raft_configuration keeper 클러스터의 각 서버에 대한 설정 각각에 대한 서버와 설정
id keeper 서비스용 서버의 숫자 id 1
hostname keeper 클러스터의 각 서버 호스트 이름, IP 또는 FQDN chnode1.domain.com
port keeper 간 서버 연결을 수신할 포트 9234
  1. Zookeeper 컴포넌트를 활성화해요. ClickHouse Keeper 엔진을 사용해요:
<zookeeper>
        <node>
            <host>chnode1.domain.com</host>
            <port>9181</port>
        </node>
        <node>
            <host>chnode2.domain.com</host>
            <port>9181</port>
        </node>
        <node>
            <host>chnode3.domain.com</host>
            <port>9181</port>
        </node>
    </zookeeper>

위에서 사용한 기본 설정:

매개변수 설명 예제
node ClickHouse Keeper 연결용 노드 목록 각 서버의 설정 항목
host 각 ClickHouse keeper 노드의 호스트 이름, IP 또는 FQDN chnode1.domain.com
port ClickHouse Keeper 클라이언트 포트 9181
  1. ClickHouse를 재시작하고 각 Keeper 인스턴스가 실행 중인지 확인해요. 각 서버에서 다음 명령을 실행해요. ruok 명령은 Keeper가 실행되고 정상이면 imok를 반환해요:
# echo ruok | nc localhost 9181; echo
imok
  1. system 데이터베이스에는 ClickHouse Keeper 인스턴스의 세부사항을 담는 zookeeper라는 이름의 테이블이 있어요. 테이블을 봐볼게요:
SELECT *
FROM system.zookeeper
WHERE path IN ('/', '/clickhouse')

테이블은 이렇게 생겼어요:

┌─name───────┬─value─┬─czxid─┬─mzxid─┬───────────────ctime─┬───────────────mtime─┬─version─┬─cversion─┬─aversion─┬─ephemeralOwner─┬─dataLength─┬─numChildren─┬─pzxid─┬─path────────┐
│ clickhouse │       │   124 │   124 │ 2022-03-07 00:49:34 │ 2022-03-07 00:49:34 │       0 │        2 │        0 │              0 │          0 │           2 │  5693 │ /           │
│ task_queue │       │   125 │   125 │ 2022-03-07 00:49:34 │ 2022-03-07 00:49:34 │       0 │        1 │        0 │              0 │          0 │           1 │   126 │ /clickhouse │
│ tables     │       │  5693 │  5693 │ 2022-03-07 00:49:34 │ 2022-03-07 00:49:34 │       0 │        3 │        0 │              0 │          0 │           3 │  6461 │ /clickhouse │
└────────────┴───────┴───────┴───────┴─────────────────────┴─────────────────────┴─────────┴──────────┴──────────┴────────────────┴────────────┴─────────────┴───────┴─────────────┘

2. ClickHouse에서 클러스터 구성하기

  1. 2개 노드에 2개의 샤드와 하나의 레플리카만 있는 간단한 클러스터를 구성해볼게요. 세 번째 노드는 ClickHouse Keeper의 요구 사항에 대한 쿼럼을 달성하는 데 사용돼요. chnode1chnode2의 구성을 업데이트해요. 다음 클러스터는 복제 없는 총 2개의 샤드에 대해 각 노드에 1개의 샤드를 정의해요. 이 예제에서는 일부 데이터가 한 노드에, 일부는 다른 노드에 있을 거예요:
<remote_servers>
        <cluster_2S_1R>
            <shard>
                <replica>
                    <host>chnode1.domain.com</host>
                    <port>9000</port>
                    <user>default</user>
                    <password>ClickHouse123!</password>
                </replica>
            </shard>
            <shard>
                <replica>
                    <host>chnode2.domain.com</host>
                    <port>9000</port>
                    <user>default</user>
                    <password>ClickHouse123!</password>
                </replica>
            </shard>
        </cluster_2S_1R>
    </remote_servers>
매개변수 설명 예제
shard 클러스터 정의의 레플리카 목록 각 샤드의 레플리카 목록
replica 각 레플리카의 설정 목록 각 레플리카의 설정 항목
host 레플리카 샤드를 호스팅할 서버의 호스트 이름, IP 또는 FQDN chnode1.domain.com
port 네이티브 tcp 프로토콜로 통신하는 데 사용되는 포트 9000
user 클러스터 인스턴스에 인증하는 데 사용될 사용자 이름 default
password 클러스터 인스턴스에 연결을 허용하도록 정의된 사용자의 비밀번호 ClickHouse123!
  1. ClickHouse를 재시작하고 클러스터가 생성되었는지 확인해요:
SHOW clusters;

클러스터가 보여야 해요:

┌─cluster───────┐
│ cluster_2S_1R │
└───────────────┘

3. 분산 테이블 만들고 테스트하기

  1. chnode1에서 ClickHouse 클라이언트를 사용해 새 클러스터에 새 데이터베이스를 만들어요. ON CLUSTER 절이 두 노드 모두에 데이터베이스를 자동으로 만들어요.
CREATE DATABASE db1 ON CLUSTER 'cluster_2S_1R';
  1. db1 데이터베이스에 새 테이블을 만들어요. 다시 말하지만 ON CLUSTER가 두 노드 모두에 테이블을 만들어요.
CREATE TABLE db1.table1 on cluster 'cluster_2S_1R'
(
    `id` UInt64,
    `column1` String
)
ENGINE = MergeTree
ORDER BY column1
  1. chnode1 노드에서 몇 줄을 추가해요:
INSERT INTO db1.table1
    (id, column1)
VALUES
    (1, 'abc'),
    (2, 'def')
  1. chnode2 노드에서 몇 줄을 추가해요:
INSERT INTO db1.table1
    (id, column1)
VALUES
    (3, 'ghi'),
    (4, 'jkl')
  1. 각 노드에서 SELECT문을 실행하면 해당 노드의 데이터만 표시되는 것에 주목해요. 예를 들어 chnode1에서:
SELECT *
FROM db1.table1
Query id: 7ef1edbc-df25-462b-a9d4-3fe6f9cb0b6d

┌─id─┬─column1─┐
│  1 │ abc     │
│  2 │ def     │
└────┴─────────┘

2 rows in set. Elapsed: 0.006 sec.

chnode2에서:

SELECT *
FROM db1.table1
Query id: c43763cc-c69c-4bcc-afbe-50e764adfcbf

┌─id─┬─column1─┐
│  3 │ ghi     │
│  4 │ jkl     │
└────┴─────────┘
  1. 두 샤드의 데이터를 나타내는 Distributed 테이블을 만들 수 있어요. Distributed 테이블 엔진을 가진 테이블은 그 자체로 데이터를 저장하지 않지만, 여러 서버에서 분산 쿼리 처리를 허용해요. 읽기는 모든 샤드를 치고, 쓰기는 샤드들에 분산될 수 있어요. chnode1에서 다음 쿼리를 실행해요:
CREATE TABLE db1.dist_table (
    id UInt64,
    column1 String
)
ENGINE = Distributed(cluster_2S_1R,db1,table1)
  1. dist_table을 조회하면 두 샤드의 네 행 데이터가 모두 반환되는 것을 확인해요:
SELECT *
FROM db1.dist_table
Query id: 495bffa0-f849-4a0c-aeea-d7115a54747a

┌─id─┬─column1─┐
│  1 │ abc     │
│  2 │ def     │
└────┴─────────┘
┌─id─┬─column1─┐
│  3 │ ghi     │
│  4 │ jkl     │
└────┴─────────┘

4 rows in set. Elapsed: 0.018 sec.

요약 (Summary)

이 가이드는 ClickHouse Keeper를 사용해 클러스터를 설정하는 방법을 보여줬어요. ClickHouse Keeper로 샤드들에 걸쳐 복제될 수 있는 클러스터와 분산 테이블을 구성할 수 있어요.

ClickHouse Keeper 고유 경로 구성하기

이 페이지는 ClickHouse Cloud에는 적용되지 않아요. 여기 문서화된 절차는 ClickHouse Cloud 서비스에서 자동화되어 있어요.

설명 (Description)

이 문서는 ClickHouse Keeper 또는 ZooKeeper에서 고유 항목을 만들기 위해 내장 {uuid} 매크로 설정을 사용하는 방법을 설명해요. 고유 경로는 테이블을 자주 만들고 삭제할 때 유용한데, 경로가 생성될 때마다 그 경로에 새 uuid가 사용되므로 Keeper 가비지 컬렉션이 경로 항목을 제거하기 위해 몇 분 기다리는 것을 피할 수 있어요; 경로는 결코 재사용되지 않아요.

예제 환경 (Example environment)

세 개의 노드로 구성된 클러스터이며, 세 노드 모두에 ClickHouse Keeper가, 두 노드에 ClickHouse가 있게 구성될 거예요. 이는 ClickHouse Keeper에 세 개의 노드(타이브레이커 노드 포함)를 제공하고, 두 레플리카로 구성된 단일 ClickHouse 샤드를 제공해요.

노드 설명
chnode1.marsnet.local 데이터 노드 - 클러스터 cluster_1S_2R
chnode2.marsnet.local 데이터 노드 - 클러스터 cluster_1S_2R
chnode3.marsnet.local ClickHouse Keeper 타이브레이커 노드

클러스터의 예제 구성:

    <remote_servers>
        <cluster_1S_2R>
            <shard>
                <replica>
                    <host>chnode1.marsnet.local</host>
                    <port>9440</port>
                    <user>default</user>
                    <password>ClickHouse123!</password>
                    <secure>1</secure>
                </replica>
                <replica>
                    <host>chnode2.marsnet.local</host>
                    <port>9440</port>
                    <user>default</user>
                    <password>ClickHouse123!</password>
                    <secure>1</secure>
                </replica>
            </shard>
        </cluster_1S_2R>
    </remote_servers>

{uuid}를 사용하도록 테이블을 설정하는 절차

  1. 각 서버에 Macros를 구성해요 (서버 1의 예):
    <macros>
        <shard>1</shard>
        <replica>replica_1</replica>
    </macros>

shardreplica에 대한 매크로를 정의하지만 {uuid}는 여기서 정의되지 않는다는 점에 주의하세요 — 그것은 내장되어 있고 정의할 필요가 없어요.

  1. 데이터베이스 만들기
CREATE DATABASE db_uuid
      ON CLUSTER 'cluster_1S_2R'
      ENGINE Atomic;
CREATE DATABASE db_uuid ON CLUSTER cluster_1S_2R
ENGINE = Atomic

Query id: 07fb7e65-beb4-4c30-b3ef-bd303e5c42b5

┌─host──────────────────┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ chnode2.marsnet.local │ 9440 │      0 │       │                   1 │                0 │
│ chnode1.marsnet.local │ 9440 │      0 │       │                   0 │                0 │
└───────────────────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘
  1. 매크로와 {uuid}를 사용해 클러스터에 테이블을 만들어요
CREATE TABLE db_uuid.uuid_table1 ON CLUSTER 'cluster_1S_2R'
   (
     id UInt64,
     column1 String
   )
   ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/db_uuid/{uuid}', '{replica}' )
   ORDER BY (id);
CREATE TABLE db_uuid.uuid_table1 ON CLUSTER cluster_1S_2R
(
    `id` UInt64,
    `column1` String
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/db_uuid/{uuid}', '{replica}')
ORDER BY id

Query id: 8f542664-4548-4a02-bd2a-6f2c973d0dc4

┌─host──────────────────┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ chnode1.marsnet.local │ 9440 │      0 │       │                   1 │                0 │
│ chnode2.marsnet.local │ 9440 │      0 │       │                   0 │                0 │
└───────────────────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘
  1. 분산 테이블 만들기
CREATE TABLE db_uuid.dist_uuid_table1 ON CLUSTER 'cluster_1S_2R'
   (
     id UInt64,
     column1 String
   )
   ENGINE = Distributed('cluster_1S_2R', 'db_uuid', 'uuid_table1' );
CREATE TABLE db_uuid.dist_uuid_table1 ON CLUSTER cluster_1S_2R
(
    `id` UInt64,
    `column1` String
)
ENGINE = Distributed('cluster_1S_2R', 'db_uuid', 'uuid_table1')

Query id: 3bc7f339-ab74-4c7d-a752-1ffe54219c0e

┌─host──────────────────┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ chnode2.marsnet.local │ 9440 │      0 │       │                   1 │                0 │
│ chnode1.marsnet.local │ 9440 │      0 │       │                   0 │                0 │
└───────────────────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘

테스트 (Testing)

  1. 첫 번째 노드(예: chnode1)에 데이터를 삽입해요
INSERT INTO db_uuid.uuid_table1
   ( id, column1)
   VALUES
   ( 1, 'abc');
INSERT INTO db_uuid.uuid_table1 (id, column1) FORMAT Values

Query id: 0f178db7-50a6-48e2-9a1b-52ed14e6e0f9

Ok.

1 row in set. Elapsed: 0.033 sec.
  1. 두 번째 노드(예: chnode2)에 데이터를 삽입해요
INSERT INTO db_uuid.uuid_table1
   ( id, column1)
   VALUES
   ( 2, 'def');
INSERT INTO db_uuid.uuid_table1 (id, column1) FORMAT Values

Query id: edc6f999-3e7d-40a0-8a29-3137e97e3607

Ok.

1 row in set. Elapsed: 0.529 sec.
  1. 분산 테이블을 사용해 레코드를 확인해요
SELECT * FROM db_uuid.dist_uuid_table1;
SELECT *
FROM db_uuid.dist_uuid_table1

Query id: 6cbab449-9e7f-40fe-b8c2-62d46ba9f5c8

┌─id─┬─column1─┐
│  1 │ abc     │
└────┴─────────┘
┌─id─┬─column1─┐
│  2 │ def     │
└────┴─────────┘

2 rows in set. Elapsed: 0.007 sec.

대안 (Alternatives)

기본 복제 경로는 매크로와 {uuid}를 사용해 미리 정의할 수 있어요

  1. 각 노드의 테이블에 기본값을 설정해요
<default_replica_path>/clickhouse/tables/{shard}/db_uuid/{uuid}</default_replica_path>
<default_replica_name>{replica}</default_replica_name>

노드가 특정 데이터베이스에 사용된다면 각 노드에 {database} 매크로를 정의할 수도 있어요.

  1. 명시적 매개변수 없이 테이블 만들기:
CREATE TABLE db_uuid.uuid_table1 ON CLUSTER 'cluster_1S_2R'
   (
     id UInt64,
     column1 String
   )
   ENGINE = ReplicatedMergeTree
   ORDER BY (id);
CREATE TABLE db_uuid.uuid_table1 ON CLUSTER cluster_1S_2R
(
    `id` UInt64,
    `column1` String
)
ENGINE = ReplicatedMergeTree
ORDER BY id

Query id: ab68cda9-ae41-4d6d-8d3b-20d8255774ee

┌─host──────────────────┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ chnode2.marsnet.local │ 9440 │      0 │       │                   1 │                0 │
│ chnode1.marsnet.local │ 9440 │      0 │       │                   0 │                0 │
└───────────────────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘

2 rows in set. Elapsed: 1.175 sec.
  1. 기본 구성에서 사용한 설정을 사용했는지 확인해요
SHOW CREATE TABLE db_uuid.uuid_table1;
SHOW CREATE TABLE db_uuid.uuid_table1

CREATE TABLE db_uuid.uuid_table1
(
    `id` UInt64,
    `column1` String
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/db_uuid/{uuid}', '{replica}')
ORDER BY id

1 row in set. Elapsed: 0.003 sec.

문제 해결 (Troubleshooting)

테이블 정보와 UUID를 얻는 예제 명령:

SELECT * FROM system.tables
WHERE database = 'db_uuid' AND name = 'uuid_table1';

위 테이블의 UUID로 zookeeper에서 테이블에 대한 정보를 얻는 예제 명령:

SELECT * FROM system.zookeeper
WHERE path = '/clickhouse/tables/1/db_uuid/9e8a3cc2-0dec-4438-81a7-c3e63ce2a1cf/replicas';

데이터베이스는 Atomic이어야 해요. 이전 버전에서 업그레이드한다면 default 데이터베이스는 Ordinary 유형일 가능성이 높아요.

확인하려면: 예를 들면

SELECT name, engine FROM system.databases WHERE name = 'db_uuid';
SELECT
    name,
    engine
FROM system.databases
WHERE name = 'db_uuid'

Query id: b047d459-a1d2-4016-bcf9-3e97e30e49c2

┌─name────┬─engine─┐
│ db_uuid │ Atomic │
└─────────┴────────┘

1 row in set. Elapsed: 0.004 sec.

ClickHouse Keeper 동적 재구성

이 페이지는 ClickHouse Cloud에는 적용되지 않아요. 여기 문서화된 절차는 ClickHouse Cloud 서비스에서 자동화되어 있어요.

설명 (Description)

ClickHouse Keeper는 keeper_server.enable_reconfiguration이 켜져 있으면 동적 클러스터 재구성을 위한 ZooKeeper reconfig 명령을 부분적으로 지원해요.

이 설정이 꺼져 있으면 레플리카의 raft_configuration 섹션을 수동으로 수정해 클러스터를 재구성할 수 있어요. 변경을 적용하는 것은 리더뿐이므로 모든 레플리카에서 파일을 편집해야 해요. 또는 ZooKeeper 호환 클라이언트를 통해 reconfig 쿼리를 보낼 수 있어요.

가상 노드 /keeper/config는 다음 형식의 마지막 커밋된 클러스터 구성을 포함해요:

server.id = server_host:server_port[;server_type][;server_priority]
server.id2 = ...
...
  • 각 서버 항목은 개행으로 구분돼요.
  • server_typeparticipant 또는 learner예요(learner는 리더 선거에 참여하지 않아요).
  • server_priority리더 선거에서 어떤 노드를 우선시해야 하는지 알려주는 음이 아닌 정수예요. 우선순위 0은 서버가 절대 리더가 되지 않음을 의미해요.

예:

:) get /keeper/config
server.1=zoo1:9234;participant;1
server.2=zoo2:9234;participant;1
server.3=zoo3:9234;participant;1

reconfig 명령으로 새 서버를 추가하고, 기존 서버를 제거하고, 기존 서버의 우선순위를 변경할 수 있어요. 예제는 다음과 같아요(clickhouse-keeper-client 사용):

# Add two new servers
reconfig add "server.5=localhost:123,server.6=localhost:234;learner"
# Remove two other servers
reconfig remove "3,4"
# Change existing server priority to 8
reconfig add "server.5=localhost:5123;participant;8"

kazoo의 예제:

# Add two new servers, remove two other servers
reconfig(joining="server.5=localhost:123,server.6=localhost:234;learner", leaving="3,4")

# Change existing server priority to 8
reconfig(joining="server.5=localhost:5123;participant;8", leaving=None)

joining의 서버는 위에서 설명한 서버 형식이어야 해요. 서버 항목은 쉼표로 구분돼요. 새 서버를 추가할 때 server_priority(기본 값 1)와 server_type(기본 값 participant)은 생략할 수 있어요. 기존 서버 우선순위를 변경하려면 대상 우선순위로 joining에 추가해요. 서버 호스트, 포트, 유형은 기존 서버 구성과 같아야 해요. 서버는 joiningleaving에 나타나는 순서대로 추가·제거돼요. joining의 모든 업데이트가 leaving의 업데이트보다 먼저 처리돼요. Keeper 재구성 구현에는 몇 가지 주의사항이 있어요:

  • 증분 재구성만 지원돼요. 비어 있지 않은 new_members가 있는 요청은 거부돼요. ClickHouse Keeper 구현은 멤버십을 동적으로 변경하기 위해 NuRaft API에 의존해요. NuRaft는 한 번에 하나씩 단일 서버를 추가하거나 단일 서버를 제거하는 방법이 있어요. 즉 구성에 대한 각 변경(joining의 각 부분, leaving의 각 부분)은 별도로 결정되어야 해요. 따라서 사용자에게 오해를 줄 수 있으므로 대량 재구성은 사용할 수 없어요. 서버 유형(participant/learner) 변경도 NuRaft가 지원하지 않으므로 불가능하며, 유일한 방법은 서버를 제거하고 추가하는 것뿐인데 다시 오해를 줄 수 있어요.
  • 반환된 znodestat 값을 사용할 수 없어요.
  • from_version 필드는 사용되지 않아요. from_version이 설정된 모든 요청은 거부돼요. /keeper/config가 가상 노드이기 때문이며, 이는 영구 저장소에 저장되지 않고 요청마다 지정된 노드 구성으로 즉석에서 생성된다는 뜻이에요. NuRaft가 이미 이 구성을 저장하므로 데이터를 중복하지 않기 위해 이 결정을 내렸어요.
  • ZooKeeper와 달리 sync 명령을 제출해 클러스터 재구성을 기다릴 수 있는 방법이 없어요. 새 구성은 결국 적용되지만 시간 보장은 없어요.
  • reconfig 명령은 여러 이유로 실패할 수 있어요. 클러스터 상태를 확인해 업데이트가 적용되었는지 볼 수 있어요.

단일 노드 keeper를 클러스터로 변환하기 (Converting a single-node keeper into a cluster)

때로는 실험용 keeper 노드를 클러스터로 확장해야 할 필요가 있어요. 3-노드 클러스터를 위한 단계별 방법:

  • 중요: 새 노드는 현재 쿼럼보다 작은 배치로 추가해야 해요. 그렇지 않으면 그들 사이에서 리더를 선출할 거예요. 이 예제에서는 하나씩 추가해요.
  • 기존 keeper 노드에는 keeper_server.enable_reconfiguration 구성 매개변수가 켜져 있어야 해요.
  • keeper 클러스터의 전체 새 구성으로 두 번째 노드를 시작해요.
  • 시작된 후 reconfig로 노드 1에 추가해요.
  • 이제 세 번째 노드를 시작하고 reconfig로 추가해요.
  • 새 keeper 노드를 추가해 clickhouse-server 구성을 업데이트하고 변경 사항을 적용하기 위해 재시작해요.
  • 노드 1의 raft 구성을 업데이트하고, 선택적으로 재시작해요.

이 과정에 익숙해지려면 샌드박스 저장소를 참고하세요.

지원되지 않는 기능 (Unsupported features)

ClickHouse Keeper는 ZooKeeper와 완전히 호환되는 것을 목표로 하지만, 아직 구현되지 않은 기능이 일부 있어요(개발은 계속 진행 중):

더 알아보기 (Learn more)