📗카프카 개요
Kafka는 분산 스트리밍 플랫폼으로, 대규모 실시간 데이터 처리에 사용된다. 실시간 데이터 스트리밍, 로그 수집, 이벤트 소싱, 메시지 브로커로 활용한다.
🏳️🌈 [궁금한점]
🔗목차
| 구성 요소 | 설명 |
|---|---|
| Producer | Kafka로 데이터를 보내는 주체 (ex. 앱, 서비스) |
| Consumer | Kafka에서 데이터를 읽는 주체 (ex. 분석 시스템, 저장소) |
| Topic | 메시지 단위의 스트림. 카테고리 같은 개념 |
| Broker | Kafka 서버 한 대. 여러 개가 모여 클러스터를 구성 |
| Partition | Topic을 쪼갠 단위. 병렬 처리 및 확장성의 핵심 |
| Zookeeper | 클러스터 상태 관리 (최근엔 KRaft라는 Kafka 자체 메타스토어도 사용 가능) |
Data Lake에서 Kafka는 데이터 수집(ingestion)과 실시간 스트리밍 처리의 핵심 축 역할을 한다
Kafka는 일반적인 메시지 큐(MQ) 시스템들과는 철학과 설계가 다르다. MQ(예: RabbitMQ, ActiveMQ, IBM MQ)와 비교해보면 Kafka는 단순 메시징을 넘어서 데이터 스트리밍 플랫폼으로 진화하였다
| 항목 | Kafka | 전통 MQ (RabbitMQ 등) |
|-------------------|---------------------------------------------------------------------|---------------------------------------------------------------------|
| 메시지 저장 방식 | 디스크 기반 로그로 저장, 오랫동안 보관 가능 | 메모리 중심, 소비되면 삭제됨 |
| 처리 방식 | Publish-Subscribe 기반 + Replay 가능 | 일반적으로 point-to-point 또는 pub/sub, replay는 어려움 |
| 내구성 (Durability) | 디스크에 영구 저장, 복제 기능으로 장애 대응 | 보통 메모리에 저장, 퍼시스턴스 설정 필요 |
| 처리량 (Throughput) | 아주 높음 (백만 건+/초) | 중간~낮음, 대량 처리에 병목 가능 |
| 확장성 (Scalability) | 수평 확장 용이 (Partition 기반) | 브로커 수 증가 시 성능 저하 가능 |
| 복수 소비자 처리 | 같은 데이터를 여러 그룹이 독립 소비 가능 | 하나의 큐에 여러 소비자가 붙으면 부하 분산, Replay 어려움 |
| 메시지 순서 보장 | Partition 내 순서 보장 | 큐에 따라 순서 보장 안 되거나 설정 필요 |
| 지속 가능성 | 메시지를 오래 저장하며 재처리/분석 가능 | 주로 실시간 처리 후 버림 (임시적 성격) |
| 단점 | 내용 |
|---|---|
| 설정 복잡성 | 클러스터 운영에 대한 이해 필요 (ZooKeeper 또는 KRaft, Broker, Retention 등) |
| 지연시간 | 초저지연이 필요한 경우 RabbitMQ가 더 적합할 수도 있음 |
| 단순한 메시징만 필요할 땐 과함 | 고성능 스트리밍 플랫폼이지만 단순 요청-응답 시엔 오버엔지니어링 될 수 있음 |
Kafka의 리소스 한계치는 기본적으로 무제한에 가깝게 설계되어 있지만, 현실적으로는 하드웨어, 운영체제, JVM, 설정값 등에 의해 제약을 받는다.
클러스터 노드(Broker) 수
- 이론적 한계: 수백 개 가능
- 권장 범위: 3~200개
- 100개 이상은 운영 복잡성, 네트워크 비용, Controller 부하 등 고려 필요
- 최근 Kafka KRaft 모드에서는 더 높은 안정성으로 확장 가능
Topic 수
- 이론적 한계: 수십만 개 이상 가능
- 권장 범위: 수천 개
- 문제는 Topic 자체보다는 Partition 수가 핵심 병목
- Controller는 모든 Partition 메타데이터를 메모리에 유지 → 메모리/GC 부하 발생 가능
Partition 수
- 이론적 한계: 수백만 개
- 권장 범위: Kafka 클러스터당 20,000 ~ 100,000개
- 한 Broker당 4,000~10,000개 이하 권장
- 너무 많으면 Controller 메모리/CPU 부하, 리더 선출 지연, 복제 지연, 컨슈머 부하 발생
- Partition 수는 병렬성과 부하 분산에 직접 영향
- Kafka 2.8+ 기준으로 KRaft 모드 사용 시 Partition 수 증가에 유리
Consumer Group 수
- 이론상 제약은 없으나, 각 Group의 오프셋을 Kafka가 유지
- 수천 개 이상이면 __consumer_offsets 토픽이 과부하 가능
메시지 저장량
- Kafka는 디스크 기반 저장이며, 디스크만 충분하면 TB~PB 단위까지 가능
- Broker별 디스크 용량의 70~80% 이하로 유지 권장
- Retention 정책(기간/크기 기반 삭제) 적절히 설정 필요
메시지 처리량 (Throughput)
실전에서 1개 Kafka 클러스터가 초당 수백 MB~GB 이상 처리 가능
메모리/Heap 크기
- Broker당 일반적으로 8~32GB heap 설정
- Heap을 너무 크게 잡으면 GC 성능에 부정적 → 대신 OS Page Cache를 활용하는 구조
| 항목 | 권장 범위 | 비고 |
|---|---|---|
| Broker 수 | 3 ~ 200개 | 10~20개가 일반적 |
| Topic 수 | 수천 개 이하 | Partition 수가 더 중요 |
| Partition 수 | 클러스터당 20k ~ 100k | Broker당 4k ~ 10k 권장 |
| 메시지 저장 | TB ~ PB 가능 | 디스크 용량 및 Retention 설정에 따라 |
| Consumer Group | 수천 개 이하 | __consumer_offsets 부하 주의 |
| 처리량 | 초당 수백 MB ~ 수 GB | 하드웨어, 네트워크 성능에 의존 |
카프카는 수십만~수백만 TPS(초당 메시지 수)를 거뜬히 처리할 수 있다.
카프카는 메시지를 디스크에 기록하지만, 운영체제의 페이지 캐시(OS Cache)를 활용해 메모리에 올라와 있는 것처럼 읽고 쓴다. 그래서 디스크 I/O가 아니라 메모리 I/O처럼 빠르게 처리할 수 있다. 게다가 Kafka의 I/O는 순차 쓰기(Sequential Write)라서 디스크에서도 엄청 빠르다. (디스크는 랜덤 읽기/쓰기에 약하고, 순차 쓰기에 강하다.)
카프카는 Sendfile 시스템 콜을 써서 데이터를 디스크에서 네트워크로 바로 보낸다. CPU가 데이터를 메모리에 복사하지 않고 바로 소켓으로 뿌려준다. 이걸 Zero-Copy라고 부르고, 데이터 이동 오버헤드를 엄청 줄인다.
카프카는 복잡한 인덱스나 쿼리 없이, append-only 로그 구조로만 동작한다.그냥 뒤에 덧붙이고, 읽을 때는 오프셋(offset)만 알면 된다.디스크 상에서 "어디에 쓰고, 어디서 읽을지" 명확하니까 매우 빠르다.
프로듀서가 메시지를 하나씩 보내지 않고, 여러 개를 묶어서 보내기(batch) 때문에 네트워크 I/O를 절약한다.
데이터 전송 전에 압축(snappy, lz4 등)을 해서 보내기 때문에 트래픽도 줄고 속도도 올라간다.
토픽이 파티션 단위로 나뉘어 있어서, 브로커 간에 부하 분산이 잘된다. 컨슈머들도 여러 파티션을 병렬로 읽을 수 있어서, 읽기 성능이 선형적으로 확장된다.
브로커는 "누가 어디까지 읽었는지"를 기억하지 않는다. 오프셋 관리 책임은 컨슈머에게 있다. 이로인해 브로커가 훨씬 가볍고, 스케일 아웃이 쉽다.
Kafka는 메시지를 메모리에만 저장하지 않고 디스크에 쓰기 때문에, 서버가 죽더라도 데이터가 남아 있다. 토픽은 "로그 파일"처럼 계속 append-only 방식으로 저장된다. PageCache(커널 디스크 캐시) 를 이용해서 속도도 빠르게 유지된다.
Kafka는 토픽 데이터를 여러 브로커에 복제한다. (replication factor 설정). 예를 들어 replication factor가 3이면, 동일한 메시지가 3개 브로커에 존재한다. 이 때문에 브로커가 죽어도 다른 브로커가 복구할 수 있다.
프로듀서가 데이터를 보낼 때, "메시지가 저장된 걸 확인하고 응답 받는 방식" 을 지원한다.
acks 설정값:Kafka는 각 파티션에 Leader를 하나 정하고, 나머지는 Follower다. ISR(In-Sync Replicas) 목록은 현재 리더와 데이터가 일치하는 복제본들이다. 만약 리더 브로커가 죽으면, ISR 중 하나가 새 리더로 승격돼서 데이터 일관성을 유지한다. 비동기 복제지만 ISR을 통해 강한 내구성을 보장한다.
각 파티션은 독립적으로 처리된다. 프로듀서가 여러 파티션에 메시지를 동시에 보내고,
컨슈머도 여러 파티션을 동시에 읽을 수 있다. 그래서 파티션 수가 많으면 많을수록 시스템 처리량(throughput)이 높아질 수 있다.
카프카 클러스터를 구성할 때, 파티션을 여러 브로커에 분산시켜 저장할 수 있다. 그러면 브로커 개수를 늘릴 때 자연스럽게 부하가 분산돼서, 노드를 추가하는 것만으로 시스템이 커질 수 있다 (Scale-Out).
카프카는 파티션 내부에서는 메시지 순서를 보장한다. 하지만 파티션을 넘어서면 순서 보장은 안 한다. 그래서 "같은 키" (예: 같은 사용자 ID, 같은 주문번호)는 항상 같은 파티션에 매핑해서 순서를 유지할 수 있게 설계해야 한다. 카프카에서는 파티셔너(Partitioner)가 키를 기반으로 파티션을 정하는 것도 이런 이유다.
파티션을 너무 많이 만들면 오히려 오버헤드가 커진다. (메타데이터 관리, 리밸런스 부하, 파일 핸들 수 증가 등)
적절한 개수를 잡아야 한다. 보통 파티션 수 ≈ 브로커 수 × 100~200 정도에서 시작하고, 워크로드 테스트를 통해 조정한다.
| 항목 | 가이드라인 |
|---|---|
| 컨슈머 수 | 컨슈머 수보다 파티션 수가 많아야 함 |
| 브로커 수 | 브로커당 2,000~4,000 파티션 이내 |
| TPS 부하 | 파티션당 1,000~10,000 TPS 적당 |
| 확장성 | 미래 컨슈머 확장 고려해서 넉넉하게 설정 |
Consumer Group은 Kafka에서 메시지 소비자(Consumer) 를 관리하고 메시지 분배 방식을 결정하는 중요한 개념이다. 기본적으로 Kafka에서는 하나의 Consumer만 메시지를 받는 게 아니라, 여러 Consumer가 같은 그룹에 속하면 각 Consumer가 파트ition을 나눠서 처리하도록 한다.
Kafka에서 Consumer Group은 여러 개의 Consumer가 하나의 그룹을 형성하여, 특정 토픽의 메시지를 공유해서 처리하는 구조를 말한다.하나의 Consumer Group 안에 여러 Consumer가 있을 수 있고, 이 Consumer들은 동일한 group.id를 설정하여 같은 그룹에 속하게 된다.
각 Consumer는 토픽의 파티션을 할당받아서 독립적으로 메시지를 소비한다.
Kafka는 각 파티션에 있는 메시지를 소비자들에게 분배하는 방식을 다음과 같이 처리한다
Kafka를 쿠버네티스(Kubernetes) 위에 올릴지, 아니면 VM이나 베어메탈 환경에 별도 설치할지는 운영 환경의 요구사항, 안정성, 복잡도, 팀의 숙련도에 따라 달리해야 한다.
장점
- 인프라 자동화 통합: Kafka도 다른 워크로드처럼 Helm, ArgoCD 등으로 일관성 있게 배포/운영 가능
- CI/CD 파이프라인과 통합 용이
- 리소스 제어 용이
- CPU/MEM 제한, QoS, Node Affinity 등 쿠버네티스 기능 활용 가능
- Auto-healing, Rolling Update 등
- StatefulSet + PV 구조로 장애 자동 복구, 버전 관리도 수월
- Kafka + Kafka Connect + Schema Registry + Monitoring 통합 운영
- Strimzi, Confluent Operator 등을 사용하면 전체 Kafka 생태계 배포가 쉬움
단점
- 스토리지 민감도: Kafka는 디스크 I/O 성능이 중요한데, 쿠버네티스의 일반적인 PVC는 성능이 낮을 수 있음 (특히 NFS 등 네트워크 스토리지), 로컬 SSD, PowerFlex, EBS io1/io2 같은 고성능 스토리지가 필요
- 복잡성: Kafka는 네트워크, 스토리지, 컨트롤 플레인 등 종합적인 고려가 필요한 시스템, 쿠버네티스 위에 올리면 클러스터 설계 복잡도 증가
- 노드 재스케줄링 주의: Kafka Pod가 죽었다 다시 올라오면 리더 선출, 복제 지연 등 발생
- 노드 재스케줄링 정책과 조화 필요
장점
- 최고의 안정성과 성능
- OS, 디스크, 네트워크를 Kafka 최적화에 맞춰 직접 튜닝 가능
- 대규모 환경에서 성능 예측과 보장이 용이
- 운영 노하우 축적된 방식
- 대기업/금융권 등에서 Kafka 운영 노하우는 대부분 VM/Bare-metal 기반
- 스토리지 문제 적음
- 로컬 디스크 성능 활용 가능 → Throughput과 Latency 면에서 유리
단점
- 운영 자동화 어려움
- 쿠버네티스의 Self-healing, CI/CD 파이프라인을 쓰기 어렵고 수작업 많음
- 리소스 분리 어려움
- 워크로드 단위의 리소스 제어(CPU/MEM QoS 등) 어려움
- Kafka 이외 리소스 통합 어려움
- Kafka Connect, Schema Registry, Flink 등과 연동 시 개별 배포 필요
개발/테스트 환경, 마이크로서비스용 Kafka, 중간 규모 스트리밍 처리는 쿠버네티스 + Strimzi (혹은 Confluent Operator)로 한다.
초대규모 실시간 처리 (TB급 메시지, 수백만 Partition), 초고가용성 요구, 금융/이커머스 핵심 서비스는 VM 또는 Bare-metal에 설치하고 성능 튜닝을 진행한다.
| 기준 | 쿠버네티스 배포 | 베어메탈 |
|---|---|---|
| 팀의 쿠버네티스 숙련도 | 높음 | 낮음 |
| Kafka 처리량/성능 민감도 | 낮~중간 | 높음 (초당 수백 MB 이상) |
| 스토리지 성능 (로컬SSD, 고성능 블록) | 있음 | 있음 |
| 운영 자동화와 통합 중요도 | 높음 | 낮음 |
| 복잡성 감수 가능 여부 | 가능 | 불가능 |
| Kafka 고가용성(H/A) 직접 설계 가능 | 쿠버네티스 지원 (StatefulSet 등) | OS단에서 직접 구성 |
kafka-topics.sh --bootstrap-server <broker:port> --describe --topic my-topic
Topic: my-topic PartitionCount:3 ReplicationFactor:2
Partition: 0 Leader: 1 Replicas: 1,2 Isr: 1,2
kafka-consumer-groups.sh --bootstrap-server <broker:port> \
--describe --group <consumer-group-id>
각 Consumer의 현재 offset, lag, 토픽/파티션 별 처리 현황 확인 가능, Lag이 쌓이면 컨슈머가 밀리고 있다는 뜻
docker run -d -p 9000:9000 \
-e KAFKA_BROKERCONNECT=<broker:port> \
obsidiandynamics/kafdrop
https://github.com/provectus/kafka-ui
https://github.com/danielqsj/kafka_exporter
복제 팩터 replication.factor >= 2 이상이면 다른 브로커에 복제본이 있다. 이 경우 장애 브로커를 클러스터에서 제외하고, 필요하면 새 브로커를 추가한다. 리더 선출이 자동으로 이루어져서 정상 서비스는 이어갈 수 있다.
만약 복구가 필요하면 장애 브로커를 살리고 (혹은 새 브로커를 붙이고), kafka-reassign-partitions 툴을 이용해서 파티션을 다시 균등하게 분산시킨다.
retention.ms(보존 시간)나 retention.bytes(보존 용량)을 넘으면 데이터를 자동 삭제한다. 한 번 삭제된 메시지는 카프카 내부에서는 복구 불가하다. 삭제된 걸 복구하려면 외부에 따로 저장해둔 백업이나 아카이빙 시스템이 있어야 한다 (예: S3에 메시지 저장하는 시스템).
오프셋을 잘못 관리해서 메시지를 놓쳤을 경우에는 컨슈머 그룹을 새로 만들거나 오프셋을 수동으로 조정해서 재처리할 수 있다. kafka-consumer-groups --reset-offsets 커맨드를 쓰면 된다.
카프카 데이터 복구를 대비한 "운영 전략"은 보통 이렇게 준비한다
운영 환경에서는 모니터링(Prometheus+Grafana 조합 많이 씀)과 알람 설정이 중요하다. 죽지 않고 오래 돌게 하는 것이 가장 어렵고, 가장 큰 가치를 가져온다.
브로커 다운이나 네트워크 장애에 민감하다. CPU, 메모리, 디스크 I/O, 네트워크 트래픽을 꾸준히 모니터링해야 한다.
브로커 간 리플리케이션 레이턴시(lag)도 체크해야 한다.
카프카는 디스크 풀(pool) 쓰듯이 계속 데이터를 저장한다. 로그 세그먼트가 삭제되지 않으면 디스크가 가득 찰 수 있고, 가득 차면 브로커가 멈춘다. retention.ms(보관 기간)나 retention.bytes(보관 크기) 세팅을 잘 잡아야 한다.
파티션이 너무 많으면(특히 수만 개 이상) 컨트롤러나 Zookeeper, 클라이언트가 힘들어진다. 반대로 너무 적으면 병렬처리가 어렵고 성능이 떨어진다. 브로커 수 × 100~1000 개 정도를 권장하는 경우가 많다 (워크로드에 따라 다르다).
acks=all로 두지 않으면 데이터 유실 위험이 있다. linger.ms, batch.size를 조정해 퍼포먼스 최적화할 수 있다.
max.poll.interval.ms 값을 잘 조정해야 한다. (너무 길거나 짧으면 컨슈머 그룹 리밸런스가 자주 발생할 수 있다.)
오프셋 커밋(auto.commit)을 전략적으로 관리해야 한다. (수동 커밋 권장)
Zookeeper를 쓰는 경우, ZK 장애로 인해 카프카 클러스터 전체가 불안정해질 수 있다. 그래서 Zookeeper도 HA 구성, 모니터링이 필수이다. (카프카 KRaft 모드(Kafka Raft)로 넘어가면 Zookeeper를 안 써도 된다.)
토픽이나 파티션 수 변경 후 리밸런스가 일어날 때 부하가 크다. 운영 시간대에는 웬만하면 안 하는 걸 추천한다.
클러스터 간 통신(브로커↔브로커, 클라이언트↔브로커) 암호화 (SSL/TLS), 인증/권한 부여(SASL, ACL)도 고려해야 한다.
업그레이드할 때 반드시 롤링 업그레이드(한 대씩)로 진행해야 한다. 버전 간 호환성(프로토콜 호환성)을 미리 체크해야 한다.