1. 개발

링크드인에서 개발되었고 카프카 개발자 3명이 분사하여 콘플루언트를 창업하였다. 현재는 아파치를 통해 제공되고 있다.

기타 카프카 관련 Tech Note는 다음과 같다.


2. 소개

2.1. Kafka 소개

분산 메시징 시스템으로 대용량 스트림 정보를 처리하기 위한 오픈 소스 프로젝트이다.

Pub-sub 모델의 메시지 큐로 ActiveMQ, RabbitMQ 등의 다른 메시지 큐와 비교되지만 분산 및 대량 처리에 특화되어 있어 기존 메시지 큐에 비해 TPS가 우수하다. 역으로 범용적인 메시지 큐가 제공하는 다양한 기능은 제공되지 않을 수 있다. 물론 클러스터 구성, failover, replication 등은 모두 지원한다.

오버헤드를 줄이기 위해 단순한 메시지 헤더 기반의 TCP 프로토콜을 사용하므로 AMQP, JMS API 등은 지원하지 않는다.

클라우드 단에서는 AWS Kinesis와 비교되기도 한다.

메시지를 기본적으로 메모리에 저장하는 제품들도 있는데 카프카는 메시지를 파일 시스템으로 저장함. 따라서 데이터 영속성을 보장한다.

처리되지 않고 남아있는 메시지가 많으면 기존 제품들은 성능 저하가 발생하지만 카프카는 파일 처리 방식이라 크게 영향 없다. 처리된 메시지를 바로 삭제하는 제품들과 달리 처리된 메시지를 삭제하지 않는다. 단지 설정된 수명이 다하면 삭제된다. 혹시 나중에 다시 메시지를 처리하고자 할 때 (처리 로직 변경 등) 유용하다.

그렇다고 무한정 보관되는 것은 아니기 때문에 카프카에서 소멸되기 전 하둡이나 기타 파일 디비 등으로 아카이브해야 한다.

데이터를 분석하고 시각화하고자 한다면 ELK(Elasticsearch, Logstash, Kibana) 도입이 가능하다.

2.2. Pub-sub 모델

메시지를 특정 수신자에게 직접 보내는 방식이 아니다.

  • Publisher는 topic을 통해 메시지를 카테고리화한다. 
  • 분류된 메시지를 받기 원하는 receiver는 해당 topic을 Subscribe한다.
  • 이는 즉 Publisher와 Subscriber 모두 topic만 바라본다. Publisher와 Subscriber는 서로 모른다.

3. Producer - Consumer

앞서 설명한대로 Publish-Subscribe 모델 기반으로 Producer, Consumer, Broker로 구성된다.

  • 메시지 생산 주체인 Producer가 Topic에 메시지 생성하여 Broker에게 전달하고
  • Broker는 전달받은 메시지를 Topic별로 쌓아놓으면
  • 해당 Topic을 구독하는 (즉 메시지를 소비하는 주체인) Consumer들이 메시지를 가져간다.
  • Consumer는 topic 내 파티션에 존재하는 offset의 위치를 통해 이전에 소비한 offset 위치를 기억/관리하고 이를 통해 Consumer는 문제가 생겨도 마지막 위치부터 다시 읽어들일 수 있다.

3.1. Producer

Produce API 또는 미들웨어를 사용하여 Broker에 데이터를 송신한다.

  • Apache Log4j (Kafka Appender)
  • Apache Flume (대량 데이터를 수집, 취합하기 위한 분산형 소프트웨어) 
  • Fluentd (데이터 수집 소프트웨어)
  • Logstash (Elastic이 제공하는 데이터 수집)

Producer가 Broker에게 메시지 전달 시 기존 메시지 큐는 각 메시지를 개별적으로 전달해야 했지만 카프카는 배치 형태로 Broker에게 한번에 전달할 수 있어서 RT 수를 줄일 수 있다.

3.2. Consumer

Consume API를 통해 메시지를 취득한다. 또 Producer처럼 Consume 기능을 갖춘 제품들이 있다.

  • Apache Spark (빅데이터 처리를 위한 오픈 소스 프레임워크)
  • Apache Samza (스트림 처리를 위한 리얼타임 비동기 프레임워크)
  • Apache Flink (스트림 처리용)

카프카는 데이터를 디스크에 영속화하기 때문에 Broker에 데이터가 도달하는 즉시 Consume할 필요가 없다는 특징이 있다. 즉 Broker는 일정 기간 데이터를 보관하고 있다. 

또 다른 제품들은 Broker가 Consumer에게 push 하는 방식이지만, 카프카는 Consumer가 Broker로부터 직접 메시지를 가져오는 pull 방식이다.

Consumer 자신의 처리 능력만큼만 메시지를 가져온다. 따라서 Broker가 Consumer가 어떤 메시지를 처리해야 하는지 계산하고 처리 중 메시지를 트래킹할 필요가 없으므로 Broker의 부하가 줄어든다. 메시지를 pull 방식으로 처리하기 때문에 Batch Consumer 구현이 가능하다.

Consumer에는 Old Consumer와 New Consumer가 있다.

  • Old Consumer : Consumer의 offset 관리를 주키퍼의 znode에 저장한다.
  • New Consumer : Consumer의 offset 관리를 Kafka의 topic에 저장한다.

메시지를 메모리에 저장하지 않기 때문에 JVM의 부담이 줄어든다. GC도 줄인다.

3.3. Broker

카프카는 Scale-out과 HA 지원을 위해 Broker들이 클러스터로 동한다.

Broker가 단 1개여도 클러스터로 동작하는데, 클러스터 내 Broker에 대한 분산처리는 주키퍼가 담당한다. 그래서 카프카를 설치할 때는 주키퍼부터 깐다.

카프카 클러스터에서 파티션은 보통 8~20개로 분리하여 사용한다. 파티션은 0번부터 시작하고 카프카의 최소 단위는 offset이다. (offset -> 파티션 -> Topic -> Broker -> 클러스터)


4. Topic & Partition

4.1. 기본 개념

  • 메시지는 topic으로 분류된다.
  • topic은 여러 파티션으로 나눠질 수 있다. Producer에서의 메시지 수신, Consumer로의 배달을 분산하여 수행함으로서 하나의 topic에 대한 대규모 처리를 가능하게 한다. 
  • 파티션 내 한칸의 단위는 로그이다. 데이터는 한칸의 로그에 순차적으로 append된다.
  • 메시지의 상대적 위치를 나타내는 것은 offset이다. (배열의 index 개념)

4.2. 파티션 관리

  1. 하나의 topic에 하나의 파티션
  2. 하나의 topic에 여러개 파티션

메시지는 topic에 저장되는데 저장 과정에도 시간이 소요된다.

  • 만약 수백 수천건의 메시지가 동시에 kafka에 저장된다면, 하나의 파티션에 순차적으로 append될 것이다. 
  • 따라서 여러 파티션을 통해 분산 저장을 꾀하는 것이다. 병렬처리를 통해 시간절약이 가능하다.
  • 단 한번 늘린 파티션은 다시 줄일 수 없기 때문에 충분한 고려가 필요하다. 

4.3. 이벤트 순서

Kafka는 topic의 타 파티션이 아닌 특정 파티션 내의 메시지에 대해서만 전체 순서를 보장한다 즉 단일 파티션이 있는 경우 주문이 보장된다. 예를 들어,

  • 파티션이 3개인 topic을 생성 후
  • 해당 topic으로 메시지 a, b, c, d, e를 보내고
  • Consumer 실행 후 결과를 보면 a, b, d, e, c 순으로 메시지가 들어올 수 있다.
  • 이는 0번 파티션에 b, e / 1번 파티션에 c / 2번 파티션에  a, d가 저장되어 있을 수 있는 것이다.

다시 정리하면 Kafka에서 메시지 순서를 보장하고자 한다면 topic의 파티션 수를 1로 지정해야 한다. 계속 같은 이야기지만 이 때의 단점은 topic에 파티션이 1개이기 때문에 분산 처리가 안된다.

만약 topic 내에 다중 파티션을 사용한다면 publish 시에 key로 구분하고 동일 key의 이벤트는 해시 알고리즘에 의해 동일 파티션에 publish되게 할 수는 있겠으나 만약 중간에 파티션 개수가 늘어난다면 이 또한 개런티가 안된다.

4.3. Custom Partition

특정 파티션에 데이터를 넣어주도록 할 수 있다.


5. Replication

5.1. Replication 개념

사실 topic 자체를 replication하는 것이 아니라 topic을 구성하는 각 파티션을 replication하는 것이다.

Broker 3대를 띄우고 (replica-factor=3) 복제하는 경우를 보면,

  • 복제는 Scale-out 방식이다.
  • Broker 3대 중 leader는 단 1대이며, 나머지 2대를 follower다.
  • Producer가 메시지를 쓰고 Consumer가 메시지를 읽는 것은 단지 leader가 담당한다.
  • 나머지 follower들은 leader와 sync를 맞추고 있다. 옵션에 따라 나머지 follower중에 하나가 leader 선출될 수 있다. 다만 주의가 필요하다. 좀 더 자세히 알아보자.

5.2. Leader & Follower

우선 false 설정이다. 복구 시에 데이터 일관성을 우선시하는 설정이다. 이는 가장 최근의 leader가 다시 leader로 선출되는 방식이다. 따라서 최근 leader 복구를 위한 서비스 장애가 좀 더 지속될 수 있다. 하지만 데이터 유실은 발생하지 않는다.

unclean.leader.election.enable=false

이번엔 true 설정이다. 이는 가장 먼저 복구되는 것을 새로운 leader로 선출하는 방식이다. 서비스 장애는 최소화되지만 장애 발생 시점의 데이터 복제 상태에 따라 데이터 유실이 발생할 여지가 있다.

unclean.leader.election.enable=true

6. Kafka Connect

서버에서 생성되는 데이터를 실시간으로 Kafka에 보내거나, topic에 있는 데이터를 실시간으로 RDBMS 혹은 Object storage에 보낼 수도 있다. Kafka Connect는 Kafka와 이런 다양한 시스템과의 연결을 가능케 한다. 

Kafka Connect는 여러 작업 프로세스로 구성되는데, 아래 Connector 사이에서 일을 한다.

  • Soure Connector : Source System -> Kafka (소스에서 데이터를 읽어 Connect 데이터 객체로 작업 프로세스에 제공) 
  • Sink Connector : Kafka -> Target (작업 프로세스로부터 Connect 데이터 객체를 받아서 대상 시스템에 반영)

Kafka Connect는 JSON, Avro, Protobuf 등 다양한 직렬화 포맷을 지원하며 Kafka Schema Registry와 연동하여 공통된 스키마 지정을 할 수도 있다. 만약 Kafka Connect를 사용하지 않는 상황에서 데이터를 실시간으로 전달하고자 한다면 Producer, Consumer API를 사용해야 하는데 이 과정에서 이미 처리되거나 실패한 데이터를 추적한다거나, 데이터를 분산 처리하고, 작업을 배포하는 등의 작업이 동반된다. Kafka Connect는 이런 작업들을 다 수행해주면서 Connector task를 클러스터에 자동 배포한다.

또한 사용자 정의 Source Connector를 만들 수도 있다. 

EFK의 Fluentd, ELK의 Logstash 등 여러 시스템 사이의 브릿지 역할을 하는 프레임워크들이 있긴 하지만 Kafka Connect는 Kafka와 밀접하게 연결되어 있다는 장점이 있다.

  • DB to DB
    - 1:N 복제, NoSQL 연계 등에 활용
    - CDC나 EAI와 비교 분석 필요
    - 물론 일반적인 조회성 처리는 API로 처리하고 준실시간 데이터 복제등만 활용하는 방안을 고민한다.
  • 대용량 데이터 전송 처리
  • 데이터 복제
  • 데이터 변경 이력 기록

Kafka Connect의 실행 모드는 다음과 같다.

  • Standalone : 하나의 작업 프로세스를 통해 모든 Connector와 Task가 실행, 테스트 시에는 유용
  • Distributed : 대용량 데이터 처리를 위해서 추천

7. Debezium Connector

MySQL에서 발생한 변경사항을 Kafka로 전송하는 Connector이다. 

원리는 MySQL에서 생성된 Binlog(변경된 내용이 저장된 영역)를 사용하는 것이다. INSERT/UPDATE/DELETE에 대한 내용을 Kafka Topic으로 전송한다. 

따라서 당연히 Binlog가 활성화되어 있어야 하고, Binlog는 row-level이어야 한다.


8. Kubernetes와 Kafka

카프카를 VM으로 만들지 컨테이너로 만들지 결정한다. 컨테이너로 만든다면 자원의 격리 구성(Isolation 모델)을 고려한다.