Zookeeper
Apache에서 개발한 오픈 소스 분산 형상 관리 시스템으로, 분산 어플리케이션을 위한 분산 코디네이터라고 할 수 있다.
한 노드 안에서 프로그램이 수행될 때는 걱정하지 않아도 될 요소인 네트워크를 통해서 메세지가 전송되는 부분이 종종 문제가 된다. 특히 메세지를 전송하고 네트워크가 끊겼을 때 송신자는 수신자가 메세지를 성공적으로 수신 했는지 조차 알 수 없게 된다. 메세지를 받고 처리까지 했는데 응답을 못 준 걸 수도 있고, 메세지 전송 자체가 실패한 걸 수도 있다. 작업이 성공했는지 실패했는지 여부 조차 알 수가 없는 것이다. zookeeper는 이러한 부분 실패(Partial Failure)를 처리하기 위한 분산 처리 도구를 제공한다.
zookeeper는 분산 어플리케이션을 만들기 위해 필요한 동기화, 설정, group, naming에 대한 추상화된 수준의 서비스를 제공한다. 이 기능들을 API로 제공해서 사용하기 쉽고, 데이터 모델도 파일 디렉토리구조와 동일하여 이해하기 쉽다. 또한, JAVA로 작성되었다.
분산 시스템에 있어서 Coordination 기능이 중요하지만 Lock, Race condition, Deadlock 때문에 구현하기 어렵다. 그러나, Zookeeper를 이용하면 coordination은 zookeeper에게 맡기고 분산 어플리케이션을 쉽게 구현할 수 있다.
- Lock: 여러 스레드나 프로세스가 공유 자원을 동시에 접근하는 것을 제어하기 위해 사용되는 동기화 메커니즘, 해당 영역에 들어가기 전에 락을 획득하고 작업이 끝나면 락을 해제
- Race condition(경쟁 상태): 공유 자원을 동시에 접근할 때 결과가 실행 순서에 따라 달라지는 상황, 이 경쟁 상태를 해결하기 위해 락이 사용되며 크리티컬 섹션을 보호하여 스레드 간의 순차적인 액세스를 보장
- 크리티컬 섹션: 동시에 접근하면 안 되는 공유 자원 또는 코드 영역
- Deadlock(교착 상태): 두 개 이상의 작업이 서로가 소유한 자원을 기다리며 무한히 대기하는 상태, 각 작업은 다른 작업이 가진 자원을 요청하면서 자신이 가진 자원은 유지함, 이로 인해 모든 작업이 더 이상 진행 할 수 없게 됨
Zookeeper 특징
- high performance: zookeeper 성능은 대규모 분산 시스템에서 사용할 수 있는 수준이다.
- high available: 자기 자신이 클러스터로 구성되어 있어 특정 노드가 죽더라도 SPOF(single point of failure)가 발생하지 않는다.
- strictly ordered access: 모든 접근에 순서를 보장하여 정교한 동기화 기능을 구현한다.
모든 zookeeper 내 서버들은 같은 데이터를 복제한 것을 가지고 있다. 또한, zookeeper 자체도 ensemble(앙상블)이라는 호스트 집합을 통해 복제된다.
zookeeper 클러스터를 구성하는 서버들은 모두 서로에 대해 알고 있다. persistent store(disk)에 트랜잭션 로그, 스냅샷, 메모리 내 상태 이미지를 유지한다. 클러스터를 구성하는 서버들 중 과반수(quorum) 이상이 유지되면 일부에 장애나 문제가 있어도 전체 zookeeper 서비스는 유지된다.
클라이언트는 zookeeper 서버에 연결한다. 클라이언트는 zookeeper 연결에 대해 다음 기능을 수행할 수 있다.
- request: read
- response: write
- watch event: 내가 바라보고 있는 znode가 변경되면 알려줘!
- heartbeat: connection이 활성화된 상태에서 client library에서 주기적으로 보냄(널 계속 보고있다구!)
- 클라이언트가 연결된 서버에 대한 TCP 연결이 끊어지면 클러스터 내 다른 서버에 연결된다.
zookeeper is ordered: zookeeper는 모든 트랜잭션에 대해 순서를 남기고 그대로 관리한다. 이 순서를 사용하여 synchronization primitives와 같은 더 높은 수준의 추상화를 구현한다.
zookeeper is fast: 특히 read 위주의 워크로드에서 빠르다. 수천대 서버에서 하나의 주키퍼 클러스터를 이용해서 코디네이션 서비스를 이용할 수 있지만, 이것은 read 위주의 작업이고 그 데이터의 양이 많지 않을 때 적합하다. (because of memory) 따라서, zookeeper에서 감당 가능한 적정한 read:write 비율은 10:1 (혹은 그 이하) 이다.
zookeeper 데이터 모델 (znode)
zookeeper는 파일 시스템과 매우 유사하게 구성된다. 네임스페이스는 zookeeper 용어로, znodes라고 불리는 데이터 레지스터로 구성되어 있다. 이름은 슬래시(/)로 구분된 일련의 경로 요소이고, 네임스페이스의 모든 노드는 경로로 식별된다.
표준 파일 시스템과 달리 자기 자신도 파일(? 을 가질 수 있다. 또한 하위 경로도 가지고 있으므로 각 하위 경로 내의 파일또한 모두 가지고 있게 된다. zookeeper는 coordination 데이터(상태 정보, 구성, 위치 정보 등)를 저장하도록 설계되었으므로 각 노드에 저장되는 데이터는 일반적으로 바이트에서 킬로바이트 범위로 작다.
zookeeper 요청 처리
- Replication & read: request process를 제외하면 zookeeper 서비스를 구성하는 각 서버는 각 구성요소의 자체 복사본을 복제한다. 복제된 데이터베이스는 전체 데이터 트리를 포함하는 in-memory 데이터베이스이다. update는 복구 가능성을 위해 디스크에 기록된다. write는 메모리 내 데잉터베이스에 적용되기 전에 디스크에 serialization 된다.
- in-memory: 일반적으로 데이터는 디스크나 데이터베이스와 같은 영구 저장소에 저장되지만, 인메모리 데이터는 메모리(RAM)에 저장됨
- Contract Protocol: 모든 zookeeper 서버는 클라이언트에 서비스를 제공한다. 클라이언트는 cluster 내의 여러 서버 중 하나의 서버에 연결하고 요청을 보낸다. (맨 처음 그림 참조) read, request는 각 서버 데이터베이스의 로컬 복제본에서 처리한다. 서비스 상태를 변경하는 요청, write 요청은 contract protocol에 의해 처리된다.
- zookeeper 내 서버는 역할에 따라 leader와 follower로 나뉘는데, 역할에 따라 contract protocol은 다음 기능을 한다.
- leader: 데이터의 수정 권한을 가진 노드, write operation이 수행됨
- follower: leader가 아닌 클러스터 내 모든 서버, write operation을 리더에게 전달, 리더로부터 메세지 받고 전달함
- zookeeper 내 서버는 역할에 따라 leader와 follower로 나뉘는데, 역할에 따라 contract protocol은 다음 기능을 한다.
- Messaging Layer: 메세징 계층은 실패 시 리더를 교체하고 팔로워를 리더와 동기화하는 작업을 담당한다.
Zookeeper Quorum
Quorum이란, zookeeper 클러스터의 앙상블을 이루고 있는 모든 서버 중 과반수 서버로 이루어진 그룹을 말한다. Quorum을 이루는 노드들은 반드시 running 상태여야 하고(노드 하나가 죽으면 빠르게 다른 노드를 포함시켜야 함) 클라이언트 요청을 처리하는 최소한의 서버 노드로 구성되어 있어야 한다. zookeeper에서 데이터 변경(write)를 성공했다면, Quorum을 구성하는 노드들은 변경 트랜잭션이 반영된 상태를 유지해야 한다. -> Quorum은 We are one!
만약 과반수로 구성하지 않는다면
- 일부 복제본이 오래된 데이터를 가지고 있거나 데이터가 불일치 할 수 있다.
- 과반수를 충족해야 동일한 데이터를 보유하고 동일한 결정을 내릴 수 있다.