카프카에 대해 알아보자

카프카란?

처음 링크드인(LinkedIn)에서 시작한 데이터 스트리밍 플랫폼으로, 링크드인 서비스 내부의 문제를 해결하기 위해서 시작된 프로젝트이다. 그 문제는, 링크드인 고객이 이직하였을 때 지인들에게 그 사실이 업데이트되는 부분과 추천 서비스에 필요한 정보들이 업데이트되는 것이 더디다는 점이였다.
카프카를 통해 문제를 해결할 수 있었고 좋은 사례로 자리잡아, 2011년 아파치 오픈소스로 공개되어 많은 곳에서 채택되어 사용되었다.

아파치 카프카는 실시간으로 레코드의 스트림을 게시, 구독, 저장, 처리할 수 있는 분산 데이터 스트리밍 플랫폼이다. 다양한 소스에서 생겨나는 데이터 스트림을 처리하고 여러 컨슈머에게 전달되도록 설계되었습니다.
한마디로, 데이터 스트림을 처리할 뿐 아니라, 저장 그리고 다른 시스템에서 데이터를 가져오거나 시스템에 데이터를 내보낼 수 있는 기능을 가지고 있다. 공식 문서에 의하면 다음과 같이 정리된다.

  • 이벤트 스트림을 발행 및 구독하며, 다른 시스템에 데이터를 import/export 할 수 있다.
  • 이벤트 스트림을 원하는 기간 만큼 신뢰성 있는 곳에 저장할 수 있다.
  • 이벤트 발생 즉시 스트림을 처리하거나 모아서 처리할 수 있다.

또한, 모든 기능들은 분산 서비스가 가능하고, 확장성 있으며, 탄력적이고 내결함성이 있는 안전하게 제공된다.
이러한 카프카는 베어 메탈, 가상 머신, 컨테이너에 배포 가능하며 클라우드나 온 프레미스 환경에도 배포 가능하다. 카프카 환경도 직접 관리하거나 완전 관리형 서비스를 사용해도 좋다.

이벤트 기반 아키텍처

이벤트 기반 아키텍처는 이벤트 발행, 캡처, 처리, 저장을 중심으로 비즈니스 로직이 수행되는 아키텍처를 뜻한다. 여기서 이벤트란 작업 또는 변경에 대한 기록이며, 애플리케이션이나 서비스에서 어떤 작업이나 변경을 수행하는데 다른 애플리케이션이나 서비스에서 이 상황을 알아야하는 경우 이벤트를 발행한다고 표현한다.

이러한 아키텍처는 서로 연결된 어플리케이션과 서비스 간의 느슨한 결합을 지원하게 되는데, 기존에 요청 기반 방식과는 달리 이벤트를 발행/소비하는 방식으로 통신을 합니다. 상호 간에 통신을 위해서 알아야하는 정보는 이벤트의 형식 외에는 없다.

그렇다보니 이벤트 생성자와 소비자 간의 이벤트가 비동기식으로 처리되기 때문에 생성자는 소비자와 소비한 결과에 대해서 알 수 없다. 소비자 또한, 이벤트 외에 생성자에 대한 정보를 알 수 없다.
이벤트 처리 플랫폼이 생성자와 소비자를 이어주는 채널을 생성해주며, 서로 간의 연결고리 역할을 해주는 것이다.

이벤트 기반 아키텍처 메시징 모델

  • Pub/Sub 모델
    • 이벤트 생산자가 이벤트를 발행하면, 소비하려는 모든 구독자에게 이벤트가 전송된다.
    • 메세지 브로커가 발행자와 구독자간의 이벤트 메세지 전송을 처리한다. 이 때, 필요하다면 변환하며 항상 메세지의 순서를 유지한다.
    • 만약 메세지가 소비되었다면, 해당 메세지를 삭제한다.
  • Streaming 모델
    • Pub/Sub 모델과 동일하게 이벤트 발행자가 브로커에게 이벤트의 스트림을 발행하고, 소비자는 스트림을 구독한다.
    • 이벤트 발행 순서대로 수신, 소비하는 것이 아닌 소비자가 각 스트림에서 임의의 지점에 진입하여 소비하고 싶은 이벤트만 소비한다.
    • 이벤트를 소비하더라도 브로커가 보존한다.

위와 같은 특징을 기반으로 장점과 고려 사항을 정리해보려고 한다.

장점

  • 실시간 응답과 분석
  • 느슨한 결합에 의한 유지보수 이점
    • 확장성, 내장애성
  • 비동기 메시징
  • 마이크로 서비스 아키텍처(MSA)와 적용

고려 사항

  • 가변적인 지연 시간
  • 호출자에게 값 반환
  • 에러 디버깅 및 재현
  • 데이터 결과적 일관성

카프카 특징

  • 높은 처리량과 낮은 지연시간
    • 페이지 캐시
    • 배치 전송 처리
    • 메세지 메모리 저장이 아닌 파일 시스템에 저장
    • TCP 기반 프로토콜
Kafka, Pulsar, RabbitMQ Benchmark (출처)
  • 높은 확장성
  • 고가용성
    • replication
  • 내구성
    • acks 기능
    • disk 저장
  • 개발 편의성
    • 생성자와 소비자의 완벽한 분리
      • Kafka consumer group
    • 3rd party
      • Kafka connect
      • schema registry
  • 운영 및 관리 편의성
    • 분산 시스템

카프카 구조

  • 주키퍼(Zookeeper)
    • 카프카 클러스터 정보 및 분산처리 관리 등 메타데이터 저장
  • 카프카 클러스터(Kafka cluster)
    • 브로커들의 모임으로, 확장성과 고가용성을 위해 클러스터로 구성
  • 브로커(Broker)
    • 카프카가 설치된 서버 또는 노드
  • 프로듀서(Producer)
    • 카프카로 메세지를 보내는 주체
  • 컨슈머(Consumer)
    • 카프카의 메세지를 꺼내는 주체
  • 토픽(Topic)
    • 논리적으로 메세지가 저장되는 장소
  • 파티션(Partition)
    • 메세지를 저장하는 물리적인 파일
    • 한 토픽은 하나 이상의 파티션으로 구성
  • 세그먼트(Segment)
    • 메세지가 브로커의 로컬 디스크에 저장된 파일
  • 메세지(Message) or 레코드(Record)
    • 데이터 그 자체

Zookeeper

분산 애플리케이션을 위한 코디네이션 시스템으로, 분산 애플리케이션이 안정적인 서비스를 할 수 있도록 각 애플리케이션들의 정보를 중앙에서 집중 관리, 그룹 관리, 동기화 등의 서비스를 제공한다.
주키퍼를 앙상블(3대 이상의 주키퍼)로 구성한 뒤 여러 대의 카프카와 연결하여 상태 정보(메타데이터)를 주고 받는다. 이러한 메타데이터들은 znode에 key-value 형태로 저장되게 된다.
Master(leader)/Slave(follower) 아키텍처로 master가 정보를 가지고 있고, slave들이 정보를 복제해서 가지고 있는 형태이다.

추가적으로 주키퍼는 3대나 5대 같이 홀수로 구성하게 되어있다. 그 이유는 과반수에 의한 의사 결정을 하는 Quorum 기반 알고리즘으로 이루어져있기 때문이다.
과반수 이상의 주키퍼 장비가 정상적으로 작동한다면 주키퍼는 정상 작동하게 되어있다. 따라서 짝수로 구성하더라도 과반수의 수는 같으므로 홀수의 서버로 구성하는 것이다.

카프카 2.8 버전부터 KRaft (Kafka Raft metadata mode)가 추가되어서 주키퍼의 의존성을 제거하고 있다. Raft 알고리즘을 통해서 주키퍼의 역할(브로커)을 대체하고 있다.
정확히 3.3 버전부터 정식 릴리즈가 되어 Zookeeper mode와 KRaft mode 두 가지 중 골라서 사용할 수 있게 되었다.

Producer

Kafka Producer Flow (출처)

레코드는 데이터를 뜻하며, topic과 value는 필수 필드, partition과 key optional 필드이다. 여기서 파티션과 키의 역할은 다음과 같다.

  • partition: 특정 파티션을 지정해서 저장하기 위한 필드
  • key: 특정 파티션에 레코드들을 정렬하기 위한 필드

Record가 Send() 메소드를 통해 직렬화한 뒤 파티션이 정해지게 된다. 이 때, 파티션을 지정하지 않았다면 라운드 로빈 방식으로 파티션이 정해지게 되고, 지정하였다면 바로 해당 파티션에 레코드가 전달된다.
Send() 메소드 이후에 레코드들을 파티션 별로 잠시 모아두고 프로듀서가 카프카 브로커에게 배치 전송하게 된다. 전송 실패 시엔 정해진 횟수만큼 재시도를 하게 되고, 성공하게 되면 메타데이터를 반환한다.

Consumer

메세지를 구독하는 주체로, 파티션을 지정해서 1:1로 매핑하게 된다. 파티션과 1:1 매핑하다보니, 토픽안의 파티션이 너무 많은 경우 Consumer Group을 통해 처리량을 늘릴 수 있다.

Consumer Group

컨슈머들의 논리적 그룹으로, 파티션과 1:N 매핑을 갖으며 동일 그룹 내 하나의 컨슈머만 파티션과의 연결이 가능하다. 따라서 컨슈머 그룹 내에 여러 컨슈머들이 있더라도 순서를 보장할 수 있다.
일반적인 메세지 큐에서는 한 컨슈머가 메시지를 읽어가면 다른 컨슈머가 다음 메시지를 읽어가게 된다. 즉, 메세지 큐 내에서 번갈아가면서 읽어가게 된다. 카프카는 다중 컨슈머 그룹(Multiple Consumer Group) 지원을 통해 그룹 간의 독립적으로 데이터를 읽어갈 수 있다.
또, 그룹 내 컨슈머가 추가되거나 한 컨슈머에 문제가 생겼다면 fail over를 통한 리밸런싱을 할 수 있다.

Partition

메세지를 저장하는 물리적인 파일로, 서로 독립적이다. 또한, 토픽에는 무조건 한 개 이상의 파티션을 구성해야햔다. 토픽에 파티션이 많을 수록 많은 컨슈머와 연결(병렬 처리)이 가능하며 1:1 매핑이 best practice로 뽑힌다.
토픽에서 파티션의 수는 언제나 늘릴 수 있으나 줄일 수는 없으므로 토픽 생성 시에 파티션 수를 적게 생성 후 모니터링하며 늘리는 것이 좋다.

또한, 파티션은 append-only로 메세지는 항상 추가만 된다. offset을 통해 파티션 내에서 메시지들의 저장된 상대적 위치를 파악하고, 마지막 커밋 시점부터 순서대로 읽어서 처리하게 된다. 파티션 내의 메시지 파일들은 처리 후에도 계속 저장되어 있으며 설정에 따라 일정 시간 뒤에 삭제된다.

Anatomy of a Kafka Topic (출처)

Segment

브로커에 전송된 메세지는 토픽의 파티션에 저장되는데, 이 메세지들은 세그먼트라는 로그 파일의 형태로 브로커의 로컬 디스크에 저장이 된다.
메세지를 저장하고 소비하는 과정을 적어보자면 다음과 같다.

  1. 프로듀서가 토픽으로 메세지 전송
  2. 정해진 설정에 따라 파티션이 정해지고 해당 파티션의 세그먼트 로그 파일에 메세지를 저장
  3. 브로커의 세그먼트 로그 파일로 저장된 메세지를 컨슈머가 읽어감

ref

댓글