Kafka?
- 대규모 실시간 데이터 스트리밍과 로그수집, 이벤트 처리 등을 위해 설계된 분산 이벤트 스트리밍 플랫폼이다.
- 카프카는 단순한 메시지큐이상의 기능을 제공하며, 고성능, 확장성, 내결함성을 갖춘 데이터 파이프 라인을 구성하는데 중점을 둔다.
- 분산 스트리밍 플랫폼
- 카프카는 여러 브로커(서버)로 구성된 클러스터 환경에서 실시간 데이터 스트리밍, 로그수집, 이벤트 소싱 등 다양한 용도로 활용된다.
- 로그 기반 메시징 시스템
- 카프카는 메시지를 변경 불가능한 로그 형태로, 저장한다. 각 메시지는 특정 주제 별로 기록되며 순차적으로 저장된다.
kafka 도입 이유
- 어플리케이션 <-> DB간 end-to-end로 연결이 되어 있고, 요구사항이 늘어남에 따라 시스템 복잡도가 높아지면서 문제점이 발생하고 있음
- 시스템 복잡도 증가 (Complexity)
- 통합된 전송영역이 없어, 데이터 흐름을 파악하기 어렵고 시스템 관리가 어려움
- 특정 부분에서 장애가 발생했을 경우, 연결된 모든 어플리케이션을 확인해야하기 때문에 조치 시간 증가
- HW교체 / SW 업그레이드 시 관리포인트가 늘어나고 어플리케이션 별 사이드 이펙트 유무를 체크해야하기에, 작업 시간 증가
- 데이터 파이프 라인 관리의 어려움
- 각 어플리케이션과 데이터 시스템간의 별도의 파이프라인이 존재하고, 파이프라인마다 데이터 포맷과 처리 방식이 다름
- 새로운 파이프라인 추가될 경우 확장성 및 유연성이 어려움
- 데이터 불일치 가능성이 있어, 신뢰도 감소
- 이러한 문제점들이 존재하기 때문에, 새로운 시스템 개발의 필요성이 높아지고 아래와 같은 목표를 가지고 시스템 개발
- 모든 시스템으로 데이터를 전송할 수 있고, 실시간 처리도 가능하며, 급속도로 성장하는 서비스를 위해 확장이 용이한 시스템 개발
- 이렇게, 모든 어플리케이션에 대한 이벤트 및 데이터의 흐름을 중앙관리를 할 수 있는 Kafka를 개발
Kafka 적용 이후

- 모든 어플리케이션의 이벤트 및 데이터의 흐름을 중앙관리시스템으로 처리가 가능해짐
- 새로운 서비스/시스템이 추가되도, 카프카가 제공하는 표준 포맷형태로 연결이 되고, 데이터가 흐르므로, 확장성과 신뢰성 증가
- 개발자는 각 서비스간의 연결이 아닌, 비즈니스 로직에 더 신경쓸 수 있음
Kafka 동작 방식
- Kafka는 Publish <-> Subscribe모델의 메시지큐 형태로 동작
- 메시지 큐(Message Queue)
Kafka 구조

TOPIC
- 카프카 클러스터의 브로커에서 데이터를 관리할 때 기준이 되는 개념
- 어떤 종류의 데이터를 관리를할지에 대한 주제
- 토픽은 Leader Partition / Follwer Partition으로 구성될 수 있음
Partition
- Partition은 Topic내에서 데이터를 분할, 저장 및 병렬 처리를 가능하게 하는 핵심 단위이다.
역할
- 데이터 분할
- 하나의 토픽에 저장되는 모든 메시지를 여러 파티션으로 분할라여 저장함으로써, 데이터를 분산시키고, 동시에 여러 브로커에 분산 저장할 수 있다.
- 병렬 처리
- 각각의 파티션은 독립적으로 메시지를 읽고 쓸 수 있기 때문에 여러 컨슈머가 서로 다른 파티션의 데이터를 병렬로 처리할 수 있다. 이는 전체 시스템의 처리량을 크게 향상시킨다.
- 순서 보장
- 각 파티션 내에서는 메시지가 기록된 순서대로 정렬되며, 오프셋이라는 고유 번호로 관리된다. 이로 인해 동일 파티션 내에서는 순서가 보장되지만, 여러 파티션간에는 전체 순서를 보장하지 않는다.
구조
- 로그(append-only log)
- 파티션은 변경 불가능한 로그 형태로 메시지를 저장한다. 메시지는 생성 순서대로 추가되며 한 번 기록된 메시지는 수정되지 않는다.
- 오프셋(Offset)
- 각 메시지는 파티션 내에서 고유한 오프셋을 부여받는다. 이 오프셋을 통해 컨슈머는 이전에 읽은 위치를 추적하고, 장애 복구 시 해당 위치부터 데이터를 이어서 처리할 수 있다.
- 세그먼트 파일
- 파티션의 로그는 일정 크기나 시간 기준으로 세그먼트 단위로 분할되어 저장된다. 세그먼트 파일은 주기적으로 롤오버되어, 오래된 세그먼트는 설정된 보존정책에 따라 삭제될 수 있다.
- 복제 및 내결함성
- 파티션은 내결함성을 위해 복제된다. 각 파티션에는 하나의 리더와 여러 팔로워 파티션이 있으며, 리더는 쓰기 및 읽기 작업을 담당하고, 팔로워는 리더의 데이터를 복제하여 장애 발생 시 리더를 대체할 수 있다.
- 파티셔닝 전략
- 프로듀서가 메시지를 전송할 때 특정 키를 기반으로 파티션이 선택되거나, 라운드 로빈 방식으로 균등하게 분배된다. 이를 통해 데이터의 분산과 처리 부하를 조절한다.
Leader Partition
- Partition은 Producer로 부터 전달 받은 데이터를 보관하는 역할
- Leader Partition은 Producer/Consumer와 직접 통신하는 Partition
- Producer/Consumer와 직접 통신함으로써, 읽기/쓰기 연산을 담당
Follower Partition
- Producer에서 Leader Partition으로 전달된 데이터를 복제하여 복제된 데이터를 보관
- 보통 Follower Partition은 복제 데이터를 보관하는 역할을 하지만, Leader Partition이 장애가 발생했을 경우 Leader Partition의 지위를 받아 역할을 수행할 수 있음
Segment
- Segment
- 메시지 정보들을 일정한 크기로 나눠 관리하는 파일이다.
- 메시지를 저장하는 물리적 단위이다.
- 여러 세그먼트가 하나의 토픽의 파티션에 대해 로컬 디스크에 저장된다.
- Record
- 카프카에서 전송되는 메시지 데이터의 최소 단위이다.
- 각 레코드는 특정 토픽과 파티션에 속하며, 세그먼트 내에 저장된다.
- Consumer가 읽어서 처리하는 메시지, 즉, consumer는 Record 오프셋을 통해, 메시지 정보를 찾는다.

Kafka에서 가장 작은 파일 단위이며, 메시지를 안전하게 저장하는 단위이다.
- 역할 및 특징
- 데이터 저장 단위: Segment는 Kafka 파티션 내의 데이터를 저장하는 기본 단위이다. 각 Segment파일은 일정 크기에 도달하거나, 특정 시간이 경과하면 새로운 세그먼트 파일로 전환된다.
- 효율적인 데이터 관리: Segment 기반의 데이터 관리는 Kafka가 불필요한 데이터를 효율적으로 정리하고, 디스크 공간을 최적화하는데 도움을 준다.
- 로그 컴팩션 및 삭제: Kafka는 설정에 따라 오래된 Segment 파일을 삭제하거나, 로그를 컴팩션(중복 제거)하는 방식으로 데이터를 관리한다. 이는 저장 공간을 절약하고, 시스템의 성능을 유지하는데 중요하다.
- 동작 방식
- Producer는 카프카로 메시지를 전달한다.
- 전달받은 메시지는 브로커로 전달되고, 내부 파티션을 통해 저장된다.
- 파티션은 세그먼트 파일에 index파일 log파일, timestamp파일을 저장한다.
- 파일형식: 00000000000000000000.log
- 세그먼트의 오프셋을 기준으로 파일이름을 지정한다.
- 구성
- index파일: 메시지(레코드) 오프셋과 실제 물리적 위치(바이트)를 가지고있는 파일이다.
- 예. 10000 오프셋, 위치 0바이트 일경우 10000의 log파일에 메시지 정보가 0~n바이트까지 저장되어 있다라고 보면된다.
- log파일: 실제 메시지 데이터가 담긴 파일이며. 데이터는 Record 단위형태로 저장되어 있다.
- timeindex: 메시지의 타임스탬프를 기록하는 파일이다.
- 즉, 파티션이 메시지정보를 관리한다는 것은 레코드 오프셋 정보를 관리한다는 의미이다.

Segment Record 구성
Partition에는 아래의 형태를 가진 Record형식으로 저장된다.

- Header
- 메시지의 메타 데이터 정보 목록
- Key-Value 데이터를 추가할 수 있다.
- 기본적으로 카프카의
Confluent Schema Registry에서 스키마 ID를 부여한다.
스키마 ID가 필요한 이유
- 스키마 버전 관리
- 카프카는 각 타입별 스카마를 ID별로 관리하고 있으며, 동일한 스키마를 여러 번 등록해도 ID는 고정되어 유일하게 식별된다. 스키마가 변경될 때마다 새로운 버전의 스키마가 등록되고, 이전 버전의 스키마와 구분될 수 있다.
- 데이터 직렬화 / 역직렬화
- 프로듀에서 메시지 데이터를 직렬화할 때 스키마 ID를 포함하여 직렬화한다. 이를 통해 컨슈머에서 역직렬화를 할 때 메시지 데이터의 4바이트 만큼의 스키마 ID를 취득하여 데이터 타입에 맞는 형태로 역직렬화를 한다. 이를 통해 효율적인 메시지를 관리할 수 있고, 호환성이 높일 수 있다.
- Timestamp
- 시간 값을 저장한다.(Unix Timestamp)
- 디폴트
- ProducerRecord 생성시간(Create Time)
- BrokerLoading 시간으로도 설정이 가능하다.
- Topic 단위로 설정한다.
- Key
- Key를 분류하기 위한 목적이다.
- 메시지 키는 Prititioner에 의해 Topic의 Partition 번호로 지정한다.
- 키 값이 없으면 null이다.
- Round Robbin 방식이다.
- Value
- 실제 메시지 데이터가 들어가는 곳이다.
- Float, ByteArray, String 지정이 가능하다.
- 어떤 포맷으로 직렬화되었는지 Consumer는 알지 못한다.
- 보통 String으로 직렬화, 역직렬화 또는 JSON형태로 한다.
- Offset
- Producer가 생성한 Record에는 존재하지 않는다.
- Broker에 적재되면서 Offset이 지정된다.
- Offset기반으로 Consumer가 처리한다.
Record 데이터를 기준으로 Consumer는 메시지를 받고, 처리한다.
Replication Factor
- 파티션의 복제 계수가 1인경우, 즉 복제가 없을 경우
- 복제 계수가 0이므로 Broker A 파티션의 데이터를 복제해서 가지고 있을 브로커 B의 파티션이 없음, 즉 Broker A에는 하나의 리더 파티션만 존재
- 브로커 A의 해당 토픽의 파티션이 3개일 경우, 3개의 파티션은 모두 리더 파티션
- 파티션의 복제 계수가 3인경우, 즉 복제 2개
- 복제 계수가 3이므로, Broker A의 파티션 데이터를 복제해서 가지고 있을 Broker B,C의 파티션이 존재
- Broker A의 파티션이 3개일 경우, A와 B, C는 각각 하나의 리더 파티션과 두개의 팔로워 파티션을 가지게 됨
Partition의 Offset 역할
카프카에서 오프셋은 각 파티션 내의 메시지에 부여되는 고유한 순차 번호인데, 이 번호는 단순히 메시지 순서를 관리하는 것을 넘어 여러 중요한 역할을 수행한다.
- 메시지 순서 보장 및 추적
- 순차적 식별자: 오프셋은 파티션 내에서 메시지가 도착한 순서를 보장하며, 각 메시지를 고유하게 식별할 수 있도록 합니다.
- 컨슈머 위치 추적: 컨슈머는 현재 읽고 있는 메시지의 오프셋을 기반으로 어디까지 처리했는지 추적할 수 있다. 이는 컨슈머가 중단된 지점부터 다시 읽기를 할 수 있게 해준다.
- 재처리 및 장애 복구
- 재시작 및 리플레이: 만약 컨슈머가 장애를 겪거나, 재시작할 경우, 마지막 커밋된 오프셋부터 다시 읽기를 시작할 수 있다. 이로 인해 메시지 손실 없이 안정적인 처리가 가능하다.
- 정확한 컨슈머 위치 확인: 컨슈머의 진행 상황을 외부 저장소에 커밋하여, 동일한 메시지를 중복 처리하지 않도록 도와준다.
- 모니터링 및 성능 관리
- 컨슈머 랙 측정: 각 컨슈머가 마지막으로 커밋한 오프셋과 파티션의 최신 오프셋 차이의 값을 통해, 메시지 처리 지연을 모니터링할 수 있습니다. 이는 시스템 성능과 부하를 관리하는 데 중요한 지표로 작용한다.
- 분산 처리: 여러 컨슈머가 같은 파티션의 다른 오프셋 범위를 읽음으로써, 전체 데이터의 분산 처리 및 부하 분산이 가능해진다.
Broker
- Broker는 카프카의 서버를 뜻함
- Broker 내부에는 여러 개의 토픽이 생성될 수 있고, 이러한 토픽들에 의해 생성된 파티션들이 가지고 있는 데이터에 대해 분산 저장을 하거나, 장애가 발생했을 경우 안전하게 데이터를 사용할 수 있게 함
Controller
- Controller는 카프카 클러스터의 여러 개의 브로커 중 하나의 브로커가 역할을 함
- 카프카 클러스터의 브로커가 장애로 인하여 사용할 수 없을 경우, 장애가 발생한 브로커의 토픽에 있는 리더 파티션을 같은 클러스터 내의 정상 브로커에, 토픽의 리더 파티션을 재분배하는 역할을 함
- 이때, Controller의 역할을 하는 브로커에 장애가 발생했을 경우에는 다른 브로커가 Controller의 역할을 담당
Coordinator
- Coordinator는 카프카 클러스터의 여러 브로커 중 하나의 브로커가 Coordinator 역할을 하게 되며, 컨슈머 그룹의 상태를 체크하여, 그룹 내의 컨슈머가 장애가 발생하여 매칭된 파티션의 데이터를 Consume할 수 없는 경우 장애가 발생한 컨슈머에게 매칭된 파티션을 정상 동작하는 다른 컨슈머에게 매칭하는 역할을 담당
- 파티션을 컨슈머에게 재할당하는 과정을 ReBalance라고 함
Cluster
- 브로커 즉, 카프카 서버로 이루어진 집합체를 의미
Zookeeper
- 카프카의 분산 처리 시스템의 서버들에 관한 메타 데이터 즉, 환경 설정 등을 통합 관리하는 시스템
- 카프카 클러스터를 구성하는 브로커들 간에 공유되는 데이터를 유지하거나, 조율을 하기 위해 사용
- Zookeeper
- 설정 관리(Configuration Management)
- 클러스터이의 설정 정보를 최신으로 유지하기 위한 시스템
- 클러스터 관리(Cluster Management)
- 브로커가 추가되거나, 제외될 때 그 정보를 클러스터 내부의 브로커들이 공유하는데 사용
- Zookeeper Ensemble
- Zookeeper의 클러스터를 Ensemble이라고 불림
Producer & Partitioner
- Producer
- 메시지를 Porduce(생산)해서 브로커의 토픽으로 메시지를 보내는 역할을 담당하며, 어플리케이션이나 서버 모두를 Producer라고 부름
- Producer는 데이터를 전송할 때 리더 파티션을 가지고 있는 카프카 브로커와 직접 통신
- Partitioner
- Partitioner는 프로듀서 어플리케이션 내에서 생성된 메시지를 카프카에 보낼 때, 메시지가 토픽의 어떤 파티션에 전달될 지를 정하는 역할
- Partitioner는 파티션이 있는 곳에서 동작하는 것이 아니라, 프로듀서 어플리케이션 내에서 동작되며, Producer API를 사용하는 경우, 2개의 Partitioner(RoundRobinPartitioner, UniformStickyPartitioner)를 제공
- 두 Partitioner는 둘 다 메시지 Key가 있을 때는 메시지 Key의 hash값과 파티션을 매칭하여 데이터를 전송한다는 공통점을 가짐
- 메시지의 Key가 없을 경우 파티션에 메시지를 분배하는 점에서는 차이점이 존재
- Partitioner 종류
- RoundRobinPartitioner
- 카프카 클라이언트 LIB 2.4.0버전에서 파티셔너를 지정하지 않은 경우 기본 Partitioner의 역할을 담당
- 메시지가 발생하는대로 파티션을 순회하면서 전송하기에, 묶이는 빈도가 적음
- 여러번 브로커와 통신이 발생하므로 높은 리소스 사용률을 가진다는 단점이 존재
- UniformStickyPartitioner
- 카프카 클라이언트 LIB 2.5.0버전에서 파티셔너를 지정하지 않을 경우 기본 파티셔너로 지정
- RoundRobinPartitioner의 단점인 브로커와의 잦은 통신을 해결하기 위해, 데이터가 배치로 모두 묶일 때까지 기다린 뒤, 묶여진 데이터 덩어리를 모두 동일한 파티션에 전송함으로써 높은 처리량과 낮은 리소스 사용률을 가짐
Consumer
- Consumer는 토픽의 파티션에 저장되어 있는 메시지를 소비(Consume)하는 역할을 담당하며, 어플리케이션이나 서버 모두를 Consumer라고 불림
- Consumer는 데이터를 요청할 때 리더 파티션을 가지고 있는 브로커와 통신
- 토픽의 파티션으로부터 데이터를 가져가기 위해 Consumer를 운영하는 방법은 2가지이며, 토픽의 특정 파티션만 구독하는 Consumer를 운영하는 방법과 1개 이상의 Consumer로 이루어진 Consumer그룹을 운여하는 방법이 존재
Consumer Group
- 컨슈머들 간 논리적으로 묶인 그룹을 의미한다.
그룹을 하는 이유는 아래와 같다
- 병렬처리
- 같은 그룹에 속하 컨슈머들이 파티션을 나눠 갖고 동시에 처리할 수 있어 처리량이 늘어난다.
- 내결함성
- 그룹 내에서 하나의 컨슈머가 서버 다운이 발생했을 경우, 남은 컨슈머가 자동으로 해당 파티션을 인계 받아 데이터를 읽는다.
- 순서 보장
- 파티션은 기본적으로 한 컨슈머만 할당하여 폴링하기 때문에, 해당 파티션 안에 있는 메시지 데이터 순서가 보장된다.
- 독립적 소비
- 서로 다른 group ID를 쓰는 여러 그룹이 같은 토픽을 구독해도, 각 그룹은 자기만의 오프셋을 관리한다.
- 예) 컨슈머 A그룹: 처음부터 끝까지 모든 메시지 데이터, 컨슈머 B그룹: 최신 메시지 데이터만
- 토픽의 파티션을 동일 Consumer Group내의 Consumer와 1:1 ~ n:1형태로 매칭될 수 있음
- 파티션 3개와 컨슈머 그룹 A의 컨슈머 3개일경우
- 파티션 2개와 컨슈머 그룹 B의 컨슈머 3개일경우
- 2개의 파티션은 각각 하나의 컨슈머에 할당되면서 컨슈머 그룹B에 있는 하나의 컨슈머는 어떤 파티션도 할당받지 못하는 상태가 됨
- 파티션 4개와 컨슈머그룹 C의 컨슈머 3개일 경우
- 4개의 파티션은 각각 컨슈머에 할당이 되는데, 이때 2개의 컨슈머는 각각 1개의 파티션을 할당 받으며, 남은 하나의 컨슈머는 2개의 파티션을 처리
장애 관련 처리
카프카 자체에 장애가 발생했을 경우
카프카는 분산 시스템의 특성과 내결함성 메커니즘을 통해 문제를 완화하고 복구할 수 있도록 설계되어 있다.
- 브로커 및 리더 선정
- 카프카 클러스터 내 개별 브로커에 장애가 발생했을 경우, 해당 브로커가 관리하던 파티션의 리더 역할을 다른 팔로워 파티션이 자동으로 새로운 리더로 선정되어 대체한다.
이 과정에서 컨슈머는 새로운 리더에 연결되어 마지막으로 커밋된 오프셋부터 데이터를 다시 받을 수 있다.
- 데이터 복제와 내결함성
- 카프카는 각 파티션의 데이터를 여러 브로커에 복제하여 저장한다.
- 복제본을 통해 하나의 브로커에 문제가 생겨도 데이터 손실 없이 다른 브로커에서 데이터를 제공할 수 있다.
- 클러스터가 충분히 분산되어 있다면, 전체 시스템의 장애 없이 개별 브로커의 장애를 처리할 수 있다.
- 클러스터 전체 장애
- 클러스터 전체에 문제가 발생했을 경우, 운영자는 빠른 복구를 위해 백업 데이터 혹은 미러 클러스터로 복구해야한다.
- 이경우, 컨슈머는 클러스터 복구 후 마지막으로 커밋된 오프셋부터 다시 데이터를 읽게 된다.
- 재연결 및 복구 로직
- 컨슈머와 프로듀서 모두 클러스터와 연결이 끊어지면 자동 재연결기 기능을 활용하여, 장애 발생 후 가능한 빠르게 새로운 리더 또는 복구된 브로커와 연결을 재시도한다.
파티션 장애가 발생했을 경우
카프카의 리더 파티션에 장애가 발생했을 경우 ISR(In-Sync-Replica) 목록에 포함된 팔로워 파티션 중에서 자동으로 새로운 리더 파티션으로 바뀌게 된다.
- 자동 장애 복구 과정
- 장애 감지: 카프카는 ZooKeeper를 통해 리더 노드(리더 복제본이 존재하는 브로커)의 상태를 지속적으로 모니터링한다.
- ISR(In-Sync-Replica) 확인: 장애가 발생하면, 카프카는 최신 데이터를 보유하고 있는 ISR 목록의 팔로워 파티션들을 확인한다.
- ISR(In-Sync-Replica): 리더 파티션과 완벽하게 동기화된 팔로워 파티션들의 집합을 의미한다.
- 리더 선정: ISR 목록의 팔로워 파티션 중 하나를 자동으로 새로운 리더로 선정하고, 클러스터의 정상 운영을 유지한다.
참고
카프카 세그먼트1
카프카 세그먼트2