Skip to main content
손상되었거나 비정상적인 ClickHouse Keeper 스냅샷은 메타데이터 불일치, 테이블의 읽기 전용 상태, 리소스 고갈, 백업 실패와 같은 심각한 시스템 불안정을 초래할 수 있습니다. 이 문서에서는 다음 내용을 다룹니다.

Keeper 스냅샷 개요

스냅샷이란 무엇입니까?

스냅샷은 특정 시점의 Keeper 내부 데이터(예: 클러스터 관련 메타데이터, 테이블 조정 경로, 구성)를 직렬화한 상태입니다. 스냅샷은 클러스터 내 Keeper 노드의 재동기화, 장애 발생 시 메타데이터 복구, 그리고 정상 상태로 확인된 Keeper 상태에 의존하는 시작 또는 재시작 과정에 매우 중요합니다.

스냅샷은 어디에서 찾을 수 있습니까?

스냅샷은 Keeper 노드의 로컬 파일 시스템에 파일 형태로 저장됩니다. 기본적으로 /var/lib/clickhouse/coordination/snapshots/에 저장되며, keeper_server.xml 파일의 snapshot_storage_path에 지정한 사용자 지정 경로에 저장될 수도 있습니다. 스냅샷 이름은 증가하는 순서로 지정되며(예: snapshot.23), 최신 스냅샷일수록 숫자가 더 큽니다. 멀티 노드 클러스터에서는 각 Keeper 노드가 자체 스냅샷 디렉터리를 가집니다.
복구를 위해서는 노드 간 스냅샷의 일관성이 매우 중요합니다.

손상된 Keeper 스냅샷의 주요 증상과 징후

아래 표는 손상된 Keeper 스냅샷에서 흔히 나타나는 몇 가지 주요 증상과 징후를 보여줍니다: 로그 징후: 스냅샷 손상을 진단하기 전에 Keeper 로그에서 특정 오류 패턴을 확인하십시오:

손상된 Keeper 스냅샷에서 복구하기

파일을 건드리기 전에 항상 다음 사항을 수행하십시오:
  1. 추가 손상을 방지하기 위해 모든 Keeper 노드를 중지합니다
  2. 전체 coordination 디렉터리를 안전한 위치에 복사해 모두 백업합니다
  3. 클러스터 정족수(quorum)를 확인하여 최소 1개의 노드에 정상 데이터가 있는지 검증합니다

  1. 기존 Backup에서 복원

다음에 해당하면 이 절차를 따르십시오:
  • Keeper 메타데이터(metadata) 또는 스냅샷 손상으로 인해 현재 데이터를 복구할 수 없습니다.
  • 정상 상태로 확인된 Keeper 상태가 포함된 Backup이 있습니다.
기존 Backup을 복원하려면 아래 단계를 따르십시오:
  1. 메타데이터 일관성을 기준으로 가장 최신 Backup을 찾아 검증합니다.
  2. ClickHouse 및 Keeper 서비스를 종료합니다.
  3. Backup 디렉터리의 스냅샷 및 로그로 손상된 항목을 대체합니다.
  4. Keeper 클러스터를 다시 시작하고 메타데이터 동기화가 정상인지 검증합니다.
정기적으로 Backup하십시오Backup이 오래된 경우 최근 메타데이터 변경 사항이 손실될 수 있습니다. 따라서 정기적으로 Backup할 것을 권장합니다.

  1. 이전 스냅샷으로 롤백

다음과 같은 경우 이 절차를 따르십시오.
  • 최근 스냅샷이 손상되었지만, 이전 스냅샷은 여전히 사용할 수 있습니다.
  • 일관된 복구에 필요한 증분 로그가 온전하게 유지되어 있습니다.
이전 스냅샷으로 롤백하려면 아래 단계를 따르십시오.
  1. Keeper 디렉터리에서 유효한 이전 스냅샷(예: snapshot.19)을 찾아 선택합니다.
  2. 더 최신 스냅샷과 로그를 제거합니다.
  3. Keeper를 다시 시작하여 로그를 재생하고 메타데이터 상태를 복구합니다.
메타데이터 비동기화 위험스냅샷과 로그가 없거나 불완전하면 메타데이터가 비동기화될 위험이 있습니다.

  1. SYSTEM RESTORE REPLICA를 사용해 메타데이터 복원

다음과 같은 경우 이 절차를 따르십시오.
  • Keeper 메타데이터가 유실되었거나 손상되었지만 테이블 데이터는 여전히 디스크에 남아 있는 경우
  • ZooKeeper/Keeper 메타데이터가 없어 테이블이 읽기 전용 모드로 전환된 경우
  • 로컬에서 사용 가능한 데이터 파트를 기반으로 Keeper 메타데이터를 재생성해야 하는 경우
메타데이터를 복원하려면 아래 단계를 따르십시오.
  1. 구성의 <path>로 설정된 clickHouse-server 데이터 경로에 테이블 데이터가 로컬에 존재하는지 확인하십시오. (기본값: /var/lib/clickhouse/data/)
  2. 영향을 받는 각 테이블에 대해 다음을 실행하십시오:
  1. 데이터베이스 수준 복구를 수행하는 경우(Replicated 데이터베이스 엔진 사용 시):
  1. 동기화가 완료될 때까지 기다리십시오:
  1. system.replicas에서 is_readonly = 0인지 확인하고 system.detached_parts를 모니터링하여 복구를 검증합니다
작동 방식SYSTEM RESTORE REPLICA는 기존의 모든 파트를 detach한 뒤 Keeper에 메타데이터를 재생성한 다음(새 빈 테이블인 것처럼), 모든 파트를 다시 ATTACH합니다. 이렇게 하면 네트워크를 통해 데이터를 다시 다운로드하지 않아도 됩니다.
필수 조건이 방법은 로컬 데이터 파트가 온전한 경우에만 작동합니다. 데이터도 손상된 경우에는 대신 전략 #5(클러스터 재구축)를 사용하십시오.

  1. Keeper에서 레플리카 메타데이터 삭제 및 재생성

다음과 같은 경우 이 절차를 따르십시오:
  • 오류가 클러스터의 단일 레플리카에서 발생했고, Keeper의 메타데이터가 손상되었거나 일관되지 않은 경우
  • “Part XXXXX intersects previous part YYYYY”와 같은 오류가 발생하는 경우
  • 로컬 데이터는 유지하면서 레플리카의 Keeper 메타데이터를 완전히 재설정해야 하는 경우
메타데이터를 삭제하고 다시 생성하려면 아래 단계를 따르십시오:
  1. 영향을 받은 레플리카에서 테이블을 detach합니다:
  1. Keeper에서 레플리카의 메타데이터를 제거합니다(임의의 레플리카에서 실행):
올바른 ZooKeeper 경로를 찾으려면 다음과 같이 하십시오:
  1. 테이블을 다시 ATTACH합니다(읽기 전용 모드로 전환됩니다):
  1. 레플리카 메타데이터를 복원합니다:
  1. 다른 레플리카와 동기화:
  1. 복구 후 모든 레플리카에서 system.detached_parts를 확인합니다
영향을 받은 모든 레플리카에서 실행하십시오손상이 여러 레플리카에 영향을 미친 경우, 각 레플리카에서 이 단계를 순차적으로 반복하십시오.
전체 데이터베이스에 적용하는 경우복제된 데이터베이스를 사용하는 경우에는 대신 SYSTEM DROP REPLICA ... FROM DATABASE db_name을 사용할 수 있습니다.
대안: force_restore_data 플래그 사용 서버 시작 시 모든 복제된 테이블을 자동으로 복구하려면 다음을 수행하십시오.
  1. ClickHouse 서버를 중지합니다
  2. 복구 플래그를 생성합니다:
  1. ClickHouse 서버를 시작합니다
  2. 서버가 자동으로 플래그를 삭제하고 모든 복제된 테이블을 복원합니다
  3. 로그를 확인하여 복구 진행 상황을 모니터링합니다
이 방법은 여러 테이블을 동시에 복원해야 할 때 유용합니다.

  1. Keeper 클러스터 재구축

다음과 같은 경우 이 절차를 따르십시오.
  • 복구에 사용할 수 있는 유효한 스냅샷, 로그 또는 Backup이 없습니다.
  • 전체 Keeper 클러스터와 해당 메타데이터를 다시 생성해야 합니다.
아래 단계에 따라 Keeper 클러스터를 재구축하십시오.
  1. ClickHouse 클러스터와 Keeper 클러스터를 완전히 중지합니다.
  2. 스냅샷 및 로그 디렉터리를 정리하여 각 Keeper 노드를 초기화합니다.
  3. Keeper 노드 하나를 리더로 초기화한 다음, 다른 노드를 순차적으로 추가합니다.
  4. 외부 기록이 있는 경우 메타데이터를 다시 가져옵니다.
시간이 많이 소요되는 절차이 절차는 시간이 많이 소요되며 장시간 서비스 중단이 발생할 위험이 있습니다. 전체 데이터를 다시 구성해야 합니다.

  1. 시작 시 스냅샷에서 고아 노드 제거하기

고아(orphaned) 노드란 스냅샷에 부모 노드가 없는 노드를 말합니다. Keeper는 이러한 스냅샷의 로드를 거부하고 Failure to load from latest snapshot with index라는 로그를 남깁니다. 이 절차는 다음 조건에 모두 해당할 때 따르십시오.
  • 스냅샷이 정확히 이러한 방식으로 구조적 손상을 입었고,
  • 유효한 Backup이나 이전 스냅샷을 사용할 수 없는 경우(전략 1과 2를 적용할 수 있다면 항상 해당 전략을 우선 사용하는 것이 좋습니다).
<keeper_server> 아래에 다음 두 설정을 모두 지정한 후 노드를 재시작하십시오.
인메모리 노드 저장소 필요이 정리 작업은 인메모리 노드 저장소에서만 구현되어 있습니다. <coordination_settings> 아래에서 use_lsmt_storage가 활성화되어 있으면 remove_orphaned_nodes_on_startup이 설정된 상태에서 Keeper가 시작을 거부합니다. 복구를 위해 재시작할 때는 use_lsmt_storage를 비활성화하고, 정상적으로 시작된 후 다시 활성화하십시오.
그러면 Keeper는 고아(orphaned) 하위 트리를 삭제하고 시작합니다. 삭제 내역은 로그에 WARNING 레벨로 두 줄에 걸쳐 기록됩니다. 한 줄에는 삭제된 노드의 총 개수와 삭제된 고아 하위 트리의 루트가, 다른 한 줄에는 스냅샷에서 누락되어 손상된 영역의 경계가 되는 경로가 기록됩니다. 해당 루트 아래의 하위 노드도 함께 삭제되지만 개별적으로 나열되지는 않습니다. Keeper는 로컬 로그 끝부분을 확인한 후, Raft를 시작하기 전에 복구된 트리를 동일한 로그 인덱스의 새 스냅샷 파일에 기록합니다. 완료된 복구 결과는 해당 인덱스의 이전 사본보다 우선하므로, 복구가 중단되더라도 손상된 스냅샷을 선택하지 않고 재개할 수 있습니다. 새 팔로워는 복구된 스냅샷을 전달받으며, 이후 재시작할 때는 고아 노드 제거를 더 이상 활성화할 필요가 없습니다. 원본 스냅샷과 동일 인덱스의 모든 사본은 파일 이름에 orphaned_ 접두사가 붙은 채로 보존되며, 이 파일들은 스냅샷 로드와 자동 보존 관리 대상에서 제외됩니다. 진단용으로 보관해 두었다가 더 이상 필요하지 않으면 수동으로 삭제하십시오. 복구를 위한 재시작에는 복구된 스냅샷과 원본 Backup을 저장할 추가 디스크 공간이 필요합니다. 복구된 스냅샷 기록, 로컬 디렉터리 변경 사항 동기화 또는 원본 보존 중 하나라도 실패하면 Keeper는 시작을 거부합니다. 중단된 보관 작업은 다음 시작 시 마저 완료됩니다.
로컬 상태만 복구함이 작업은 이 노드가 가진 데이터 사본에서만 노드를 삭제합니다. 해당 노드를 아직 보유한 피어는 이 노드와 상태가 어긋나게 되며, digest_enabled가 false로 설정되어 있으면 이후 이러한 불일치를 감지할 방법이 없습니다. 이 작업은 단일 노드 복구 용도로만 사용하거나, 클러스터의 모든 노드에 적용하십시오. 노드가 가동되면 두 설정을 모두 기본값으로 되돌리십시오.
스냅샷 이후의 로컬 로그 엔트리가 손상된 영역, 즉 삭제된 노드, 그 아래의 모든 노드, 또는 삭제된 하위 트리의 직계 부모를 참조하면 Keeper는 의도적으로 시작을 거부합니다. 직계 부모를 충돌로 간주하는 이유는 해당 부모의 자식 집합이 로그 기록 당시의 트리와 더 이상 일치하지 않기 때문입니다. 따라서 부모만 건드리는 엔트리라도 다르게 재생될 수 있으며, 여기에는 복구된 자식 카운터를 갱신하는 그 아래 형제 노드의 생성이나 삭제도 포함됩니다. 반면 부모의 존재 여부, 버전 또는 수정 ID만 확인하는 엔트리는 영향을 받지 않으며 복구를 막지 않습니다. 예를 들어 Check, CheckNotExists, numChildren과 cversion을 모두 비교하지 않는 CheckStat, 부모 자신의 데이터나 ACL만 다시 쓰는 Set 또는 SetACL, 클라이언트가 재연결 후 다시 등록하는 data, child, exist watch가 이에 해당합니다. 클라이언트가 다시 등록하는 영구(persistent) watch는 트리를 전혀 참조하지 않으므로 복구를 막지 않습니다. 그보다 더 위쪽의 노드는 영향을 받지 않습니다. 이 노드들은 정상 피어와 정확히 동일한 데이터, 버전, 자식을 유지하므로, 이러한 노드만 건드리는 엔트리는 동일하게 재생되며 복구를 막지 않습니다. 단, 재귀적 삭제나 재귀적 목록 조회처럼 해당 경로 아래의 전체 하위 트리를 순회하는 엔트리는 예외입니다. 이러한 엔트리는 삭제된 노드를 실제로 참조하므로 높이에 관계없이 충돌합니다. 다만 Keeper가 트리를 확인하기 전에 요청을 거부하는 경우(예: /의 재귀적 삭제)는 여기에 해당하지 않습니다. 로그 끝부분의 세션 종료(Close)도 해당 세션이 삭제된 임시(ephemeral) 노드를 소유했거나, 그러한 부모 바로 아래에 남아 있는 임시 노드를 소유하고 있다면 마찬가지입니다. 세션을 종료하면 해당 세션의 임시 노드가 삭제되고 그 부모의 카운터가 갱신되기 때문입니다. 이러한 엔트리를 복구된 트리에 재생하면 아무런 경고 없이 다른 상태가 만들어집니다. 이 경우에는 정상 피어로부터 노드를 복구하십시오. 노드를 중지하고 조정 디렉터리를 비운 다음 리더로부터 다시 동기화하게 하거나, 전략 1 또는 2를 사용하십시오.