[ZooKeeper] (1) ZNode - ZooKeeper가 data를 저장하는 방법.

지난번 글에서 ZooKeeper는 일종의 파일시스템을 제공해주어 이를 이용하여 semaphore나 mutex를 만들어 사용할 수 있다고 말했다. 이때 ZooKeeper가 제공해주는 파일시스템에 저장되는 파일 하나하나를 znode라고 부른다. 이번에는 znode에 대해서 자세히 설명해보도록 하겠다.

ZNode의 특징

hierarchy

znode는 unix-like 시스템에서 쓰이는 file system처럼 node 간에 hierarchy namespace를 가지고, 이를 /(slash)를 이용하여 구분한다.

https://zookeeper.apache.org/doc/r3.3.2/zookeeperOver.html

일반적인 file system과 다른 부분이 있다. ZooKeeper는 file과 directory의 구분이 없이 znode라는 것 하나만을 제공한다. 즉, directory에도 내용을 적을 수 있는, directory와 file 간의 구분이 없는 file system이라는 것이다.

이는 znode의 큰 특징 중 하나이다. namespace hierarchy를 가지기 때문에 관련 있는 일들을 눈에 보이는 하나의 묶음으로 관리할 수 있으면서, directory가 내용을 가질 수 있게 함으로써(혹은 file 간에 hierarchy를 가진다고 하기도 한다. 원하는 표현으로 말하면 된다.) redundunt한 file을 생성해야 하는 것을 막을 수 있다.

size 제한

ZooKeeper는 모든 data를 메모리에 저장한다. data를 메모리에 저장하기 때문에 jvm의 heap memory를 모든 znode를 올릴 수 있는 충분한 크기로 만들어야 한다.

심지어 The disk is death to ZooKeeper.라고 말하면서, JVM이 heap memory를 swap 하여 하드에 저장하는 것을 피하도록 설정하는 것을 강요(?)하고 있다.

data를 저장하는 보통의 파일 시스템이나 DBMS같은 경우 모든 data가 메모리에 올라갈 수 있는 크기로 제한된다는 제약조건은 말도 안 되는 조건이라고 할 것이다. 하지만 znode의 목적은 data를 저장하는 것이 아니라, distributed 된 시스템 간의 조정을 하기 위함이다. 따라서 znode에는 조정에 필요한 meta data만을 저장하는 것이 기본적은 사용법이고, znode 자체도 크기가 작은 data를 저장할 것이라고 가정하고 구현되어 있기 때문에 각 znode의 크기는 1MB로 제한된다.

Recovery

ZooKeeper설정파일을 봤으면 모든 data를 메모리에 올린다는 설명이 이상하게 느껴질 것이다. ZooKeeper의 설정파일에는 dataDir을 설정할 수 있게 되어 있다. 그렇다면 dataDir은 무엇을 위한 것일까?

dataDir은 zookeeper의 recovery를 위해 사용된다. ZooKeeper는 모든 data를 메모리에 들고 있기 때문에 서버가 종료되었다가 재 시작했을 때 자료를 보존할 수 없다. 이때 원래의 자료를 복구할 수 있는 것을 보장하기 위하여 ZooKeeper는 모든 transaction log를 dataDir에 저장한다.

zookeeper를 재 시작하면 dataDir에서 transaction log를 읽어와서 모든 트랜잭션을 다시 실행하여 data를 복구한다. 하지만 언제나 transaction log만을 이용하여 자료를 복구한다면, 자료를 복구하는데 시간이 걸리기도 하고, 무엇보다도 로그가 쌓일수록 복구할 자료의 양에 비해서 로그의 크기가 커지는 문제가 생긴다.

이를 해결하기 위하여 ZooKeeper는 transaction log가 일정 이상이 되면, 지금까지 쌓인 transaction log로 만들 수 있는 data를 하드에 저장하고 transaction log를 지운다. 이 data를 snapshot이라고 하는데, 다음에 복구할 일이 생기면 이 snapshot에서부터 자료를 읽어온 뒤, 추가로 쌓인 transaction log만을 실행시켜 자료를 복구한다.

ZNode의 종류

znode를 생성할 때 2종류의 옵션을 줄 수 있다. 하나는 life cycle에 관한 option으로 persistent인지 ephemeral인지를 설정하는 것이고, 다른 하나는 znode의 uniqueness에 관한 option으로 sequential node인지 아닌지를 설정하는 것이다.

Persistent mode와 Ephemeral mode

Persistent mode와 Ephemeral mode는 znode의 life cycle에 관한 설정으로, 모든 znode는 persistent 하거나 ephemeral 하지만 동시에 둘 다일 수는 없다. Persistent mode로 생성된 znode는 명시적으로 삭제될 때까지 지워지지 않는, 우리가 일반적으로 생각하는 file과 같다. 그렇다면 Ephemeral mode는 어떻게 동작할까?

Ephemeral mode로 생성된 znode는 ZooKeeper서버와 znode를 생성하도록 요청한 클라이언트 사이의 connection이 종료되면 자동으로 지워진다. ephemeral node의 이런 특성을 이용하여 lock이나 leader election을 구현하기도 한다.

Sequence mode

Sequence mode는 znode의 uniqueness를 보장하기 위한 것이다. sequence mode로 만들어진 znode는 주어진 이름 뒤에 int 범위의 10개의 숫자가 postfix로 붙는다. 이 숫자는 atomic 하게 증가하여 같은 이름으로 만든 node라고 해도 서로 다른 이름의 znode로 만들어준다.1)

ephemeral mode와 persistent mode 둘 다 sequence node로 만들 수 있다. 하지만 ephemeral mode와 sequence mode를 동시에 사용하여 node를 생성하는 것은 문제가 생길 수 있다. ZooKeeper서버와 ZooKeeper클라이언트는 비동기적으로 동작할 뿐 아니라 둘 사이의 connection은 빈번하게 끊길 수 있다. 따라서 ephemeral + sequence node를 생성하라고 요청하였을 때 성공인지 실패인지 응답이 와야 하지만 실제로는 응답이 오지 않고 timeout이 발생할 수 있다. 이때 성공인지 실패인지 알기 위해서는 node가 생성되었는지 확인해야 하는데 생성된 znode의 이름을 서버에서 결정하기 때문에 클라이언트는 znode의 생성 여부를 알 방법이 없다.

ZooKeeper를 사용하기 쉽게 해주는 curator라는 라이브러리에서는 이를 위해 protect mode라는 것을 도입하였다. 이에 대해서는 다음 기회에 curator를 설명할 기회가 있으면 더 자세히 다루도록 하겠다.

ACL (Access Control List)

ACL Permissions

ZooKeeper에서의 권한은 unix-like 파일시스템의 권한과 크게 다를 것은 없다. 특정 권한들에 대해 allow flag가 있어서 이것이 어떻게 설정되는가에 따라서 해당 권한을 실행시킬 수 있는지가 결정된다.

ZooKeeper에서 설정할 수 있는 ACL의 종류는 아래의 5가지이다.

  1. CREATE : 해당 znode의 자식 node를 만들 수 있는 권한.
  2. READ : 해당 znode에서 data를 읽고 와 그 자식들의 목록을 읽을 수 있는 권한.
  3. WRITE : 해당 znode에 값을 쓸 수 있는 권한.
  4. DELETE : 해당 znode의 자식들을 지울 수 있는 권한.
  5. ADMIN : 해당 znode에 권한을 설정할 수 있는 권한.

unix-like file system과 다른 부분이 2가지 있다. 첫 번째는 보통의 file system에는 없는 CREATE와 DELETE라는 권한이 존재하여 자식 node를 생성하고 삭제할 수 있는 권한이 있다는 것이다. unix-like file system에서 directory는 실제로는 자기 자식의 list를 가지고 있는 file이다. 그래서 자식을 만들고 지우는 것은 부모 directory에 내용을 변경하는 것이고, 부모 directory에 쓰기 권한이 있는지가 자식을 만들고 지우는 권한이 된다. 하지만 ZooKeeper에서는 모든 znode가 directory이기도 하고, file이기도 해서 자기 자신에 대한 쓰기 권한과 자식 node에 대한 생성/삭제 권한을 같이 쓸 수 없다.

이때 조심해야 하는 것이 있다. 각 znode의 ACL은 자기 자신의 ACL이고, 자식들에게 recursive 하게 적용되지 않는다는 것이다. 즉, /some-node에 권한을 설정하였다고 해도 /some-node/child에는 권한이 설정되지 않는다는 것이다.

Schemes

znode는 ACL(Access control list)을 이용하여 각각의 znode에 접근권한을 설정할 수 있다.

ZooKeeper는 unix-like 시스템과 다르게 각 znode에 user/group/others라는 개념이 존재하지 않는다. 대신 scheme이라는 것을 이용하여 권한을 구분하게 되어 있다. built in으로 제공되는 설정할 수 있는 scheme은 아래와 같이 4가지가 있다.

  1. WORLD
  2. AUTH
  3. DIGEST
  4. IP

WORLD는 모든 요청에 대해 허락하는 것이고, AUTH는 authenticated된 session에서 들어오는 요청에 대해서만 허락하는 것이다. DIGEST는 username과 password를 보내서 이를 이용하여 만든 MD5 hash값이 같은 요청에 대해서만 처리하는 것이고, IP는 해당 IP에서의 요청만을 처리하도록 하는 것이다.

Stat

ZNode는 node와 node의 data에 관한 여러 정보를 들고 있고, 이것을 stat이라고 부른다. stat이 가지는 정보는 다음과 같다.

  • czxid : znode를 생성한 트랜잭션의 id
  • mzxid : znode를 마지막으로 수정 트랜잭션의 id
  • ctime : znode가 생성됐을 때의 시스템 시간
  • mtime : znode가 마지막으로 변경되었을 때의 시스템 시간
  • version : znode가 변경된 횟수
  • cversion : znode의 자식 node를 수정한 횟수
  • aversion : ACL 정책을 수정한 횟수
  • ephemeralOwner : 임시 노드인지에 대한 flag
  • dataLength : data의 길이
  • numChildren : 자식 node의 수

1) 이 숫자의 범위는 int 범위로 최대 2147483647까지 올라가고 그보다 커지면 overflow가 발생한다. 문서에는 overflow가 발생했을 때 -2147483647가 된다고 적혀 있지만, 실제로는 -2147483648이다. 이는 단순한 실수로 보인다.

댓글

이 블로그의 인기 게시물

[C++] enum class - 안전하고 쓰기 쉬운 enum

Log Aggregator 비교 - Scribe, Flume, Fluentd, logstash

RAII는 무엇인가

[Python] cache 데코레이터로 최적화하기

[Web] SpeechSynthesis - TTS API