Zookeeper는 분산 애플리케이션을 위한 분산 오픈 소스 코디네이션 서비스이다. 파일 시스템의 친숙한 디렉토리 트리 구조와 유사한 데이터 모델을 사용한다.
코디네이션 서비스는 race condition 및 교착 상태와 같은 오류가 발생하기 쉬운데 주키퍼는 이를 쉽게 할 수 있게 책임을 덜어준다.
특징 중 하나는 다수의 머신에서 실행되며 고가용성을 보장하도록 설계되었다는 것이다.
Zookeeper
표준 파일 시스템과 유사하게 구성된 공유 계층 네임스페이스를 통해 분산 프로세스를 서로 조정할 수 있다. namespace는 ZNode라고 하는 데이터 레지스터로 구성되며 이는 파일 및 디렉토리와 유사하다. 저장용으로 설계된 일반 파일 시스템과 달리 주키퍼 데이터는 메모리에 보관되므로 주키퍼는 높은 처리량과 낮은 대기 시간을 달성할 수 있다.
주키퍼 구현은 고성능, 고가용성, strictly ordered access에 중점을 둔다.
주키퍼는 앙상블이라고 하는 호스트 집합을 통해 복제되도록 되어있다.
주키퍼 서비스를 구성하는 서버는 모두 동기화되어 있어야 한다. 영구 저장소의 트랜잭션 로그 및 스냅샷과 함께(아래 설명 있음) 메모리 내 상태 이미지를 유지 관리한다.
클라이언트는 단일 주키퍼 서버에 연결해서 요청을 보내고, 응답을 받고, 감시 이벤트를 받고, 하트비트를 보내는 TCP 연결을 유지 관리한다. TCP 연결이 끊어지면 클라이언트는 다른 서버에 연결한다.
주키퍼는 특히 읽기가 주로 발생하는 워크로드에서 속도가 빠르다.
ZNode
Zookeeper의 계층적 네임 스페이스. zookeeper의 name space에 있는 모든 노드는 경로로 식별된다.
znode는 주키퍼의 핵심으로 znode를 통해 주키퍼가 제공할 수 있는 글로벌 락, 동기화, 리더 채택, 설정 관리 등의 기능을 구현할 수 있다.
클라이언트 관점에서는 서버에 접속할 시 보게 되는 것이 znode 들이다. 주키퍼에 채택된 아키텍처와 기법들은 결국 znode로 구성된 데이터 모델을 안정적으로 제공하고자 하는 목적으로 만들어졌다고 해도 과언이 아니다.
주키퍼는 znode로 불리는 노드를 계층적 트리 형태로 관리하며 각 node는 데이터를 저장하고 ACL을 가진다.
코디네이션 서비스를 위해 설계되었으며 대용량의 데이터를 저장하는 용도로는 사용할 수 없다. 그러므로 znode에 저장할 수 있는 데이터의 크기는 1MB로 제한되어 있다.
데이터 접근은 원자성(성공 또는 실패)을 가진다. 클라이언트가 znode에 저장된 데이터를 읽을 때 데이터의 일부만 받을 수는 없으며 데이터 전체가 전달되지 않으면 읽기는 실패하게 된다. 쓰기도 znode와 관련된 모든 데이터를 갱신한다. 주키퍼는 쓰기를 성공과 실패로만 결정하기 때문에 클라이언트가 데이터의 일부만 변경하는 부분적 쓰기는 불가능하다.
주키퍼의 각 znode는 경로로 참조된다. 경로는 /로 구분된 유니코드 문자열로 유닉스의 파일시스템 경로와 같다. 절대 경로만 지원하기 때문에 반드시 루트를 뜻하는 슬래시 문자로 시작해야 한다.
(* 참고 : znode는 ACL-Access Control List-를 사용하여 각각의 znode에 접근권한을 설정할 수 있다. 각 znode의 ACL은 자기 자신에게만 허용되며 자식들에게 recursive하게 적용되지 않는다.)
예시)
get /kafka/brokers/topics/test_topic/partitions/0/state
# 결과
{"controller_epoch":100, "leader":10, "version":1, "leader_epoch":200, "isr":[10,11,12]}
get /kafka-service/brokers/ids/100
#결과
{"listener_security_protocol_map":{"PLAINTEXT":"PLAINTEXT"},
"endpoints":["PLAINTEXT://server1:9092"], "jmx_port":9xxx, "host":"server1",
"timestamp":"16.........", "port":9092, "version":4}
ZNode의 특징
1. hierarchy
ZNode는 파일 시스템과 유사하게 Node간에 hierarchy namespace를 가지며 /로 구분한다.
일반적인 파일 시스템과 다른 부분은 ZooKeeper는 file과 directory의 구분이 없고 znode 하나만 제공한다.
즉, 디렉토리와 파일 간의 구분이 없는 점이 파일 시스템과의 차이점이다.
이것은 ZNode의 큰 특징 중 하나로 namespace hierarchy를 가지기 때문에 관련 있는 일들을 하나의 묶음으로 관리할 수 있으며 file 간에 hierarchy를 가짐으로써 redundunt file을 생성하는 것을 막을 수 있다.
2. size 제한
Zookeeper는 모든 데이터를 메모리에 저장한다. 따라서 jvm의 모든 heap memory를 모든 ZNode에 올릴 수 있는 충분한 크기로 만들어야 한다. (파일 시스템이나 DBMS 같은 경우 모든 데이터가 올라갈 수 있도록 메모리 크기를 산정해야 하는 것은 말이 안된다.) 하지만 ZNode의 목적은 데이터를 저장하는 것이 아니라 분산처리된 시스템 간의 조정을 하기 위함이다.
따라서 ZNode에는 조정에 필요한 meta data만을 저장하는 것이 기본적인 사용법이며, ZNode 자체도 크기가 작은 Data를 저장할 것이라고 가정하고 구현되어 있기 때문에 ZNode의 크기는 1MB로 제한된다.
3. Recovery
Zookeeper 설정 파일에는 dataDir라는 설정이 있다. 모든 데이터는 메모리에 올린다고 했는데 이 설정은 무엇을 나타내는 것일까?
dataDir은 zookeeper의 recovery를 위해 사용된다.
Zookeeper는 모든 데이터를 메모리에 가지고 있기 때문에 서버가 재시작할 때 보존이 불가능하다. 이때 원래의 데이터를 복구할 수 있는 것을 보장하기 위하여 zookeeper는 모든 transaction log를 dataDir에 저장한다.
zookeeper를 재시작하면 dataDir에서 transaction log를 읽어와 모든 트랜잭션을 다시 실행하여 데이터를 복구한다.
하지만 항상 transaction log를 사용하여 자료를 복구한다면 시간이 오래 걸리고 로그가 쌓일수록 복구할 자료의 양에 비해 로그의 크기가 커지는 문제가 발생한다.
이를 해결하기 위하여 transaction log가 일정 이상이 되면, 쌓인 transaction log로 만들 수 있는 데이터를 하드에 저장하고 transaction log를 지운다. (이 데이터를 snapshot이라고 한다.)
다음에 복구할 일이 생기면 snapshot에서 자료를 읽은 뒤 추가로 쌓인 transaction log만을 실행시켜 자료를 복구한다.
분산 애플리케이션을 구현할 때 유용한 ZNode의 몇 가지 속성
ZNode를 생성할 때 두 가지 옵션을 줄 수 있다.
하나는 life cycle에 관련된 옵션으로 persistent/ephemeral 을 설정하는 것이고 다른 하나는 uniqueness에 관련된 옵션으로 sequential node인지 여부를 설정하는 것이다.
1. Persistent mode와 Ephemeral mode (영속 또는 임시 타입)
ZNode의 life cycle에 관련된 설정으로 모든 znode는 둘 중 하나의 성질을 가진다.
임시 znode는 이를 생성한 클라이언트의 세션이 종료되면 주키퍼가 자동으로 삭제한다. 이와 달리 영속 znode는 클라이언트의 세션에 묶여있지 않아서 세션의 종료 여부와 전혀 상관이 없으며 클라이언트가 명시적으로 삭제해야 한다. (znode를 생성한 클라이언트가 아니어도 상관없다). 임시 znode는 어떠한 znode(임시/영속)도 자식으로 가질 수 없다.
임시 노드의 이런 특성을 이용하여 lock이나 leader election을 구현하기도 한다. (???)
임시 노드는 단일 클라이언트 세션에 묶여있지만 다른 클라이언트도 볼 수는 있다(ACL 정책 때문에 보지 못할 수도 있다).
애플리케이션이 언제 분산 자원을 이용할 수 있는지 알아야 할 때 임시 znode를 사용하는 것은 매우 좋은 방법이다.
2. Sequenct mode (순차 번호)
Sequential znode(순차 znode)는 주키퍼가 znode 이름 뒤에 순차 번호를 부여한 것이다. 순차 플래그를 설정하고 znode를 생성하면 단조 증가 카운터(부모 znode가 관리)의 값이 znode 이름에 덧붙여진다.
예를 들어 클라이언트가 '/a/b-'라는 이름의 순차 znode 생성을 요청하면 생성된 znode의 이름은 'a/b-3'이 된다. 이후에 생성 요청을 하면 3보다 더 큰 값을 가지는 znode 이름이 생성된다. 따라서 유일한 이름의 znode가 생성된다.
순차 번호는 분산 시스템에서 전체 이벤트를 정렬하거나 클라이언트가 순서를 알려고 할 때 사용할 수 있다.
3. 감시
감시는 znode에 어떤 변화가 있을 때 클라이언트에 관련 이벤트를 통지하는 기능이다. 주키퍼 서비스에는 감시 기능을 설정하는 연산자와 이를 유발하는 트리거 연산자가 따로 있다.
예를 들어 클라이언트는 특정 znode에 exists 연산자를 호출하는 동시에 감시를 설정할 수 있다. 만일 해당 znode가 존재하지 않으면 exists 연산은 false를 반환할 것이다. 나중에 두 번째 클라이언트가 해당 znode를 생성하면 설정된 감시가 유발되면서 첫 번째 클라이언트에 해당 znode의 생성을 알려준다.
References
하둡 완벽 가이드 4판
https://blog.seulgi.kim/2014/05/zookeeper-1-znode-zookeeper-data.html
https://blog.1028web.com/entry/loading-ZooKeeper
https://zookeeper.apache.org/doc/r3.3.2/zookeeperOver.html
https://engkimbs.tistory.com/660
'데이터 엔지니어링' 카테고리의 다른 글
Elasticsearch search_after vs scroll API(cursor) (3) | 2022.10.08 |
---|---|
Kafka exactly-once delivery 지원 (0) | 2022.09.03 |
Message Queue | Message Broker와 pubsub 차이 (0) | 2022.08.13 |
MapReduce vs Spark | 맵리듀스와 스파크의 차이점 (0) | 2022.08.13 |
Spark RDD, DAG란? (1) | 2022.08.12 |
댓글