클러스터 복제
클러스터 복제 (Cluster Replication)
재해 복구, 데이터 집계, 지리적 분산을 위해 한 HBase 클러스터의 상태를 다른 클러스터와 동기화하는 메커니즘을 설명하는 페이지예요. 복제 설정부터 운영, 모니터링까지 다룹니다.
출처: 문서
본문
클러스터 복제 (Cluster Replication)
HBase는 소스 클러스터의 write-ahead log(WAL)를 사용해 변경 사항을 전파함으로써 한 클러스터의 상태를 다른 클러스터의 상태와 동기화할 수 있게 해주는 클러스터 복제 메커니즘을 제공해요. 클러스터 복제의 사용 사례는 다음과 같아요.
- 백업 및 재해 복구
- 데이터 집계
- 지리적 데이터 분산
- 온라인 데이터 수집과 오프라인 데이터 분석의 결합
복제는 컬럼 패밀리 수준의 세분성으로 활성화돼요. 컬럼 패밀리에 대해 복제를 활성화하기 전에 대상(destination) 클러스터에 테이블과 복제할 모든 컬럼 패밀리를 만드세요.
복제는 백그라운드에서 WAL을 다른 클러스터로 보내므로 비동기적이에요. 즉 복제를 통해 복구하려면 일부 데이터를 잃을 수 있어요. 이 문제를 해결하기 위해 우리는 동기 복제(synchronous replication)라는 새 기능을 도입했어요. 메커니즘이 약간 다르므로 별도 섹션에서 설명해요. Synchronous Replication을 참고하세요.
현재 Replication과 WAL Compression을 함께 사용하면 호환성 문제가 있어요. Replication을 사용해야 한다면 hbase.regionserver.wal.enablecompression 속성을 false로 설정하는 것이 좋아요. 자세한 내용은 (HBASE-26849)를 참고하세요.
복제 개요 (Replication Overview)
클러스터 복제는 소스-푸시(source-push) 방법론을 사용해요. HBase 클러스터는 소스(master 또는 active라고도 하며, 새 데이터의 원산지라는 뜻)일 수도, 대상(destination이라고도 하며, 복제를 통해 데이터를 받는다는 뜻)일 수도, 두 역할을 동시에 수행할 수도 있어요. 복제는 비동기이며 복제의 목표는 최종 일관성(eventual consistency)이에요. 소스가 복제가 활성화된 컬럼 패밀리에 대한 편집을 받으면, 관련 region을 관리하는 RegionServer의 해당 컬럼 패밀리용 WAL을 사용해 그 편집이 모든 대상 클러스터로 전파돼요.
데이터가 한 클러스터에서 다른 클러스터로 복제될 때 데이터의 원래 소스는 메타데이터의 일부인 클러스터 ID를 통해 추적돼요. HBase 0.96 이상(HBASE-7709)에서는 이미 데이터를 소비한 모든 클러스터도 추적돼요. 이는 복제 루프를 방지해요.
각 region server의 WAL은 어떤 슬레이브 클러스터에 데이터를 복제하는 데 필요한 한동안 HDFS에 유지되어야 해요. 각 region server는 복제해야 할 가장 오래된 로그부터 읽고, 실패 복구를 단순화하기 위해 ZooKeeper 안에서 WAL 처리 진행 상황을 추적해요. 슬레이브 클러스터의 진행 상황을 나타내는 위치 마커와 처리할 WAL 큐는 슬레이브 클러스터마다 다를 수 있어요.
복제에 참여하는 클러스터는 크기가 다를 수 있어요. 마스터 클러스터는 슬레이브 클러스터에서 복제 스트림의 균형을 맞추기 위해 무작위화를 사용해요. 슬레이브 클러스터가 복제된 데이터와 자체적으로 수집해야 하는 데이터를 담을 저장 용량이 있다고 예상돼요. 슬레이브 클러스터가 공간이 부족하거나 다른 이유로 접근 불가하면 오류를 던지고, 마스터는 WAL을 유지하며 간격을 두고 복제를 재시도해요.
복제된 클러스터 간의 일관성 (Consistency Across Replicated Clusters)
복제가 작동할 때 애플리케이션이 HBase API 위에 어떻게 구축되는지가 중요해요. HBase의 복제 시스템은 활성화된 컬럼 패밀리의 클라이언트 편집을 각 구성된 대상 클러스터로 적어도 한 번(at-least-once) 전달해요. 특정 대상에 도달하지 못하면 복제 시스템은 주어진 메시지를 반복할 수 있는 방식으로 편집 전송을 재시도해요. HBase는 두 가지 복제 방식을 제공해요. 하나는 원래 복제이고 다른 하나는 직렬 복제(serial replication)예요. 이전 복제 방식에서는 클라이언트 편집의 전달 순서가 보장되지 않아요. RegionServer가 실패하면 복제 큐의 복구는 그 서버가 이전에 처리하던 개별 region들의 복구와 독립적으로 발생해요. 이는 아직 복제되지 않은 편집이, 실패 후의 편집을 처리하는 것보다 현재 더 느리게 복제하는 RegionServer에 의해 처리될 수 있음을 의미해요. 이 두 속성(적어도 한 번 전달과 메시지 순서 없음)의 조합은 애플리케이션이 Increments처럼 멱등(idempotent)하지 않은 연산을 사용하면 일부 대상 클러스터가 다른 상태로 끝날 수 있다는 뜻이에요. 이 문제를 해결하기 위해 HBase는 이제 클라이언트의 요청 순서대로 편집을 대상 클러스터로 보내는 직렬 복제를 지원해요. Serial Replication을 참고하세요.
용어 변경 (Terminology Changes)
이전에는 master-master, master-slave, cyclical 같은 용어로 HBase의 복제 관계를 설명했어요. 이 용어들은 혼란을 더해, 다양한 시나리오에 적합한 클러스터 토폴로지에 대한 논의를 선호하며 사용되지 않게 됐어요.
클러스터 토폴로지 (Cluster Topologies)
- 중앙 소스 클러스터는 페일오버나 지리적 분산 때문에 변경 사항을 여러 대상 클러스터로 전파할 수 있어요.
- 소스 클러스터가 대상 클러스터로 변경 사항을 밀어 넣을 수 있고, 대상이 자신의 변경 사항을 원래 클러스터로 다시 밀어 넣을 수도 있어요.
- 여러 다른 저지연 클러스터가 백업이나 리소스 집약적인 데이터 분석 작업을 위해 한 중앙집중 클러스터로 변경 사항을 밀어 넣을 수 있어요. 처리된 데이터는 그다음 저지연 클러스터로 다시 복제될 수 있어요.
조직의 필요에 따라 여러 수준의 복제를 체인으로 연결할 수 있어요. 다음 다이어그램은 가상의 시나리오를 보여줘요. 화살표로 데이터 경로를 따라가세요.
복잡한 클러스터 복제 설정의 예시 (Example of a Complex Cluster Replication Configuration)
HBase 복제는 MySQL이 사용하는 statement-based replication 설계에서 많은 개념을 차용해요. SQL 문 대신, 원자성을 유지하기 위해 전체 WALEdit(클라이언트의 Put과 Delete 연산에서 오는 여러 셀 삽입으로 구성)이 복제돼요.
클러스터 복제 관리·구성 (Managing and Configuring Cluster Replication)
클러스터 구성 개요 (Cluster Configuration Overview)
- 소스와 대상 클러스터를 구성하고 시작하세요. 소스와 대상 클러스터 양쪽에 같은 이름과 컬럼 패밀리를 가진 테이블을 만들어 대상 클러스터가 받을 데이터를 어디에 저장할지 알게 하세요.
- 소스와 대상 클러스터의 모든 호스트는 서로 연결 가능해야 해요.
- 두 클러스터가 같은 ZooKeeper 클러스터를 사용한다면 다른
zookeeper.znode.parent를 사용해야 해요. 같은 폴더에 쓸 수 없으니까요. - 소스 클러스터의 HBase Shell에서
add_peer명령으로 대상 클러스터를 피어(peer)로 추가하세요. - 소스 클러스터의 HBase Shell에서
enable_table_replication명령으로 테이블 복제를 활성화하세요. - 로그를 확인해 복제가 일어나는지 보세요. 진행된다면 ReplicationSource에서 다음과 같은 메시지를 볼 거예요.
LOG.info("Replicating "+clusterId + " -> " + peerClusterId);
직렬 복제 구성 (Serial Replication Configuration)
Serial Replication을 참고하세요.
클러스터 관리 명령 (Cluster Management Commands)
add_peer
두 클러스터 사이에 복제 관계를 추가해요.
- ID — 하이픈을 포함해서는 안 되는 고유 문자열.
- CLUSTER_KEY: 적절한 플레이스홀더와 함께 다음 템플릿으로 구성:
hbase.zookeeper.quorum:hbase.zookeeper.property.clientPort:zookeeper.znode.parent. 이 값은 Master UI 정보 페이지에서 찾을 수 있어요. - STATE(선택): ENABLED 또는 DISABLED, 기본값은 ENABLED.
list_peers
이 클러스터가 아는 모든 복제 관계를 나열해요.
enable_peer
이전에 비활성화된 복제 관계를 활성화해요.
disable_peer
복제 관계를 비활성화해요. HBase는 더 이상 그 피어 클러스터로 편집을 보내지 않지만, 다시 활성화될 경우 복제해야 할 모든 새 WAL을 여전히 추적해요. 피어가 존재하는 한 복제를 활성화·비활성화할 때 WAL이 유지돼요.
remove_peer
복제 관계를 비활성화하고 제거해요. HBase는 더 이상 그 피어 클러스터로 편집을 보내거나 WAL을 추적하지 않아요.
enable_table_replication <TABLE_NAME>
모든 컬럼 패밀리에 대해 테이블 복제 스위치를 활성화해요. 대상 클러스터에서 테이블을 찾지 못하면 같은 이름과 컬럼 패밀리로 테이블을 만들어요.
disable_table_replication <TABLE_NAME>
모든 컬럼 패밀리에 대해 테이블 복제 스위치를 비활성화해요.
peer_modification_switch <enable_or_disable>, <drain_procedures>
피어 수정 연산(복제 피어 추가/제거 같은)을 활성화/비활성화해요. 두 번째 매개변수는 피어 수정을 비활성화할 때 기존의 모든 피어 수정 프로시저가 끝날 때까지 기다릴지 여부를 의미해요.
peer_modification_enabled
피어 수정이 활성화되어 있는지 확인해요.
다른 복제 피어 스토리지로 마이그레이션 (Migrate Across Different Replication Peer Storages)
2.6.0부터 우리는 파일시스템 기반 ReplicationPeerStorage를 도입했어요. 이것은 ZooKeeper의 znodes 대신 HFile 파일시스템의 파일로 복제 피어 상태를 저장해요. 그리고 서로 다른 복제 피어 스토리지 간에 복제 피어 상태를 복사하는 도구도 구현했어요.
./bin/hbase copyreppeers <SRC_REPLICATION_PEER_STORAGE> <DST_REPLICATION_PEER_STORAGE>
온라인 마이그레이션을 지원하기 위해 peer_modification_switch라는 셸 명령을 도입했어요.
hbase> peer_modification_switch false, true
위 명령으로 피어 수정 연산을 비활성화할 수 있어요. 두 번째 true는 기존의 모든 복제 피어 수정 프로시저가 끝날 때까지 기다렸다가 반환하겠다는 뜻이에요. 피어 수정을 비활성화한 뒤 위 도구로 복제 피어 상태를 복사하고, 클러스터의 모든 hbase-site.xml 파일을 업데이트해 새 복제 피어 스토리지를 지정하고, 마지막으로 온라인 설정 업데이트를 트리거해 새 복제 피어 스토리지를 로드하는 것이 안전해요.
직렬 복제 (Serial Replication)
메모: 이 기능은 HBase 2.1에서 도입됐어요.
직렬 복제의 기능 (Function of serial replication)
직렬 복제는 로그가 소스 클러스터에 도달하는 것과 같은 순서로 대상 클러스터로 로그를 푸시하는 것을 지원해요.
왜 직렬 복제가 필요한가? (Why need serial replication?)
HBase의 복제에서 우리는 각 region server의 WAL을 읽어 대상 클러스터로 mutations를 푸시해요. WAL 파일용 큐가 있어 생성 시간 순서대로 읽을 수 있어요. 그러나 소스 클러스터에서 region-move나 RS 실패가 발생하면, region-move나 RS-failure 이전에 푸시되지 않은 hlog 항목은 원래 RS(region move의 경우)나 죽은 RS의 남은 hlog를 인수하는 다른 RS(RS failure의 경우)가 푸시하고, 같은 region(s)에 대한 새 항목은 지금 그 region(s)을 서빙하는 RS가 푸시하지만, 그들은 조정 없이 같은 region의 hlog 항목을 동시에 푸시해요.
이 처리는 소스와 대상 클러스터 사이의 데이터 불일치를 초래할 수 있어요.
- 소스 클러스터에 put이 쓰여지고 그다음 delete가 쓰여진다.
- region-move / RS-failure 때문에 그것들은 서로 다른 replication-source 스레드가 피어 클러스터로 푸시한다.
- delete가 put보다 먼저 피어 클러스터로 푸시되고, put이 피어 클러스터로 푸시되기 전에 피어 클러스터에서 flush와 major-compact가 발생하면, delete는 수집되고 put은 피어 클러스터에 남는다. 하지만 소스 클러스터에서는 put이 delete에 의해 마스킹되므로 소스와 대상 클러스터 사이에 데이터 불일치가 발생한다.
직렬 복제 구성 (Serial replication configuration)
복제 피어에 대해 serial 플래그를 true로 설정하세요. 기본 serial 플래그는 false예요.
-
serial 플래그가 true인 새 복제 피어 추가 hbase> add_peer '1', CLUSTER_KEY => "server1.cie.com:2181:/hbase", SERIAL => true
-
복제 피어의 serial 플래그를 false로 설정 hbase> set_peer_serial '1', false
-
복제 피어의 serial 플래그를 true로 설정 hbase> set_peer_serial '1', true
직렬 복제 기능은 먼저 HBASE-9465에서 이루어졌고, 그다음 HBASE-20046에서 되돌렸다가 다시 이루어졌어요. 이 이슈들에서 더 많은 세부를 찾을 수 있어요.
복제된 데이터 검증 (Verifying Replicated Data)
HBase에 포함된 VerifyReplication MapReduce 작업은 두 클러스터 사이의 복제된 데이터를 체계적으로 비교해요. VerifyReplication 작업을 master 클러스터에서 실행하고 검증에 사용할 피어 ID와 테이블 이름을 제공하세요. 시간 범위나 특정 패밀리를 지정해 검증을 더 제한할 수 있어요. 작업의 짧은 이름은 verifyrep이에요. 작업을 실행하려면 다음과 같은 명령을 사용하세요.
$ HADOOP_CLASSPATH=${HBASE_HOME}/bin/hbase classpath "${HADOOP_HOME}/bin/hadoop" jar "${HBASE_HOME}/hbase-mapreduce-VERSION.jar" verifyrep --starttime=
VerifyReplication명령은 올바르게 복제된 행과 그렇지 않은 행을 나타내기 위해GOODROWS와BADROWS카운터를 출력해요.
클러스터 복제에 대한 자세한 정보 (Detailed Information About Cluster Replication)
복제 아키텍처 개요 (Replication Architecture Overview)
WAL 편집의 수명 (Life of a WAL Edit)
단일 WAL 편집은 슬레이브 클러스터로 복제되기 위해 여러 단계를 거쳐요.
- HBase 클라이언트가 Put이나 Delete 연산으로 HBase의 데이터를 조작한다.
- region server는 성공적으로 쓰여지지 않으면 재생할 수 있는 방식으로 요청을 WAL에 쓴다.
- 변경된 셀이 복제 범위로 설정된 컬럼 패밀리에 해당하면 편집이 복제용 큐에 추가된다.
- 별도 스레드에서 편집이 배치 프로세스의 일부로 로그에서 읽힌다. 복제 자격이 있는 KeyValue만 유지된다. 복제 가능한 KeyValue는 스키마가 GLOBAL로 범위가 지정된 컬럼 패밀리의 일부이고,
hbase:meta같은 카탈로그의 일부가 아니며, 대상 슬레이브 클러스터에서 발생하지 않았고, 이미 대상 슬레이브 클러스터에 의해 소비되지 않은 것이다. - 편집이 master의 UUID로 태그가 붙고 버퍼에 추가된다. 버퍼가 차거나 reader가 파일 끝에 도달하면 버퍼는 슬레이브 클러스터의 무작위 region server로 보내진다.
- region server는 편집을 순차적으로 읽어 테이블당 하나의 버퍼로 분리한다. 모든 편집을 읽은 뒤 각 버퍼는 HBase의 일반 클라이언트인 Table을 사용해 flush된다. master의 UUID와 이미 데이터를 소비한 슬레이브들의 UUID는 적용되는 편집에 보존되어 복제 루프를 방지한다.
- master에서 현재 복제 중인 WAL의 오프셋이 ZooKeeper에 등록된다.
- 첫 세 단계(편집이 삽입되는 곳)는 동일하다.
- 다시 별도 스레드에서 region server가 위와 같은 방식으로 로그 편집을 읽고 필터링하고 편집한다. 슬레이브 region server는 RPC 호출에 응답하지 않는다.
- master는 잠자고 구성 가능한 횟수만큼 다시 시도한다.
- 슬레이브 region server가 여전히 사용 불가하면 master는 복제할 region server의 새 하위 집합을 선택하고 편집 버퍼를 다시 보내려 한다.
- 그동안 WAL들은 롤링되고 ZooKeeper의 큐에 저장된다. region server가 로그를 region server의 로그 디렉터리에서 중앙 로그 디렉터리로 이동해 보관하면, 복제 스레드의 메모리 내 큐에서 경로를 업데이트한다.
- 슬레이브 클러스터가 마침내 사용 가능해지면 버퍼가 정상 처리와 같은 방식으로 적용된다. master region server는 그다음 중단 동안 축적된 로그 백로그를 복제한다.
큐 페일오버 부하 분산 (Spreading Queue Failover Load)
복제가 활성화되면 소스 클러스터의 region server 하위 집합이 편집을 싱크로 운반할 책임을 진다. 이 책임은 프로세스나 노드가 크래시하면 다른 모든 region server 기능처럼 페일오버되어야 한다. 소스 클러스터의 나머지 살아있는 서버들 위에서 복제 활동의 균등한 분포를 유지하기 위해 다음 설정이 권장된다.
replication.source.maxretriesmultiplier를300으로 설정.replication.source.sleepforretries를1(1초)로 설정. 이 값은replication.source.maxretriesmultiplier값과 결합되어 재시도 주기가 약 5분 동안 지속되게 한다.- 소스 클러스터 사이트 설정에서
replication.sleep.before.failover를30000(30초)로 설정.
복제 중 태그 보존 (Preserving Tags During Replication)
기본적으로 클러스터 간 복제에 사용되는 codec은 셀 수준 ACL 같은 태그를 셀에서 제거한다. 태그가 제거되지 않도록 태그를 제거하지 않는 다른 codec을 사용할 수 있다. 복제에 관여하는 소스와 싱크 RegionServer 양쪽에서 hbase.replication.rpc.codec를 org.apache.hadoop.hbase.codec.KeyValueCodecWithTags로 구성하세요. 이 옵션은 HBASE-10322에서 도입됐다.
복제 내부 (Replication Internals)
복제 상태 저장 (Replication State Storage)
HBASE-15867에서 우리는 복제 상태 저장을 위한 두 인터페이스 ReplicationPeerStorage와 ReplicationQueueStorage를 추상화했다. 전자는 복제 피어 관련 상태를 저장하고, 후자는 복제 큐 관련 상태를 저장한다. HBASE-15867은 절반만 완료됐다. 이 두 인터페이스를 추상화했지만 여전히 zookeeper 기반 구현만 있기 때문이다.
그리고 HBASE-27110에서 우리는 파일시스템 기반 복제 피어 스토리지를 구현해 복제 피어 상태를 파일시스템에 저장한다. 물론 zookeeper 기반 복제 피어 스토리지를 계속 사용할 수도 있다.
그리고 HBASE-27109에서 우리는 복제 큐 스토리지를 zookeeper 기반에서 hbase 테이블 기반으로 변경했다. 자세한 내용은 아래 Replication Queue State의 hbase:replication 테이블 섹션을 참고하세요.
ZooKeeper의 복제 상태 (Replication State in ZooKeeper)
기본적으로 상태는 기본 노드 /hbase/replication에 포함된다. 보통 이 노드는 두 개의 자식 노드를 포함하는데, peers znode는 복제 피어 상태를 저장하고, rs znode는 복제 큐 상태를 저장한다. 그리고 파일시스템 기반 복제 피어 스토리지를 선택하면 peers znode를 볼 수 없다. 3.0.0부터 복제 큐 상태를 hbase:replication 테이블(아래 참고)로 옮겼으므로 rs znode를 볼 수 없다.
Peers Znode
peers znode는 기본적으로 /hbase/replication/peers에 저장된다. 모든 피어 복제 클러스터 목록과 각각의 상태로 구성된다. 각 피어의 값은 HBase Shell에서 제공되는 클러스터 키다. 클러스터 키에는 해당 클러스터에서 HBase의 quorum인 ZooKeeper 노드 목록, ZooKeeper quorum의 클라이언트 포트, HDFS의 HBase 기본 znode가 포함된다. 3.0.0부터 연결 URI를 클러스터 키로 지정할 수도 있다. 연결 URI에 대한 자세한 내용은 Connection URI를 참고하세요.
RS Znode
rs znode는 복제해야 할 WAL 로그 목록을 포함한다. 이 목록은 region server별, 그리고 region server가 로그를 운반하는 피어 클러스터별로 구성된 큐 집합으로 나뉜다. rs znode는 클러스터의 각 region server에 대해 하나의 자식 znode를 가진다. 자식 znode 이름은 region server의 호스트명, 클라이언트 포트, 시작 코드다. 이 목록은 살아있는 region server와 죽은 region server를 모두 포함한다.
hbase:replication 테이블
3.0.0 이후 Queue는 hbase:replication 테이블에 저장되며, 행 키는 <PeerId>-<ServerName>[/<SourceServerName>]이고, WAL 그룹이 퀄리파이어이며, 직렬화된 ReplicationGroupOffset이 값이다. ReplicationGroupOffset은 해당 큐(<PeerId>-<ServerName>[/<SourceServerName>])의 wal 파일과 그 오프셋을 포함한다. 파일별이 아니라 큐별로 복제 오프셋을 추적하므로 큐당 하나의 복제 오프셋만 저장하면 된다.
ReplicationPeerStorage의 다른 구현 (Other implementations for ReplicationPeerStorage)
2.6.0부터 파일시스템 기반 ReplicationPeerStorage를 도입했는데, ZooKeeper의 znodes 대신 HFile 파일시스템의 파일로 복제 피어 상태를 저장한다. 레이아웃은 zookeeper의 znodes와 거의 같고, 주요 차이는 HFile 파일시스템이 원자적 rename을 지원하지 않을 수 있어 상태를 저장하는 데 두 파일을 사용하고 읽을 때 둘 다 읽은 뒤 타임스탬프를 비교해 더 새로운 것을 찾는다는 것이다. 따라서 보통 두 개의 피어 설정 파일을 보게 된다. 그리고 enable/disable 상태의 경우 피어가 비활성화되면 disabled 파일을 touch하고, 활성화할 때 파일을 제거한다.
복제할 region server 선택 (Choosing Region Servers to Replicate To)
마스터 클러스터 region server가 슬레이브 클러스터로 복제 소스를 시작하면, 먼저 제공된 클러스터 키를 사용해 슬레이브의 ZooKeeper 앙상블에 연결한다. 그다음 rs/ 디렉터리를 스캔해 모든 사용 가능한 싱크(복제용 편집 스트림을 받아들이는 region server)를 발견하고, 기본값 10%인 구성된 비율을 사용해 그 중 무작위 하위 집합을 선택한다. 예를 들어 슬레이브 클러스터에 150대 머신이 있으면 15대가 이 마스터 클러스터 region server가 보내는 편집의 잠재 수신자로 선택된다. 이 선택이 각 마스터 region server에 의해 수행되므로 모든 슬레이브 region server가 사용될 확률은 매우 높고, 이 방법은 어떤 크기의 클러스터에서도 작동한다. 예를 들어 10대 머신 마스터 클러스터가 10% 비율로 5대 머신 슬레이브 클러스터에 복제하면, 마스터 클러스터 region server들이 각각 무작위로 머신 하나를 선택하게 한다.
마스터 클러스터의 각 region server는 슬레이브 클러스터의 $zookeeper.znode.parent/rs 노드에 ZooKeeper watcher를 둔다. 이 watch는 슬레이브 클러스터 구성의 변경을 모니터링하는 데 사용된다. 슬레이브 클러스터에서 노드가 제거되거나, 노드가 다운되거나 다시 살아나면, 마스터 클러스터의 region server들은 복제할 슬레이브 region server의 새 풀을 선택해 응답한다.
로그 추적 (ZooKeeper 기반) (Keeping Track of Logs(based on ZooKeeper))
각 마스터 클러스터 region server는 복제 znode 계층에 자신의 znode를 가진다. 피어 클러스터마다 하나의 znode를 포함하고(슬레이브 클러스터 5개면 5개 znode 생성), 각각은 처리할 WAL 큐를 포함한다. 이 큐들은 그 region server가 만든 WAL을 추적하지만 크기가 다를 수 있다. 예를 들어 한 슬레이브 클러스터가 잠시 사용 불가해지면 WAL은 삭제되어서는 안 되므로, 다른 것들은 처리되는 동안 그 큐에 남아 있어야 한다. 예시는 rs.failover.details를 참고하세요.
소스가 인스턴스화되면 region server가 쓰고 있는 현재 WAL을 포함한다. 로그 롤링 중에 새 파일은 사용 가능해지기 직전에 각 슬레이브 클러스터 znode의 큐에 추가된다. 이는 region server가 그 파일에 편집을 추가할 수 있기 전에 모든 소스가 새 로그의 존재를 알고 있도록 보장하지만, 이 연산은 이제 더 비싸다. 복제 스레드가 파일(마지막 블록 끝에 도달해서)에서 더 이상 항목을 읽을 수 없고 큐에 다른 파일이 있을 때 큐 항목이 버려진다. 즉 소스가 최신 상태이고 region server가 쓰는 로그에서 복제한다면, 현재 파일의 "끝"까지 읽는 것이 큐의 항목을 삭제하지 않는다.
로그는 더 이상 사용되지 않거나, 삽입 속도가 region이 flush되는 것보다 빠르기 때문에 로그 수가 hbase.regionserver.maxlogs를 초과하면 보관(archived)될 수 있다. 로그가 보관되면 소스 스레드는 그 로그의 경로가 변경됐다고 통지받는다. 특정 소스가 이미 보관된 로그를 끝냈다면 메시지를 무시할 뿐이다. 로그가 큐에 있다면 경로가 메모리에서 업데이트된다. 로그가 현재 복제 중이라면 변경이 원자적으로 이루어져서 reader가 이미 이동된 파일을 열려 시도하지 않는다. 파일 이동은 NameNode 연산이므로 reader가 현재 로그를 읽고 있다면 예외를 생성하지 않는다.
로그 추적 (hbase 테이블 기반) (Keeping Track of Logs(based on hbase table))
3.0.0 이후 테이블 기반 구현에서 행 키에 server name을 가지므로, 주어진 피어에 많은 행을 갖게 된다.
일반 복제 큐의 경우, WAL 파일은 여전히 살아있는 region server에 속하고 모든 WAL 파일이 메모리에 유지되므로 복제 큐 스토리지에서 WAL 파일을 가져올 필요가 없다. 그리고 복구된 복제 큐의 경우 HDFS의 old WAL 디렉터리를 나열해 죽은 region server의 WAL 파일을 얻을 수 있다. 따라서 이론적으로 복제 큐 스토리지에 모든 WAL 파일을 저장할 필요가 없다. 게다가 보통 생성 시간을 WAL 파일 이름에 저장하므로 WAL 그룹의 모든 WAL 파일을 정렬할 수 있고(실제로 현재 복제 프레임워크에서 정렬한다), 이는 큐당 하나의 복제 오프셋만 저장하면 된다는 뜻이다. 복구된 복제 큐를 시작할 때 이 오프셋보다 앞의 모든 파일을 건너뛰고 이 오프셋부터 복제를 시작한다.
ReplicationLogCleaner의 경우 이 오프셋 앞의 모든 파일은 삭제될 수 있고, 그렇지 않으면 삭제되지 않는다.
편집 읽기·필터링·보내기 (Reading, Filtering and Sending Edits)
기본적으로 소스는 WAL에서 최대한 빨리 읽고 로그 항목을 싱크로 운반하려 한다. 속도는 로그 항목의 필터링에 의해 제한된다. GLOBAL로 범위가 지정되고 카탈로그 테이블에 속하지 않는 KeyValue만 유지된다. 속도는 또한 슬레이브당 복제할 편집 목록의 총 크기로도 제한되며, 기본적으로 64 MB로 제한된다. 이 구성으로 슬레이브 3개를 가진 마스터 클러스터 region server는 복제할 데이터를 저장하는 데 최대 192 MB를 사용한다. 이것은 필터링됐지만 가비지 컬렉션되지 않은 데이터는 포함하지 않는다.
편집의 최대 크기가 버퍼링되거나 reader가 WAL 끝에 도달하면, 소스 스레드는 읽기를 멈추고 복제할 싱크를 무작위로 선택한다(슬레이브 region server의 하위 집합만 유지해 생성된 목록에서). 선택한 region server에 RPC를 직접 발행하고 메서드가 반환되기를 기다린다. RPC가 성공하면 소스는 현재 파일이 비워졌는지 아니면 읽어야 할 데이터가 더 있는지 결정한다. 파일이 비워졌으면 소스는 큐에서 znode를 삭제한다. 그렇지 않으면 로그의 znode에 새 오프셋을 등록한다. RPC가 예외를 던지면 소스는 다른 싱크를 찾기 전에 10번 재시도한다.
로그 정리 (Cleaning Logs)
복제가 활성화되지 않으면 master의 로그-정리 스레드는 구성된 TTL을 사용해 오래된 로그를 삭제한다. 이 TTL 기반 방법은 보관된 로그가 TTL을 초과했어도 여전히 큐에 있을 수 있으므로 복제와 잘 작동하지 않는다. 기본 동작은 보강되어, 로그가 TTL을 지나면 정리 스레드는 로그를 찾을 때까지 모든 큐를 조회하고, 찾은 큐를 캐시한다. 로그가 어떤 큐에서도 발견되지 않으면 로그는 삭제된다. 다음에 정리 프로세스가 로그를 찾을 필요가 있을 때 캐시된 목록부터 시작한다.
피어가 존재하는 한 복제를 활성화·비활성화할 때 WAL이 저장된다.
region server 페일오버 (Region Server Failover)
실패하는 region server가 없을 때 ZooKeeper에서 로그를 추적하는 것은 가치를 더하지 않는다. 안타깝게도 region server는 실패하며, ZooKeeper는 고가용성이므로 실패 시 큐 전송을 관리하는 데 유용하다. 마스터 클러스터의 각 region server는 다른 모든 region server에 watcher를 유지해, 하나가 죽을 때 통지를 받는다(master가 하는 것처럼). 실패가 발생하면 모두가 죽은 region server의 znode 안에 lock이라는 znode를 만들기 위해 경쟁한다. 성공적으로 만든 region server는 모든 큐를 자신의 znode로 하나씩 전송한다(ZooKeeper가 큐 이름 변경을 지원하지 않으므로). 모든 큐가 전송된 후에는 옛 위치에서 삭제된다. 복구된 znode는 죽은 서버의 이름이 붙은 슬레이브 클러스터의 ID로 이름이 변경된다.
다음으로 마스터 클러스터 region server는 복사된 큐마다 새 소스 스레드를 하나 만들고, 각 소스 스레드는 읽기/필터링/운반 패턴을 따른다. 주요 차이는 그 큐들이 새 region server에 속하지 않으므로 새 데이터를 절대 받지 않는다는 것이다. reader가 마지막 로그 끝에 도달하면 큐의 znode가 삭제되고 마스터 클러스터 region server가 그 복제 소스를 닫는다.
2.5.0부터 페일오버 로직이 SCP로 옮겨졌는데, SCP에 SERVER_CRASH_CLAIM_REPLICATION_QUEUES 단계를 추가해 죽은 서버의 복제 큐를 클레임한다. 그리고 3.0.0부터 복제 큐 스토리지를 zookeeper에서 table로 변경했고, 복제 큐 스토리지에 대한 업데이트는 비동기이므로 클레임 전에 누락된 복제 큐를 추가하는 추가 단계도 필요하다.
복제 큐 클레임 (ZooKeeper 기반) (The replication queue claiming (based on ZooKeeper))
단일 슬레이브(아이디 2)로 복제하는 3대의 region server가 있는 마스터 클러스터가 주어졌을 때, 다음 계층은 어떤 시점의 znode 레이아웃을 나타낸다. region server의 znode들은 모두 peers znode를 포함하고 그것은 단일 큐를 포함한다. 큐의 znode 이름은 address,port.timestamp 형태의 HDFS의 실제 파일 이름을 나타낸다.
/hbase/replication/rs/ 1.1.1.1,60020,123456780/ 2/ 1.1.1.1,60020.1234 (Contains a position) 1.1.1.1,60020.1265 1.1.1.2,60020,123456790/ 2/ 1.1.1.2,60020.1214 (Contains a position) 1.1.1.2,60020.1248 1.1.1.2,60020.1312 1.1.1.3,60020, 123456630/ 2/ 1.1.1.3,60020.1280 (Contains a position)
1.1.1.2가 ZoneKeeper 세션을 잃는다고 가정하자. 생존자들은 lock을 만들기 위해 경쟁하고, 임의로 1.1.1.3이 이긴다. 그러면 죽은 서버의 이름을 붙여 모든 큐를 로컬 peers znode로 전송하기 시작한다. 1.1.1.3이 옛 znodes를 정리할 수 있기 직전에 레이아웃은 다음과 같다.
/hbase/replication/rs/ 1.1.1.1,60020,123456780/ 2/ 1.1.1.1,60020.1234 (Contains a position) 1.1.1.1,60020.1265 1.1.1.2,60020,123456790/ lock 2/ 1.1.1.2,60020.1214 (Contains a position) 1.1.1.2,60020.1248 1.1.1.2,60020.1312 1.1.1.3,60020,123456630/ 2/ 1.1.1.3,60020.1280 (Contains a position)
2-1.1.1.2,60020,123456790/
1.1.1.2,60020.1214 (Contains a position)
1.1.1.2,60020.1248
1.1.1.2,60020.1312
얼마 후, 1.1.1.3이 1.1.1.2의 마지막 WAL 복제를 끝내기 전에 그것도 죽는다. 정상 큐에 새 로그도 만들어졌다. 마지막 region server는 그다음 1.1.1.3의 znode를 잠그려 시도하고 모든 큐 전송을 시작한다. 새 레이아웃은 다음과 같다.
/hbase/replication/rs/ 1.1.1.1,60020,123456780/ 2/ 1.1.1.1,60020.1378 (Contains a position)
2-1.1.1.3,60020,123456630/
1.1.1.3,60020.1325 (Contains a position)
1.1.1.3,60020.1401
2-1.1.1.2,60020,123456790-1.1.1.3,60020,123456630/
1.1.1.2,60020.1312 (Contains a position)
1.1.1.3,60020,123456630/ lock 2/ 1.1.1.3,60020.1325 (Contains a position) 1.1.1.3,60020.1401
2-1.1.1.2,60020,123456790/
1.1.1.2,60020.1312 (Contains a position)
복제 큐 클레임 (hbase 테이블 기반) (The replication queue claiming(based on hbase table))
단일 슬레이브(아이디 2)로 복제하는 3대의 region server가 있는 마스터 클러스터가 주어졌을 때, 다음 정보는 어떤 시점의 hbase:replication의 큐 저장 레이아웃을 나타낸다. 행 키는 <PeerId>-<ServerName>[/<SourceServerName>]이고 값은 WAL && Offset이다.
| WAL && Offset | |
|---|---|
| 2-1.1.1.1,60020,123456780 | 1.1.1.1,60020.1234 (Contains a position) |
| 2-1.1.1.2,60020,123456790 | 1.1.1.2,60020.1214 (Contains a position) |
| 2-1.1.1.3,60020,123456630 | 1.1.1.3,60020.1280 (Contains a position) |
1.1.1.2가 실패했다고 가정하자. 생존자들이 그것의 큐를 클레임하고, 임의로 1.1.1.3이 이긴다. 그것은 1.1.1.2의 모든 큐를 클레임하며, 복제 큐의 행을 제거하고 새 행(server name을 큐를 클레임한 region server로 바꿈)을 삽입하는 것을 포함한다. 마지막으로 레이아웃은 다음과 같다.
| WAL && Offset | |
|---|---|
| 2-1.1.1.1,60020,123456780 | 1.1.1.1,60020.1234 (Contains a position) |
| 2-1.1.1.3,60020,123456630 | 1.1.1.3,60020.1280 (Contains a position) |
| 2-1.1.1.3,60020,123456630 1.1.1.2,60020,123456790 | 1.1.1.2,60020.1214 (Contains a position) |
복제 메트릭 (Replication Metrics)
다음 메트릭은 전역 region server 수준과 피어 수준에서 노출된다.
source.sizeOfLogQueue
Replication 소스에서 처리할 WAL 수(처리 중인 것을 제외)
source.shippedOps
운반된(shipped) mutations 수
source.logEditsRead
복제 소스에서 WAL에서 읽은 mutations 수
source.ageOfLastShippedOp
복제 소스가 마지막으로 운반한 배치의 나이
source.completedLogs
이 소스와 연결된 피어로 승인된 전송을 완료한 write-ahead-log 파일 수. 이 메트릭의 증가는 HBase 복제의 정상 운영의 일부다.
source.completedRecoverQueues
이 소스가 연결된 피어로 보내기를 완료한 복구 큐 수. 이 메트릭의 증가는 실패한 Region Server에 직면한 HBase 복제의 정상 복구의 일부다.
source.uncleanlyClosedLogs
복제 시스템이 깨끗하지 않게(uncleanly) 닫힌 파일에 직면해 읽을 수 있는 항목의 끝에 도달한 뒤 완료로 간주한 write-ahead-log 파일 수.
source.ignoredUncleanlyClosedLogContentsInBytes
write-ahead-log 파일이 깨끗하게 닫히지 않으면 부분적으로 직렬화된 항목이 있을 가능성이 높다. 이 메트릭은 HBase 복제 시스템이 깨끗하지 않게 닫힌 파일에 직면해 건너뛴 파일 끝에 남아 있다고 믿는 그러한 항목의 바이트 수를 포함한다. 그 바이트들은 다른 파일에 있거나 승인되지 않은 클라이언트 쓰기를 나타내야 한다.
source.restartedLogReading
HBase 복제 시스템이 깨끗하게 닫힌 write-ahead-log 파일을 올바르게 파싱하는 데 실패했음을 감지한 횟수. 이 상황에서 시스템은 시작부터 전체 로그를 재생해 연결된 피어가 승인하지 않는 편집이 없도록 보장한다. 이 메트릭의 증가는 HBase 복제 시스템이 기본 분산 저장 시스템의 실패를 올바르게 처리하는 데 어려움을 겪고 있다는 것을 나타낸다. 데이터 손실은 없어야 하지만, 실패의 세부 사항은 Region Server 로그 파일을 확인해야 한다.
source.repeatedLogFileBytes
HBase 복제 시스템이 주어진 write-ahead-log 파일을 재생해야 한다고 결정할 때, 이 메트릭은 복제 시스템이 다시 시작하기 전에 연결된 피어가 이미 승인했다고 믿는 바이트 수만큼 증가한다.
source.closedLogsWithUnknownFileLength
HBase 복제 시스템이 write-ahead-log 파일의 끝에 있다고 믿지만 기본 분산 저장 시스템에서 그 파일의 길이를 결정할 수 없을 때 증가한다. 복제 시스템이 읽을 수 있는 항목의 끝이 파일의 예상 끝과 맞는지 결정할 수 없으므로 데이터 손실을 나타낼 수 있다. 실패의 세부 사항은 Region Server 로그 파일을 확인해야 한다.
복제 설정 옵션 (Replication Configuration Options)
| Option | Description | Default |
|---|---|---|
| zookeeper.znode.parent | HBase용으로 사용되는 기본 ZooKeeper znode 이름 | /hbase |
| zookeeper.znode.replication | 복제용으로 사용되는 기본 znode 이름 | replication |
| zookeeper.znode.replication.peers | 피어 znode 이름 | peers |
| zookeeper.znode.replication.peers.state | 피어-상태 znode 이름 | peer-state |
| zookeeper.znode.replication.rs | rs znode 이름 | rs |
| replication.sleep.before.failover | worker가 죽은 region server의 WAL 큐 복제를 시도하기 전에 잠을 재워야 하는 밀리초 | |
| replication.executor.workers | 주어진 region server가 동시에 페일오버하려 시도해야 하는 region server 수 | 1 |
| hbase.replication.peer.storage.impl | 복제 피어 스토리지 구현 | zookeeper |
| hbase.replication.peers.directory | 파일시스템 복제 피어 스토리지가 지정될 때 복제 피어 상태를 저장하기 위한 디렉터리 | peers |
| hbase.replication.queue.table.name | 복제 큐 상태를 저장하는 테이블 | hbase:replication |
| hbase.replication.queue.storage.impl | 복제 큐 스토리지 구현 | TableReplicationQueueStorage |
복제 상태 모니터링 (Monitoring Replication Status)
HBase Shell 명령 status 'replication'으로 클러스터의 복제 상태를 모니터링할 수 있다. 명령에는 세 가지 변형이 있다.
status 'replication'— 각 소스와 그 싱크의 상태를 호스트명으로 정렬해 출력.status 'replication', 'source'— 각 복제 소스의 상태를 호스트명으로 정렬해 출력.status 'replication', 'sink'— 각 복제 싱크의 상태를 호스트명으로 정렬해 출력.
출력 이해 (Understanding the output)
명령 출력은 복제 상태에 따라 달라진다. 예를 들어 재시작 직후이고 대상 피어에 연결할 수 없으면 복제 소스 스레드가 실행되지 않으므로 메트릭이 표시되지 않는다.
hbase01.home: SOURCE: PeerID=1 Normal Queue: 1 No Reader/Shipper threads runnning yet. SINK: TimeStampStarted=1591985197350, Waiting for OPs...
정상적인 상황에서 건강한 active-active 복제 배포는 다음을 보여준다.
hbase01.home: SOURCE: PeerID=1 Normal Queue: 1 AgeOfLastShippedOp=0, TimeStampOfLastShippedOp=Fri Jun 12 18:49:23 BST 2020, SizeOfLogQueue=1, EditsReadFromLogQueue=1, OpsShippedToTarget=1, TimeStampOfNextToReplicate=Fri Jun 12 18:49:23 BST 2020, Replication Lag=0 SINK: TimeStampStarted=1591983663458, AgeOfLastAppliedOp=0, TimeStampsOfLastAppliedOp=Fri Jun 12 18:57:18 BST 2020
이 각 메트릭의 정의는 아래에 상세히 있다.
| Type | Metric Name | Description |
|---|---|---|
| Source | AgeOfLastShippedOp | 마지막으로 성공적으로 운반된 편집이 대상에 실제 복제되는 데 걸린 시간 |
| Source | TimeStampOfLastShippedOp | 마지막 성공 편집 운반의 실제 날짜 |
| Source | SizeOfLogQueue | 이 주어진 큐의 wal 파일 수 |
| Source | EditsReadFromLogQueue | 이 소스 스레드가 시작된 이후 이 주어진 큐에서 읽은 편집 수 |
| Source | OpsShippedToTarget | 이 소스 스레드가 시작된 이후 대상으로 운반된 편집 수 |
| Source | TimeStampOfNextToReplicate | 복제하려 시도 중인 현재 편집의 날짜 |
| Source | Replication Lag | 이 소스 스레드가 마지막 복제할 편집을 읽은 이후 경과 시간(밀리초)으로, 대상에 실제 복제됨 |
| Sink | TimeStampStarted | 이 Sink 스레드가 시작된 날짜(밀리초) |
| Sink | AgeOfLastAppliedOp | 마지막 성공적으로 운반된 편집을 적용하는 데 걸린 시간 |
| Sink | TimeStampsOfLastAppliedOp | 마지막 성공적으로 적용된 편집의 날짜 |
Source.TimeStampsOfLastAppliedOp 및/또는 Source.Replication Lag의 증가 값은 복제 지연을 나타낸다. Source.TimeStampOfLastShippedOp, Source.EditsReadFromLogQueue, Source.OpsShippedToTarget 또는 Source.TimeStampOfNextToReplicate는 전혀 변하지 않는데 그 숫자들이 계속 올라가면, 복제 흐름이 진행에 실패하고 있으며 클러스터 간 통신에 문제가 있을 수 있다. 이는 복제가 수동으로 일시 중지되었을 때(예: hbase shell disable_peer 명령으로)도 발생할 수 있지만, 데이터는 계속 소스 클러스터 테이블에 수집된다.
복제 관찰성 프레임워크 (Replication Observability Framework)
핵심 아이디어는 주기적으로 replication marker rows를 만들어 WAL에 삽입하는 것이다. 이 마커 행은 복제 지연/버그를 발생한 region server, WAL 및 타임스탬프까지 추적하는 데 도움을 준다. 이 트래커 행의 WAL 항목은 일반 테이블 WAL 항목과 인터리브되어 사용자 테이블이 보고 있는 것과 같은 복제 지연/버그에 부딪힐 확률이 매우 높다. 자세한 내용은 다음과 같다.
REPLICATION.WALEVENTTRACKER 테이블
REPLICATION.WALEVENTTRACKER라는 새 테이블을 만들고 모든 WAL 이벤트(ACTIVE, ROLLING, ROLLED 같은)를 이 테이블에 영속화한다.
이 테이블의 속성: Replication은 0으로, Block Cache는 비활성화, Max versions는 1, TTL은 1년이다.
이 테이블에는 단일 ColumnFamily: info가 있다.
info는 여러 퀄리파이어를 포함한다.
info:region_server_nameinfo:wal_nameinfo:timestampinfo:wal_stateinfo:wal_length
WAL을 롤링할 때마다(old-wal-name → new-wal-name) 이 테이블에 3개의 행을 만든다.
<region_server_name>, <old-wal-name>, <current timestamp>, <ROLLING>, <length of old-wal-name>
<region_server_name>, <old-wal-name>, <current timestamp>, <ROLLED>, <length of old-wal-name>
<region_server_name>, <new-wal-name>, <current timestamp>, <ACTIVE>, 0
구성 (Configuration)
WAL 이벤트 영속화를 활성화하려면 구성 속성이 있다: hbase.regionserver.wal.event.tracker.enabled(기본 false)
REPLICATION.SINK_TRACKER 테이블
REPLICATION.SINK_TRACKER라는 새 테이블을 만든다.
이 테이블의 속성: Replication은 0으로, Block Cache는 비활성화, Max versions는 1, TTL은 1년이다.
이 테이블에는 단일 ColumnFamily: info가 있다.
info는 여러 퀄리파이어를 포함한다.
info:region_server_nameinfo:wal_nameinfo:timestampinfo:offset
구성 (Configuration)
위 테이블을 만들려면 구성 속성이 있다: hbase.regionserver.replication.sink.tracker.enabled(기본 false)
ReplicationMarker Chore
우리는 ReplicationMarkerChore라는 새 chore를 도입했는데, 주기적으로 활성 WAL에 마커 행을 만든다. 마커 행은 region_server_name, wal_name, timestamp and offset within WAL 메타데이터를 가진다. 이 마커들은(특수 처리를 통해) 복제되고 싱크 측 테이블 REPLICATION.SINK_TRACKER에 영속화된다.
구성 (Configuration):
ReplicationMarkerChore는 구성 속성 hbase.regionserver.replication.marker.enabled(기본 false)로 활성화되고, 마커 행을 만드는 주기는 hbase.regionserver.replication.marker.chore.duration(기본 30초)으로 제어된다. 싱크 클러스터는 이 마커 행을 처리해 REPLICATION.SINK_TRACKER 테이블에 영속화하거나, 이 행들을 무시할 수 있다. 이 동작은 구성 속성 hbase.regionserver.replication.sink.tracker.enabled(기본 false)로 제어된다. false로 설정하면 마커 행을 무시한다.
엔드투엔드 기능 활성화 방법 (How to enable end-to-end feature ?)
이 전체 기능을 사용하려면 위 구성 속성들을 2단계/릴리스로 활성화해야 한다.
첫 단계/릴리스에서 다음 구성 속성들을 true로 설정한다.
hbase.regionserver.wal.event.tracker.enabled: 모든 WAL 이벤트를 REPLICATION.WALEVENTTRACKER 테이블에 영속화만 한다.hbase.regionserver.replication.sink.tracker.enabled: REPLICATION.SINK_TRACKER 테이블을 만들고 소스 클러스터에서 오는 특수 마커 행을 처리한다.
두 번째 단계/릴리스에서 다음 구성 속성을 true로 설정한다.
hbase.regionserver.replication.marker.enabled: 주기적으로 마커 행을 만들고 싱크 클러스터가 이 마커 행을REPLICATION.SINK_TRACKER테이블에 영속화한다.