[Kafka] 카프카의 코디네이터 Zookeeper

반응형

우리가 컴퓨터에 카프카를 설치하려면 반드시 따라서 설치해야하는 것이 있습니다. 바로 주키퍼(Zookeeper)인데요. 

다른 메시지 큐와 다르게 카프카는 왜 주키퍼를 필요로 하는 것일까요?

 

 

 

코디네이션 서비스 시스템 (Coordination System)

분산 처리 시스템을 다루다보면 가장 큰 난제가 바로 분산된 시스템끼리 어떻게 정보를 공유할 것인가? 입니다. 쿠버네티스, 카프카 등 분산 시스템 자체는 클러스터를 중심으로 하위 노드들이 집결 되어 있는 형태가 보통인데 클러스터에서 하위 노드를 관리하기 위해 해야할 일은 아래와 같습니다.

 

  • 각 하위 노드들의 Healthcheck
  • Lock Processing

 

분산된 서버들끼리 통신할 때 서로 리소스를 공유하려다 보면 자원 점유 문제가 발생하는데, 이를 처리하기 위한 대표적인 해결책으로는 바로 리소스 락(Lock)을 볼 수 있습니다. 이 리소스를 Lock, Unlock하는데도 관리해주는 시스템이 필요합니다.

 

이러한 문제를 해결하는 시스템을 코디네이션 서비스 시스템이라고 하며 이에 대표적인 오픈 소스 솔루션에는 바로 Zookeeper가 있습니다. 

 

코디네이션 서비스는 분산 시스템 내에서 중요한 상태 정보나 설정 정보 등을 유지하기 때문에 코디네이션 시스템의 장애는 전체 시스템 장애로 이어지는 장애 전파 특성을 지니고 있습니다. 마치 Docker와 같죠.

 

따라서 이중화 등을 이용하여 고가용성을 제공하는 것이 필수입니다. 

 

 

 

Zookeeper

주키퍼는 분산 시스템 설계를 위해 필요한 코디네이션 서비스 시스템이 잘 구현되어 있는 오픈 소스 소프트웨어로써 코디네이션 서비스 시스템을 고가용성(HA)이 용이하도록 구현되어 있어 유명한 분산 솔루션에서 많이 사용되고 있습니다.

 

  • Apache HBase (NoSQL)
  • Apache Kafka

 

분산 시스템을 코디네이션 하는 용도로 디자인 되어 있기 때문에 다음과 같은 사항을 고려해야 합니다.

 

  • Data Access 속도가 빨라야 함.
  • 자체적인 장애 대응 솔루션 필요 (예: 클러스터링)

 

Zookeeper는 자체적으로 클러스터링 기능을 지원하고 있어, 이러한 코디네이션 서비스 시스템 디자인에 있어 아주 쉽게 적용이 가능하다는 장점을 지니고 있으며 기능은 아주 심플합니다.

 

  • 설정관리 (Configuration Management)
    클러스터의 설정 정보를 최신으로 유지하기 위한 시스템

  • 클러스터 관리 (Cluster Management)
    클러스터의 서버(브로커)가 추가되거나 제외될 때 그 정보를 클러스터 내 서버(브로커)들이 공유하기 위한 시스템

  • 리더 채택 (Leader selection)
    다중 애플리케이션 중 어떤 노드를 리더로 산출할 지 정하는 로직을 만드는 시스템

  • 락, 동기화 (Locking and syncronization Service)
    클러스터에서 쓰기 연산이 빈번할 경우 클러스터 전체를 동기화를 통해 데이터 불일치를 방지하는 시스템

 

분산 시스템에서 어떤 노드에게 연산을 시킬지 결정하는 것도 하나의 역할이라고 볼 수 있습니다. 여러 노드들 중 유휴 상태인 노드를 찾는 로직을 어떤식으로 할지 정하는 리더 채택이 그 역할을 합니다.

 

 

 

Zookeeper Architecture

그렇다면 Zookeeper는 어떤 구조로 되어 있을까요?

 

코디네이션 서비스 시스템은 분산 시스템의 일부분이 되어 동작하기 때문에 앞서 말한 것처럼 코디네이션 시스템이 중단되면 분산 시스템도 중단되는 장애 전파가 발생하게 됩니다.

 

Zookeeper는 안정성을 확보하기 위해 다수의 Server를 사용하는 서버 클러스터 구조를 이용합니다. 이는 k8s와 유사한 구조로 서버 클러스터는 1개의 Leader와 N개의 Follower로 구성되어 있고, 이를 주키퍼 앙상블(Zookeeper Ensemble)이라고도 합니다.

 

순서는 Client(여기서는 Kafka)가 주키퍼 서버들로 이뤄진 앙상블(Ensemble)에 접근하여 Znode의 데이터를 읽거나 데이터를 업데이트합니다. 앙상블 내 주키퍼 서버들은 조율된 상태이고, 항상 동일한 데이터를 가지고 있어 어느 서버에서 데이터를 읽어도 같은 데이터로 불러오게 됩니다.

 

Client(Kafka)가 어떤 설정값 혹은 메타데이터를 주키퍼에 쓸 때 아래와 같이 동작합니다.

 

  • 특정 서버에 접속하여 서버 데이터 업데이트
  • 해당 서버는 Leader 서버에 데이터가 업데이트 되었음을 전송
  • Leader 서버는 업데이트 신호를 받고, 다른 Follower 서버들에게 브로드캐스트(Broadcast) 형식으로 전송
  • 나머지 Follower 서버들도 데이터 업데이트

 

위 4가지 과정을 통해 주키퍼는 데이터를 항상 일관성 있게 유지하도록 합니다.

 

 

 

Znode (Zookeeper Data Model)

Znode는 주키퍼의 핵심입니다. 주키퍼의 모든 구현은 거의 이 녀석이라고 봐도 무방할 정도인데요. Znode란 무엇일까요?

 

Znode는 디렉터리 구조 기반으로 된 Key-value 자료구조입니다. 사실상 주키퍼는 여기에 데이터를 넣고 빼는 기능이 가장 큰 핵심이고 나머지는 분산 시스템의 문제를 이로 구현한 것일 뿐입니다.

 

 

이 Znode를 어떻게 구현하냐에 따라서 분산 시스템 간 정보 공유, 이벤트 처리, 락 관리 등을 다양한 분야에서 활용할 수도 있습니다.

 

  • Persistent Node (영속 노드)
    z노드에 데이터를 저장하는 순간 영구 저장되는 노드 (단, 강제 삭제시엔 삭제됨)

  • Ephemeral Node (임시 노드)
    z노드를 생성한 클라이언트의 세션이 연결되어 있을 때만 유효한 노드 (연결이 끊어질 시 자동 삭제)


  • Sequence Node (연속형 노드)
    z노드를 생성할 때 자동으로 sequence 번호가 붙는 노드 (분산락 구현에 이용)


얼핏 보면 Linux File system과도 유사한 형태를 띄고 있습니다. 이 구조를 주키퍼에서는 주키퍼 데이터 모델(Zookeeper Data Model)이라고도 하고, 명명 구조는 Hierarhical namespace 룰을 따릅니다. 

 

각 z노드는 Stat 구조를 유지하고 있고, 이 Stat은 아래의 메타데이터를 제공합니다. 

 

  • Version Number
    모든 znode는 버전 넘버를 가지고 있음. 데이터 업데이트시 계속 변경됨.

  • ACL (Action Control List)
    znode에 접근하기 위한 권한 획득 매커니즘 

  • Timestamp
    znode 생성 시간, 생성 후 경과된 시간, 업데이트 시간을 ms 단위로 저장.
    -> 주키퍼에는 znode를 읽고 쓰는 트랜잭션(Transaction)이 존재하는데, 이 트랜잭션 정보는 Zxid로 구분하며 timestamp를 보고 트랜잭션 발생 로그를 바로 알 수 있음.

  • Data length
    주키퍼의 데이터 크기 (주키퍼의 데이터들은 최대 1MB까지 저장 가능)

결국 주키퍼는 카프카의 코디네이터가 아닌 분산 시스템을 위한 코디네이터라고 할 수 있고, 더 정확히는 분산 작업 제어를 위한 트리 형태의 데이터 저장소입니다.

 

 

 

Watcher

주키퍼에는 Watcher 기능을 제공하는데, Watcher는 특정 znode의 변경 등을 감지하는 주키퍼의 컴포넌트로 특정 znode에 watcher를 넣으면 클라이언트로 콜백을 발생하여 해당 znode가 변경되었음을 알려주며 이와 동시에 해당 watcher는 제거됩니다.

 

 

 

In Kafka

우리가 분산 시스템에 반드시 필요로 하는 것이 코디네이터 서비스 시스템이라는 것을 알았고, 그 대표적인 것이 주키퍼라는 것을 알았습니다. 그렇다면 카프카에서 주키퍼는 어떤 용도로 사용될까요? 

 

카프카의 주키퍼 클라이언트는 카프카의 브로커를 관리하고 조정하는 데 사용합니다. 

 

카프카도 분산 처리 플랫폼이지만 공교롭게도 메시지를 주고 받는 Pub/Sub 외에는 아무것도 하지 않습니다. 따라서 브로커 상태를 저장하지 않기 때문에 상태 관리를 위한 주키퍼를 사용하는 것입니다.

 

이 때 Producer와 Consumer는 카프카의 브로커 정보를 가지고 있습니다. 만약 동적으로 브로커의 상태가 변경(스케일 아웃 되는 등)되는 경우 이를 주키퍼가 Producer와 Consumer에게 알려줍니다.

 

일반적으로 카프카 메시지 브로커와 Zookeeper는 1:1로 구성되며 만약 Producer가 특정 카프카 브로커로 메시지를 생산했을 때 이를 실패하면 주키퍼가 다른 정상 서버로 Producer와 Consumer에게 브로커를 알리는 방식으로 운영됩니다.

 

 

 

마치며...

카프카는 대용량 데이터 분산 처리 플랫폼이지만 오직 데이터 분산 처리에만 그 역할을 하고 있습니다. 따라서 분산 시스템 설계시 필요한 데이터 공유나 락 등은 카프카가 아닌 주키퍼를 통해 별도로 관리되어지고 있다 라고 보시면 됩니다.

 

이러한 소프트웨어 구조는 개인적으로 아주 깔끔하다고 생각합니다. 하나의 소프트웨어가 하나의 책임을 지니게 됨으로써 소프트웨어 관리가 유연해지고 역할이 분명하기 때문에 이해하기도 쉽기 때문입니다.

 

주키퍼에 대한 더 자세한 이야기는 많지만 이번 포스트에서는 카프카에서 주키퍼가 어떻게 사용되는지를 보여주기 위한 간단한 개념과 그 사례를 적어봤습니다.

 

다음 포스트에서는 Docker를 이용하여 카프카를 구동하는 것에 대해 알아보겠습니다.

 

반응형

Tistory Comments 0