[발 번역] HBase Replication: Operational Overview

해당 블로그는 KT UCloud의 지원을 받고 있습니다.

해당 글은 http://www.cloudera.com/blog/2012/08/hbase-replication-operational-overview/ 글을 발 번역한 것입니다. 이전에 번역한 HBase Replication Overview 가 전체적인 동작에 대한 간단한 설명이었다면, 이번 글은 실제로 Operational-Overview로 간단하게 어떻게 동작하는지에 대해서 보여주는 글입니다. 과연 실제로 어떻게 사용하는지 살펴보면 재미있을 듯 합니다. 클라우드 서비스를 이용하다보면, 결국 클라우드 서비스 자체도 장애가 날 수 있습니다. 그럴 경우,  클라우드 서비스간에 서비스를 이전하기 위해서 가장 중요한 것은 데이터 스토어입니다. 결국 클라우드간 Replication까지도 생각을 해두셔야 할 것 같습니다.

HBase Replication: Operational Overview

 해당 글은 HBase Replication 에 대한 두 번째 글 입니다. 이전 글 HBase Replication Overview 에서 유즈케이스나, 아키텍처, HBase Replication의 다양한 모드 지원에 대해서 알아보았습니다. 이번 글은 운영적인 관점에서 HBase Replication 을 어떻게 설정하고, 그것에 연관된 중요한 개념들, 부트스트래핑, 스키마 변경, 장애 안정성 등에 대해서 이야기하도록 하겠습니다.

Configuration

HBase Replication Overview에서 이미 언급했듯이, 마스터 클러스터는 WALEdit 들을 하나 이상의 슬레이브 클러스터로 전달합니다. 이번 섹션에서는 마스터-슬레이브 모드에서 리플리케이션을 설정하는 방법에 대해서 이야기하겠습니다.

  1. 리플리케이션되어야 할 모든 테이블과 컬럼 패밀리가 양 쪽 클러스터에 반드시 존재해야 합니다.
  2. 양 쪽 클러스터의 $HBASE_HOME/conf/hbase-site.xml 에 있는 다음 설정이 true로 셋팅되어야 합니다.
<property>
<name>hbase.replication</name>
<value>true</value>
</property>

 마스터 클러스터에서는 다음과 같은 추가 작업이 필요합니다.

  1. 리플리케이션을 원하는 테이블/컬럼 패밀리에 리플리케이션 범위를( REPLICATION_SCOPE  속성) 지정해야 합니다.
hbase shell> disable ‘table’
hbase shell> alter ‘table’, {NAME => ‘column-family’, REPLICATION_SCOPE => 1}
hbase shell> enable ‘table’

 REPLICATION_SCOPE 는 컬럼 패밀리 레벨의 속성이며 해당 값은 0이나 1이 될 수 있습니다. 0의 의미는 리플리케이션을 사용하지 않는 다는 뜻이고, 1은 리플리케이션을 사용하겠다는 뜻입니다.  위 예에서 보여준 것과 같이 사용자가 리플리케이션하기를 원하는 모든 컬럼 패밀리에서 각각 alter 명령어를 사용하여 변경해야 합니다.

 만약 사용자가 테이블을 생성하면서 바로 리플리케이션을 하기를 원하면 다음과 같은 과정을 따르면 됩니다.

hbase shell> create ‘table’, ‘column-family1’, ‘‘column-family2’, {NAME => ‘column-family1’, REPLICATION_SCOPE => 1}

 위의 명령은 ‘column-family1’ 이라는 위의 테이블에 대해서 리플리케이션을 사용하도록 설정할 것입니다.

       2. HBase Shell에서, Slave 노드를 추가합니다. 사용자는 슬레이브 클러스터의 zookeeper quorum 정보와, zookeeper의 클라이언트 port, 그리고 root hbase znode 를 peerId와 함께 제공해야 합니다.

hbase shell>add_peer 'peerId', "<zookeeper_quorum>:<zookeeper_client_port>:<root_hbase_znode>"

 peerId 는 한 글자나 두 글자의 long 스트링입니다. 그리고 응답하는 znode 는 peers znode 아래에 생성됩니다. 이전 블로그에서 설명한 것 처럼, 사용자가 add_peer 명령을 실행시키면, 리플리케이션 코드는 해당 peer를 위한 ReplicationSource를 인스턴스화합니다. 그리고 모든 마스터 클러스터의 region 서버들이 슬레이브 클러스터의 Region 서버에 접속하려고 시도합니다.  또한, 슬레이브 클러스터의 ClusterId (UUID, 슬레이브 클러스터의 zookeeper quorum에 등록되어 있습니다.) 도 가져옵니다.

 마스터 클러스터의 region 서버는 슬레이브 클러스터의 이용가능한 region 서버 목록을 “/hbase/rs” znode를 읽음으로써 가져옵니다. 슬레이브 클러스터의 zookeeper quorum에서 해당 노드의 자식 노드에 등록된 서버들과 연결을 맺습니다. 마스터 클러스터의 각 Region 서버들은 “replication.source.ratio” 에 정의된 값에 따라서, 슬레이브 Region 서버들의 일부를 선택합니다. 기본 값은 0.1 입니다. 익서은 각 마스터 클러스터의 Region 서버들이 슬레이브 클러스터의 Region 서버들 전체의 10%에 대해서 연결을 시도할 거라는 것을 의미합니다.

  배치 트랜잭션 작업을 보내기 위해서, 마스터 클러스터의 Region 서버는 연결된 Region 서버들 중에 랜덤으로 서버를 하나 선택할 것입니다.( 리플리케이션은 catalog 테이블에 대해서는 수행되지 않습니다. .META 와 _ROOT_ )

마스터-마스터 모드로 설정하기 위해서는, 양쪽의 클러스터에서 위의 작업을 모두 수행하면 됩니다.

Schema Change

이전 섹션에서 언급했듯이, 리플리케이션 되는 테이블과 컬럼 패밀리는 반드시 양쪽 클러스터에 모두 존재해야 합니다. 이번 섹션에서는 리플리케이션이 진행되고 있는 중에 스키마를 변경하는 동안에 발생할 수 있는 여러가지 가능한 시나리오에 대해서 논의하려고 합니다.

a) 마스터에서 컬럼 패밀리를 삭제하는 경우:  컬럼 패밀리의 삭제는 해당 컬럼 패밀리에 대해서 어떠한 아직 처리 되지 않은 변경사항에 대한 리플리케이션에 대해서 영향을 주지 않습니다. 이것은 리플리케이션 코드가 WAL을 읽고 각각의 WALEdit 가 컬럼 패밀리의 리플리케이션 범위에 있는지 체크하기 때문입니다. 각각의 WALEdit는 컬럼 패킬리가 리플리케이션 되는지에 대한 맵 정보를 가지고 있습니다. 리플리케이션 범위안에 들어가는지 아닌지에 대해서 모든 Key/Value 의 컬럼 패밀리에 대해서 체크를 합니다.  만약 맵안에 존재하면, 리플리케이션을 위해서 준비하게 됩니다. 해당 컬럼 패밀리가 삭제 되기 전에 해당 WALEdit 오브젝트가 생성되었다면, 해당 리플리케이션은 아무런 영향을 받지 않습니다.

b) 슬레이브에서 컬럼 패밀리를 삭제하는 경우: WALEdit 들이 마스터 클러스터에서 특정한 슬레이브 클러스터의 Region 서버로 전송될 때, 정상적인 HBase client와 같이 처리됩니다.( HTablePool 오브젝트를 이용합니다. ) 해당 컬럼 패밀리가 삭제되었기 때문에, put 명령은 실패할 것이고 마스터 클러스터의 Region 서버에 “예외”가 전달될 것입니다.

Start/Stop Replication

시작/종료 명령은 “Kill Switch” 처럼 동작합니다. stop_replication 명령을 HBase Shell에서 수행할 때, /hbase/replication/state 의 값을 false로 바꿀 것이고, 이로 인해 모든 Replication Source 오브젝트들이 log를 읽는 것을 멈추게 합니다. 그러나 이미 읽어둔 엔트리들은 전송이 될 것입니다. 사용자가 stop_replication 커맨드를 사용하면, 새롭게 변경되는 log들은 리플리케이션을 위해 큐에 저장되지 않을 것입니다. 비슷하게, start_replication 명령이 실행되면, 명령이 실행된 시간 부터가 아니라 현재 WAL 부터 리플리케이션을 시작할 것입니다.( 몇몇 이전 트랜잭션을 포함하는 )

 

Figure 1 explains the start-stop switch behavior, where the sequence of events flows in the direction of arrows.

Version Compatibility

 마스터 클러스터 Region 서버들은 슬레이브 클러스터 Region 서버에 정상적인 HBase Client와 같은 형태로 접속합니다. 그래서 HBase 클라이언트 버전 xxx 가 HBase 서버 버전 yyy에 접속하는 것과 동일한 룰이 적용이 됩니다.

 다른 점에서, 리플리케이션이 계속 진화하는 중이므로( 계속 많은 기능들이 지속적으로 추가되고 있습니다. ) 사용자가 현재 기능에 대해서 잘 알고 있어야 합니다. 예를 들어, CDH4 는 HBase 0.92 버전을 기반으로 하고 있습니다. 마스터/마스터 그리고 cyclic 리플리케이션, peer level에서의 리플리케이션 소스 사용 여부는 HBase 0.94에 추가되어 있습니다.

Boot-strapping

 리플리케이션은 마스터 클러스터 Region 서버들의 WAL 을 읽음으로써 동작합니다. 사용자가 이전 데이터를 리플리케이션 하기를 원한다면, 리플리케이션이 되는 동안에 copyTable 명령을 ( 시작/끝 타임스탬프를 제공함으로써 ) 실행할 수 있습니다. copyTable 명령은 시작/끝 타임스탬프의 범위 내의 데이터를 모두 복사할 것이며, 리플리케이션에서 해당 데이터를 관리할 것입니다. 전체적인 것을 요약하면 다음과 같습니다.

  1. 리플리케이션을 시작합니다.( 타임스탬프를 기억합니다. )
  2. copyTable 명령을 종료 타임스탬프를 위에서 기억한 타임스탬프와 동일한 값을 이용하여 실행시킵니다.
  3. 리플리케이션이 현재의 WAL 부터 시작되기 때문에, 슬레이브에는 아마도 copyTable 과 리플리케이션에 의해서 key/value 데이터가 복사되고 있을것입니다. 그러나 멱등한 오퍼레이션이기 때문에 문제가 없습니다. (역자 주: 즉, 리플리케이션은 현재 시점부터 시작이 되는 것이므로 그 이전 작업은 copyTable로 복사해야 한다는 의미입니다.)

 마스터/마스터 리플리케이션의 경우, 리플리케이션을 시작하기 전에 copyTable 을 수행해야 합니다. 리플리케션 수행 중에 copyTable을 사용자가 수행하면, 두번째 마스터는 첫번째 마스터로 데이터를 다시 전송할 것입니다. 왜냐하면 copyTable 작업은 해당 edit의 ClusterId를 변경하지 않기 때문입니다. 전체 과정을 요약하면 다음과 같습니다.

  1. copyTable 을 수행합니다.( job의 시작 타임스탬프를 기억해야합니다.)
  2. 리플리케이션을 시작합니다.
  3. 시작 타임스탬프를 1단계에서 저장한 시작시간으로 해서  copyTable을 다시 수행합니다.

 이것 역시 약간의 데이터가 두 클러스터 사이에서 중복으로 전달되지만 그 양을 최소화 할 수 있습니다.(역자 주: 이전 데이터를 copyTable로 복사하고, 리플리케이션 사이에 시작되는 시간 사이의 데이터만 중복으로 전달되지만, 그 양이 적을 것입니다. 그리고 리플리케이션 연산은 멱등하므로 중복된다고 해서 문제가 될 것은 없습니다.)

Fault Tolerance

Master Cluster Region Server Failover

 모든 마스터 클러스터의 Region 서버들은 “/hbase/replication/rs” 밑에 znode를 생성합니다. 아키텍처 섹션에서 언급했듯이, Region 서버는 각 WAL을 위한 자식 znode를 추가하고 byte-offset 을 저장합니다. Figure.1 에서 보여주듯이, Region 서버가 장애가 나면, 다른 Region 서버는 장애난 Region 서버의 해당znode 아래에 있는 로그들을 처리할 필요가 있습니다. 모든 Region 서버들은 다른 Region 서버의 znode를 감시하고 있습니다.(“/hbase/rs”) , 그래서 하나의 Region 서버가 장애가 나면, 다른 Region 서버들은 마스터가 해당 Region 서버가 장애라고 표시했다는 이벤트를 받게 됩니다. 이 경우에, 모든 다른 Region 서버들은 장애난 Region 서버의 znode 에서 자신의 znode로 WAL들을 옮기기 위해서 경쟁하게 됩니다. 그리고 다른 정상 log와 구별하기 위해서,  slave id 와 장애난 Region 서버의 이름을 prefix로 추가합니다.  별도의 replication source(NodeFailoverWorker instance) 는 처리하다가  장애난 전송 log 들을 위해서 인스턴스화 됩니다.

 HBase Replication Overview 의 Figure 1 을 replication znode들의 계층의 기본으로 생각한다면, 해당 포스트의 Figure 2는 foo1.bar.com 이 장애가 나서 foo2.bar.com이 foo1의 큐를 대신 처리하는 새로운 replication znode들의 계층을 보여줍니다.  새로운 znode “1-foo1.bar.com,40020,1339435481973” 이 foo2.bar.com znode 밑에 생성된 것을 기억하십시오.

If one consider Figure 1 of the HBase Replication Overview as the base figure of replication znodes hierarchy, Figure 2. shows the new replication znodes hierarchy in case server foo1.bar.com dies and foo2.bar.com takes over its queue. Note the new znode “1-foo1.bar.com,40020,1339435481973” which is created under foo2.bar.com znode


/hbase/hbaseid: b53f7ec6-ed8a-4227-b088-fd6552bd6a68 ….
/hbase/rs/foo2.bar.com,40020,1339435481973:
/hbase/rs/foo3.bar.com,40020,1339435486713: /hbase/replication:
/hbase/replication/state: true /hbase/replication/peers: /hbase/replication/peers/1: zk.quorum.slave:281:/hbase /hbase/replication/rs:
/hbase/replication/rs/foo1.bar.com.com,40020,1339435084846:
/hbase/replication/rs/foo1.bar.com,40020,1339435481973/1:
/hbase/replication/rs/foo1.bar.com,40020, 1339435481973/1/foo1.bar.com.1339435485769: 1243232 /hbase/replication/rs/foo3.bar.com,40020,1339435481742:
/hbase/replication/rs/foo3.bar.com,40020,1339435481742/1:
/hbase/replication/rs/foo3.bar.com,40020,
1339435481742/1/foo3.bar..com.1339435485769: 1243232
/hbase/replication/rs/foo2.bar.com,40020,1339435089550:
/hbase/replication/rs/foo2.bar.com,40020,1339435481742/1:
/hbase/replication/rs/foo2.bar.com,40020,
1339435481742/1/foo2.bar..com.13394354343443: 1909033
/hbase/replication/rs/foo2.bar.com,40020,1339435481742/1- foo1.bar.com,40020,1339435481973/foo1.bar.com.1339435485769: 1243232

Figure 2. Regionserver failover znodes hierarchy

 그 동안, log splitting은 장애난 Region 서버의 log를 보관하고 처리하기 시작할 것입니다. Replication source는 로그를 정규 디렉토리와 보관용 디렉토리에서 찾을 것입니다.

Slow/unresponsive slave cluster (or regionservers)

 슬레이브 클러스터가 다운되거나 일시적으로 네트웍이 단절되면, 슬레이브로 리플리케이션 되지 못한 로그는 HBase log cleaner 에 의해서 지워지지 않습니다.

 Log 삭제는 설정된 시간 마다 동작하는 LogCleaner class 에 의해서 수행되며, 리플리케이션 코드는 ReplicationLogCleaner 플러그인을 LogCleaner 클래스에 추가합니다. 마지막 특정 로그를 지우려고 시도할 때, ReplicaionLogCleaner 는 replication znode 아래에 로그가 존재하는 지 확인할 것입니다.( /hbase/replication/rs/ znode 아래의 ). 만약 로그가 발견되면, 해당 로그는 리플리케이션 중이라고 뜻이고, 해당 로그의 삭제는 진행하지 않습니다. 로그가 리플리케이션이 되면, 해당 로그의 znode 는 리플리케이션 znode 트리에서 삭제 될 것입니다. 그리고 다음 실행시에 LogCleaner 는 해당 로그가 이미 리플리케이션 되었으므로 성공적으로 삭제할 것입니다.

Verification

더 작은 양의 데이터라면 실제로 리플리케이션이 되는지 아닌지에 확인하기 위해서 슬레이브 클러스터에서 hbase shell을 이용하여 테이블 row를 간단하게 찾아볼 수 있습니다. 리플리케이션 동작 확인을 위한 일반적인 방법은 verifyrep 이라는 mapreduce 작업을 HBase에서 수행하는 것입니다. 해당 작업은 마스터 클러스터에서 수행이 되어야 하고 슬레이브 ClusterId와 확인할 테이블명이 필요합니다. 시작/종료 타임스탬프와 컬럼 패밀리 정보등의 추가적인 파라매터도 사용할 수 있습니다. GOODROWS와 BADROWS로 명명된 두 개의 카운터를 출력하는데, 리플리케이션 된 숫자와 되지 않은 숫자를 각각 의미합니다.

Future Work

 현재 버전의 HBase Replication 에 존재하는 모든 기능들은, 점차 더 개선될 것입니다. 마스터/슬레이브 간의 리플리케이션 시간이나 갭을 줄이는 등의 성능 개선과, Region 서버 장애에 대한 좀 더 안정적인 대응(HBase-2611) 등 다양합니다. 차후 개선의 범위에는 peer-level 테이블 리플리케이션의 활성화와 IncrementColumnValue(HBase-2804) 에 대한 적합한 핸들링이 포함되어 있습니다.

Conclusion
 해당 포스트는 다양한 모드에 대한 설정, 존재하는 클러스터의 부트스트래핑, 스키마 변경의 영향, 장애 안전성등을 포함한 운영 관점에서 HBase Replication 에 대해서 논의했습니다.