읽기 복제본 클러스터

읽기 복제본 클러스터 (Read Replica Cluster)

이 문서는 HBase의 읽기 복제본 클러스터 기능을 설명해요. 활성 읽기-쓰기 클러스터와 같은 공유 스토리지(hbase.rootdir)에 대해 전역 읽기 전용 모드로 실행되는 별도의 HBase 클러스터를 만들 수 있어요. 데이터를 복사하지 않고 읽기 워크로드를 여러 클러스터로 분산하고 싶을 때 유용해요.

출처: 문서

본문

배경

*읽기 복제본 클러스터(Read Replica Cluster)*는 활성 읽기-쓰기 클러스터와 같은 공유 스토리지(hbase.rootdir)에 대해 전역 읽기 전용 모드로 실행되는 전체 HBase 클러스터예요. 두 클러스터 모두 같은 HDFS/클라우드 객체 저장소 위치에 같은 HFiles를 나열해요. 데이터는 복사되지 않아요. 읽기는 어느 클러스터에서든 제공될 수 있어서 읽기 워크로드를 스토리지 비용을 두 배로 늘리지 않고 여러 클러스터로 분산할 수 있어요.

일반적인 사용 사례:

  • 무거운 스캔/분석 워크로드를 기본 클러스터에서 분산.
  • 단일 공유 버킷으로 뒷받침되는 크로스 가용 영역 읽기 용량 추가.
  • 데이터를 복사하지 않고 읽기 중심 실험을 위한 격리된 클러스터 구축.

최종 일관성(Eventual consistency). 복제본은 (a) 활성 클러스터가 데이터를 공유 스토리지의 HFiles로 flush하고, (b) 복제본이 refresh_meta와 refresh_hfiles 명령으로 공유 스토리지를 다시 읽도록 지시받았을 때만 데이터를 볼 수 있어요. 활성 클러스터의 MemStore 데이터는 flush되기 전까지 복제본에 보이지 않아요.

부모 설계는 HBASE-29081에 있어요.

설계

이 기능은 세 부분으로 구성돼요.

클러스터별 사용자 정의 hbase:meta

hbase.rootdir를 공유하는 모든 클러스터는 자체 hbase:meta와 자체 master 로컬 region 디렉터리가 필요해요. region 할당과 master-로컬 상태는 노드 범위로 지정되어 공유할 수 없기 때문이에요. 다른 시스템 테이블(hbase:acl, hbase:replication)은 내용이 스토리지 전체에 걸쳐 있고 복제본이 절대 쓰지 않으므로 공유해도 안전해요.

구성 키 hbase.meta.table.suffix는 클러스터별 접미사를 선택해요. 메타 테이블은 hbase:meta_<suffix>가 되고 master의 로컬 저장 디렉터리는 MasterData_<suffix>가 돼요. 같은 hbase.rootdir를 공유하는 각 클러스터는 자체 hbase:meta와 MasterData 디렉터리가 다른 클러스터와 충돌하지 않도록 별개의 접미사로 구성되어야 해요. 접미사는 [a-zA-Z0-9]+와 일치해야 해요.

전역 읽기 전용 모드

hbase.global.readonly.enabled=true는 클러스터를 읽기 전용 모드로 넣어요. org.apache.hadoop.hbase.security.access 아래의 다섯 개 coprocessor 컨트롤러가 모든 사용자 테이블 변형 경로를 가로채고 Operation not allowed in Read-Only Mode 메시지와 함께 WriteAttemptedOnReadOnlyClusterException(DoNotRetryIOException)을 던져요.

Class Coprocessor host Blocks
MasterReadOnlyController Master DDL, snapshots, splits, merges, namespace ops, ACL/quota ops, replication-peer ops
RegionServerReadOnlyController RegionServer WAL roll, replication sink mutations, log replay
RegionReadOnlyController Region put, delete, batchMutate, checkAnd*, append, increment, flush, compaction, WAL append, commit/replay
BulkLoadReadOnlyController Region bulk-load prepare/cleanup
EndpointReadOnlyController Region all coprocessor endpoint invocations

운영자는 이 클래스들을 수동으로 로드하지 않아요. CoprocessorConfigurationUtil.syncReadOnlyConfigurations가 시작 시와 모든 동적 ConfigurationManager.notifyAllObservers 이벤트에서 이들을 hbase.coprocessor.master.classes, hbase.coprocessor.regionserver.classes, hbase.coprocessor.region.classes에 추가해요. 그래서 이 플래그는 update_all_config로 런타임에 뒤집을 수 있어요(Case 3 참고).

여러 활성 클러스터 방지 (active.cluster.suffix.id)

같은 hbase.rootdir에 쓰는 두 클러스터는 공유 스토리지를 손상시킬 거예요. 단일 작성자를 강제하기 위해, 활성 master는 자신의 cluster ID와 meta 접미사를 기록하는 protobuf 직렬화 센티널을 <hbase.rootdir>/active.cluster.suffix.id에 만들어요. MasterFileSystem.negotiateActiveClusterSuffixFile가 master 시작 시 실행돼요:

  • 활성 클러스터(hbase.global.readonly.enabled=false)는 파일이 없으면 만들고, 아니면 내용이 자신의 정체성과 일치하는지 확인해요. 파일이 다른 클러스터 소유라면 IOException과 함께 시작이 중단돼요.
  • 복제본 클러스터(hbase.global.readonly.enabled=true)는 파일을 읽거나 쓰지 않아요. [Read-replica feature] Replica cluster is being started in Read Only Mode를 로그하고 계속해요.

AbstractReadOnlyController.manageActiveClusterIdFile는 동적 토글을 처리해요. 읽기 전용으로 전환하면 이 클러스터가 파일을 소유한 경우 삭제하고, 읽기-쓰기로 되돌리면 파일이 없으면 만들어요.

구성

읽기 복제본 클러스터의 모든 노드에서 hbase-site.xml에 다음을 추가하세요.

<property>
  <name>hbase.global.readonly.enabled</name>
  <value>true</value>
  <description>
    Put this cluster into global read-only mode. All user-table writes, flushes,
    compactions, splits, and merges are blocked. The five ReadOnly coprocessor
    controllers are loaded automatically.
  </description>
</property>
<property>
  <name>hbase.meta.table.suffix</name>
  <value>replica1</value>
  <description>
    Optional. If set, the meta table is named hbase:meta_<suffix> and the
    master's local store directory is MasterData_<suffix>. Value must match
    [a-zA-Z0-9]+. Each cluster sharing the same hbase.rootdir MUST be
    configured with a distinct suffix so its hbase:meta and MasterData
    directory do not collide with any other cluster's.
  </description>
</property>

활성 클러스터는 같은 hbase.rootdir를 사용하지만 자체 hbase.meta.table.suffix(모든 복제본의 접미사와 별개)를 사용하고, hbase.global.readonly.enabled는 설정하지 않거나 false로 둬요.

hbase.global.readonly.enabled는 동적 구성이에요. 구성 변경 이벤트가 프로세스를 재시작하지 않고 읽기 전용 coprocessor를 다시 로드해요. 모든 노드가 값에 동의해야 해요. 운영자는 update_all_config를 발행하기 전에 모든 hbase-site.xml을 동기화할 책임이 있어요.

운영과 유지보수

사례 1. 새 읽기 복제본 클러스터 기동

  1. 활성 클러스터의 hbase.rootdir(일반적으로 같은 HDFS 또는 객체 저장소)에 도달할 수 있는 하드웨어에 복제본 클러스터를 프로비저닝해요.
  2. 복제본의 hbase-site.xml에 hbase.global.readonly.enabled=true를 설정해요. 그리고 hbase.meta.table.suffix를 설정해 공유 스토리지에서 복제본 클러스터의 메타 테이블을 구분해요.
  3. 클러스터를 시작하고 Master 로그에 [Read-replica feature] Replica cluster is being started in Read Only Mode가 표시되는지 확인해요.
  4. 복제본 셸에서 refresh_meta를 실행한 다음 refresh_hfiles를 실행해 활성 클러스터의 현재 상태를 복제본에 실현해요.

사례 2. 활성 클러스터에서 쓰기 후 일상적인 동기화

# On the active cluster
hbase> flush 'my_namespace:my_table'
# On the read replica cluster
hbase> refresh_meta
hbase> refresh_hfiles 'TABLE_NAME' => 'my_namespace:my_table'

항상 refresh_meta를 먼저 실행하고 그다음 refresh_hfiles를 실행하세요. refresh_hfiles는 복제본에서 열려 있는 region만 새로 고치므로, 새로 발견된 region은 HFiles를 가져오기 전에 먼저 meta에 있어야(그리고 할당되어야) 해요. refresh_hfiles는 세 가지 범위를 지원해요.

hbase> refresh_hfiles                              # all user tables
hbase> refresh_hfiles 'TABLE_NAME' => 'ns:table'   # one table
hbase> refresh_hfiles 'NAMESPACE'  => 'ns'         # one namespace

refresh_hfiles에 TABLE_NAME과 NAMESPACE를 모두 전달하는 것은 거부돼요. 두 명령 모두 master UI나 Admin.getProcedures()로 추적할 수 있는 procedure ID를 반환해요.

복제본의 block cache가 방금 새로 고쳐진 테이블에 대해 오래된 엔트리를 보관하고 있다면 기존 clear_block_cache 'my_namespace:my_table' 셸 명령으로 축출해요.

Admin과 AsyncAdmin은 프로그래밍 방식으로도 같은 연산을 노출해요.

long pid;
pid = admin.refreshMeta();
pid = admin.refreshHFiles();                              // all user tables
pid = admin.refreshHFiles(TableName.valueOf("ns:table")); // one table
pid = admin.refreshHFiles("ns");                          // one namespace

사례 3. 읽기 전용 모드 동적 토글

hbase.global.readonly.enabled는 재시작 없이 변경할 수 있어요. hbase-site.xml을 편집한 다음 구성 새로 고침(예: 셸에서 update_all_config)을 트리거해요. ConfigurationManager는 관찰자에게 알리고, 관찰자는 읽기 전용 coprocessor를 로드/언로드하고 AbstractReadOnlyController.manageActiveClusterIdFile을 호출해요.

  • false → true (복제본이 됨): active.cluster.suffix.id 파일은 내용이 이 클러스터와 일치하는 경우에만 삭제돼요. 다른 클러스터가 파일을 소유하면 그대로 둬요.
  • true → false (활성 클러스터가 됨): 파일이 이미 존재하지 않는 한 이 클러스터의 정체성으로 파일이 재생성돼요.

진행 중인 배치 연산은 중단되지 않아요. 토글 이후에 제출된 쓰기 연산은 WriteAttemptedOnReadOnlyClusterException을 던져요. 호출자는 이를 처리하고(원한다면) 실패한 변형을 다시 제출할 책임이 있어요.

사례 4. 활성 클러스터가 없어졌을 때 복제본 승격

  1. 원래 활성 클러스터가 완전히 내려갔는지 확인해요.
  2. 이전 활성 클러스터의 오래된 active.cluster.suffix.id가 여전히 있으면 수동으로 제거해요(예: hdfs dfs -rm <hbase.rootdir>/active.cluster.suffix.id 또는 객체 저장소에 해당하는 CLI). 외부 센티널 파일이 있는 동안 새 활성 master는 시작을 거부할 거예요.
  3. 복제본에서 hbase.global.readonly.enabled=false를 설정하고 변경을 적용해요(동적 업데이트 또는 재시작). master가 이 클러스터의 정체성으로 새 센티널 파일을 써요.

사례 5. 차단된 읽기 전용 전환에서 복구

다른 클러스터의 active.cluster.suffix.id 파일이 여전히 있는 동안 복제본을 활성으로 승격하려 시도하면(hbase.global.readonly.enabled=false + update_all_config) 전환이 차단돼요. 셸은 ReadOnlyTransitionException을 반환하고 클러스터는 읽기 전용 모드로 남아요. 쓰기는 계속 WriteAttemptedOnReadOnlyClusterException으로 거부돼요.

서버 ERROR 로그에는 외부 클러스터의 ID가 포함돼요.

Cannot disable read-only mode. The active.cluster.suffix.id file contains a different
cluster ID (<foreign-id>), which means that cluster is already the active cluster.
Reverting hbase.global.readonly.enabled to true.

승격이 차단된 클러스터는 여전히 복제본이에요. 읽기 전용 coprocessor가 로드된 채 남아 있고 쓰기는 여전히 거부돼요. hbase-site.xml에 hbase.global.readonly.enabled=false가 있어도 마찬가지예요.

복구하려면 기존 활성 클러스터가 아직 실행 중인지에 따라 다음 경로 중 하나를 선택하세요.

경로 A — 활성 클러스터가 아직 실행 중인 경우:

  1. 현재 활성 클러스터에서 hbase-site.xml에 hbase.global.readonly.enabled=true를 설정하고 update_all_config를 실행해요. 이렇게 하면 복제본으로 변환되고 active.cluster.suffix.id 파일이 삭제돼요.
  2. 승격하려는 클러스터에서 update_all_config를 다시 실행해요. 이제 전환이 성공할 거예요. 클러스터가 자신의 정체성으로 새 센티널 파일을 쓰고 읽기 전용 coprocessor를 언로드해요.

경로 B — 활성 클러스터가 다운되었거나 이미 복제본으로 변환된 경우:

  1. 센티널 파일을 소유한 클러스터가 완전히 중지되었거나 이미 복제본으로 변환됐는지 확인해요.

  2. 외부 센티널 파일을 제거해요:

    hdfs dfs -rm <hbase.rootdir>/active.cluster.suffix.id
    
  3. 승격하려는 클러스터에서 update_all_config를 다시 실행해요. 이제 전환이 성공할 거예요. 클러스터가 자신의 정체성으로 새 센티널 파일을 쓰고 읽기 전용 coprocessor를 언로드해요.

센티널 파일을 쓴 클러스터가 여전히 활성 클러스터로 실행 중인 동안에는 파일을 제거하지 마세요. 그렇게 하면 두 클러스터가 동시에 같은 공유 스토리지에 쓸 수 있어 데이터 손상 위험이 있어요.

구성과 명령

새 구성

Config Default Description
hbase.meta.table.suffix "" 메타 테이블 이름에 접미사를 추가해요. value='test'는 hbase:meta_test 테이블 이름을 만들어요.
hbase.global.readonly.enabled false 전체 클러스터를 읽기 전용 모드로 넣어요.

새 명령

Command Usage Description
refresh_hfiles refresh_hfiles refresh_hfiles 'TABLE_NAME' => 'tablename' refresh_hfiles 'TABLE_NAME' => 'namespace:test_table' refresh_hfiles 'NAMESPACE' => 'namespace' 디스크에서 HFiles를 새로 고쳐요. 읽기 복제본에서 새 편집을 가져오는 데 사용해요.
refresh_meta refresh_meta 메타 테이블을 뒷받침 스토리지와 동기화해요. 새 테이블과 region을 가져오는 데 사용해요.

더 알아보기 (Learn more)