Kafka 동작 원리: 큐가 아닌 분산 커밋 로그

2025. 7. 20. 22:58·Apache Kafka
반응형

시작하며


안녕하세요. 개발자 Stark입니다. 

최근 저는 MSA 프로젝트에 분산추적을 적용하기 위해 Jaeger와 Opentelemetry라는 오픈소스의 소스코드를 하나하나 뜯어보며 분석하고 있습니다. 이렇게 오픈소스를 분석하는 과정을 진행하면서 저의 코드를 보는 시야가 이전과는 많이 달라진 것을 느낄 수 있었습니다. 특히 분석하면서 이 오픈소스가 실제로는 어떻게(how) 동작하는지에 대한 것들을 알 수 있다는 것이 너무 좋았습니다.

 

이 과정을 겪으면서 앞으로 내가 사용하는 오픈소스는 꼭 분석하고 사용하겠다는 마음을 다지며 다음 분석 타겟은 뭐로 할지 고민하던 도중 프로젝트에서 이미 사용 중인 Apache Kafka의 상세한 동작 원리를 알아야겠다는 생각이 들었습니다. 지금까지 SpringBoot에서 kafka-starter 라이브러리가 지원하는 produce, consume 기능만 사용해 봤지 kafka 내부에서는 실제로 어떻게 동작하는지 고민하거나 분석해 본 적이 없었습니다. 그래서 이번 포스팅에서는 Kafka의 동작 원리에 대해 깊이 있게 파헤쳐보고자 합니다. 다들 재미있게 봐주세요!

 

참고: 이번 포스팅은 Apache Kafka의 공식 문서를 기준으로 많은 내용들이 작성되었습니다. 또한 기본적인 kafka 지식 (Topic, Partition, Segment, offset) 등의 개념을 알고 계신 독자분들을 위해 작성하였습니다.

 

Local에서 Kafka 실행하기

2024.08.25 - [Apache Kafka] - [kafka] Docker로 카프카 실행하기 (KRaft 모드)

 

[kafka] Docker로 카프카 실행하기 (KRaft 모드)

Docker를 통해 Kafka를 실행해 보자📌 서론이전에도 kafka 세팅에 대해 포스팅을 한 적이 있지만 가장 최근에 새롭게 세팅을 하면서 알게 된 점들을 정리해 봤다.특히 port를 설정할 때 내부, 외부로

curiousjinan.tistory.com

 

제가 처음 Kafka에 대해서 공부할때는 Message Queue의 한 종류라고 생각해 왔습니다. 그런데 직접 사용하고 공부할수록 정말 MQ가 맞는지 고민이 되었습니다. 왜냐하면 Kafka를 단순 Queue라고 보기엔 제가 생각했던 것보다 사용 방식이 훨씬 유연했고 Produce, Consume 구조로 되어있으며 Message를 계속해서 순서대로 보관할 수 있었기 때문입니다. (In-Memory 보관은 아닌 것 같다는 생각을 했습니다.)

 

Java에서 직접 queue를 구현해서 사용한다고 생각해 보면 Topic, Partition 이런 것들을 정말 Java만으로 가능하게 할 수 있을까? 이런 고민이 많이 들었습니다. 또한 Kafka는 메시지를 영구저장하는데 이것을 위해 전용 DB를 사용하는 것인가? 그렇다면 왜 Kafka 브로커를 서버에 띄울 때 DB관련된 연결은 본 적이 없는 것 같지? 이런 생각도 들었습니다. 그래서 저는 Kafka가 큐인지 열심히 알아봤고 그 결론은 다음과 같습니다.

 

 

Kafka는 메시지 큐(Message Queue)일까?


Apache Kafka는 단순한 메시지 큐가 아닌 분산되고 파티션되며 복제되는 커밋 로그(commit log) 시스템입니다. 전통적인 메시지 큐(ex: RabbitMQ, ActiveMQ)는 일반적으로 소비자가 메시지를 성공적으로 처리하고 나면 큐에서 해당 메시지를 제거합니다. 데이터는 처리를 위해 잠시 머무는 '대기열'의 성격이 강합니다.

반면 Kafka는 모든 메시지를 커밋 로그(Commit Log)라는 데이터 구조를 사용해 파일 시스템에 순차적으로 기록하고 설정된 기간 동안 보존합니다. 소비자가 메시지를 읽어가도 로그에서 데이터는 삭제되지 않습니다. 소비자는 단지 어디까지 읽었는지를 나타내는 오프셋(offset) 위치만 기억할 뿐입니다.

kafka와 전통 message queue
kafka와 전통 message queue

핵심 차이점: 커밋 로그 아키텍처

Kafka는 메시지 데이터를 외부 데이터베이스에 저장하지 않습니다. 대신, 각 브로커가 자체 서버의 파일 시스템에 직접 로그 데이터를 기록합니다. 이 '커밋 로그' 아키텍처는 다음과 같은 특징을 가집니다.

 

1. 데이터의 영속성 및 불변성
전통적인 큐와 달리, Kafka는 소비자가 메시지를 읽어 가도 데이터를 삭제하지 않습니다. 데이터는 설정된 보존 기간(retention period) 동안 디스크에 안전하게 보관됩니다. 이는 데이터가 일회성으로 소비되고 사라지는 것이 아니라, '사실의 기록(record of facts)'으로서 언제든 다시 사용할 수 있음을 의미합니다.

데이터 영속성 및 불변성
데이터 영속성 및 불변성

2. 소비자 중심의 상태 관리
전통적인 큐는 브로커가 어떤 메시지를 누구에게 전달했고, 무엇을 삭제할지 관리해야 하는 부담이 있습니다. 하지만 Kafka에서는 브로커가 단순히 데이터를 로그에 쌓기만 합니다. 메시지를 어디까지 읽었는지를 나타내는 오프셋(offset)의 관리는 전적으로 소비자(Consumer)의 책임입니다. 이 덕분에 브로커는 매우 단순하고 효율적으로 동작할 수 있으며, 각기 다른 소비자 그룹이 서로에게 아무런 영향을 주지 않고 독립적으로 로그를 읽을 수 있습니다.

소비자 중심의 상태 관리
소비자 중심의 상태 관리

'커밋 로그' 설계가 제공하는 기능

Kafka는 일반적인 큐가 제공하기 어려운 차별화된 기능을 가집니다.

  1. 데이터 리플레이 (Data Replay) & 내구성
    로그에 데이터가 설정된 보존 기간 동안 디스크에 안전하게 유지되므로, 소비자는 언제든지 과거의 특정 시점으로 돌아가 데이터를 다시 처리(Replay)할 수 있습니다. 애플리케이션 장애 복구나 새로운 분석 시스템 도입 시 과거 데이터 전체를 다시 읽어오는 등의 작업이 가능합니다.

  2. 다중 구독 및 유연한 소비 모델
    하나의 토픽(Topic) 데이터를 여러 목적을 가진 소비자 그룹들이 동시에 구독할 수 있습니다. 예를 들어, 동일한 주문 데이터 스트림을 실시간 재고 관리팀, 사기 탐지 시스템, 데이터 분석을 위한 데이터 레이크 적재팀이 각자의 속도에 맞춰 독립적으로 소비할 수 있습니다.

  3. 높은 처리량과 순서 보장
    데이터를 디스크에 순차적으로만 기록(Sequential I/O)하고 OS의 페이지 캐시를 효율적으로 활용하므로 데이터 양에 관계없이 매우 높은 처리량을 유지합니다. 또한, 파티션(Partition)이라는 단위 내에서는 메시지의 순서가 엄격하게 보장되어, 이벤트 발생 순서가 중요한 비즈니스 로직을 안정적으로 구현할 수 있습니다.

따라서 Kafka를 한마디로 정의하면 다음과 같습니다.

따라서 Kafka는 메시지를 지워나가는 '큐'가 아니라, 필요에 따라 과거 기록을 다시 읽고 여러 시스템이 같은 로그 스트림을 동시에 활용할 수 있는 '공유 가능한 사실의 기록(shared record of facts)'입니다.

 

 

토픽과 파티션: 분산 로그의 기본 단위


Kafka에서 토픽(Topic)은 데이터가 흐르는 통로의 논리적인 이름입니다. 데이터베이스의 테이블이나 파일 시스템의 폴더처럼, 특정 종류의 메시지들을 구분하기 위해 사용됩니다. 이러한 토픽의 실체이자, Kafka 아키텍처의 가장 중요한 핵심 구성요소는 바로 파티션(Partition)입니다.

 

1. 파티션: 순서가 보장되는 불변의 로그

모든 토픽은 하나 이상의 파티션으로 구성됩니다. 각 파티션은 그 자체로 하나의 독립적인 로그(Log) 파일입니다. 이 로그는 다음과 같은 특징을 가집니다.

  • Append-Only: 새로운 메시지(레코드)는 항상 로그의 끝에만 추가됩니다.
  • Immutable: 한번 기록된 메시지는 수정되거나 개별적으로 삭제되지 않습니다. (오직 보존 기간 정책에 따라 오래된 로그 세그먼트 전체가 삭제될 뿐입니다.)
  • Ordered: 파티션 내에서 메시지는 추가된 순서대로 저장되며, 이 순서는 절대 변하지 않습니다.

이처럼 파티션은 데이터가 순서대로 쌓이는 커밋 로그(Commit Log) 자료구조 그 자체입니다.

 

2. 오프셋: 파티션 내 메시지의 고유 식별자

Kafka는 파티션에 저장되는 각 메시지에 오프셋(Offset)이라는 고유 번호를 부여합니다. 이 오프셋은 파티션 내에서 각 메시지에 부여되는 순차적인 정수 번호 (0, 1, 2,...)입니다. 이는 파티션 내에서 메시지의 절대적인 순서를 나타내는 식별자 역할을 합니다.

소비자는 이 오프셋을 통해 특정 파티션의 메시지를 어디까지 읽었는지 기억합니다. 예를 들어, 한 소비자가 파티션 0의 150번 오프셋까지 처리했다면, 다음에는 151번 오프셋부터 메시지를 요청하여 중복이나 누락 없이 소비를 이어갈 수 있습니다. 이 오프셋은 소비자 그룹별로 독립적으로 관리되므로, 여러 다른 소비자들이 각자의 속도에 맞춰 동일한 토픽을 소비할 수 있습니다.

 

3. 확장성과 병렬 처리의 열쇠

파티션은 Kafka의 확장성과 병렬 처리를 위한 핵심 요소입니다.

  • 데이터 분산
    하나의 토픽을 여러 파티션으로 나누면, 각 파티션을 클러스터 내의 서로 다른 브로커(서버)에 분산하여 저장할 수 있습니다. 이를 통해 단일 서버의 디스크 용량이나 처리 능력의 한계를 넘어 토픽의 크기를 수평적으로 확장할 수 있습니다.

  • 병렬 소비
    소비자 그룹 내의 소비자들은 토픽의 각 파티션에 하나씩 할당되어 메시지를 병렬로 처리할 수 있습니다. 예를 들어, 4개의 파티션을 가진 토픽은 최대 4개의 소비자가 동시에 작업하여 전체 처리량을 극대화할 수 있습니다.

 

4. 내구성과 고가용성: 복제와 리더-팔로워 모델

Kafka는 파티션 단위로 데이터를 복제(Replication)하여 데이터의 내구성과 시스템의 고가용성을 보장합니다.

  • 리더(Leader)와 팔로워(Follower)
    각 파티션은 하나의 리더와 여러 개의 팔로워를 가집니다. 모든 읽기/쓰기 요청은 리더 파티션을 통해서만 처리됩니다. 팔로워들은 리더의 로그를 그대로 복제하여 동일한 데이터를 유지합니다.

  • 장애 극복(Failover)
    만약 리더 파티션이 있는 브로커에 장애가 발생하면, 팔로워 중 하나가 새로운 리더로 자동 선출됩니다. 이 과정을 통해 서비스 중단 없이 메시지 처리를 지속할 수 있으며, 데이터 유실을 방지합니다.

 

요약하자면, Kafka의 토픽은 물리적으로 분산되고 복제된 다수의 커밋 로그(파티션)의 집합입니다.

각 파티션이 순서가 보장되는 독립적인 로그로 동작하고, 소비자가 오프셋을 통해 읽기 위치를 자유롭게 제어할 수 있다는 점이 Kafka를 단순한 메시지 큐와 구별하는 가장 근본적인 설계 철학입니다. 이 구조 덕분에 Kafka는 대용량 실시간 데이터 스트리밍에 최적화된 강력한 성능과 안정성을 제공할 수 있습니다.

 

 

로그 세그먼트와 저장 구조: 파일로 쌓이는 커밋 로그


Kafka 브로커는 각 파티션의 로그를 디스크 파일에 직접 저장합니다. 한 파티션당 하나의 디렉터리가 할당되고, 그 안에 여러 개의 세그먼트(segment) 파일로 로그를 나누어 관리합니다. 로그가 계속 길어지므로, 파일 하나에 무한정 쓰지 않고 일정 크기나 기간 단위로 잘라 여러 파일로 나누어 효율성을 높이는 것입니다.

 

제 로컬 Docker 환경을 기준으로 실제 로그 파일이 어떻게 저장되는지 확인해 보겠습니다.

  • 먼저 docker ps 명령어로 실행 중인 Kafka 컨테이너의 ID나 이름을 확인한 후, docker exec 명령어로 컨테이너 내부에 접속합니다.
# 실행 중인 컨테이너 목록 확인
docker ps

# 저는 아래와 같은 결과를 얻었습니다. 이 중 kafka를 사용할 것입니다.
CONTAINER ID   IMAGE                           COMMAND                  CREATED        STATUS       PORTS                                              NAMES
c1ad61a409a1   postgres:13                     "docker-entrypoint.s…"   3 months ago   Up 4 weeks   0.0.0.0:5432->5432/tcp                             shoot-postgres
0017a4687e2e   provectuslabs/kafka-ui:latest   "/bin/sh -c 'java --…"   4 months ago   Up 4 weeks   0.0.0.0:8085->8080/tcp                             shoot-KafkaWebUiContainer
2372e4795f3b   bitnami/kafka:3.7.0             "/opt/bitnami/script…"   4 months ago   Up 4 weeks   0.0.0.0:9092->9092/tcp, 0.0.0.0:10000->10000/tcp   shoot-Kafka00Container
ea0db1cd7166   bitnami/kafka:3.7.0             "/opt/bitnami/script…"   4 months ago   Up 4 weeks   0.0.0.0:9094->9092/tcp, 0.0.0.0:10002->10000/tcp   shoot-Kafka02Container
1fd1d6b4de2d   bitnami/kafka:3.7.0             "/opt/bitnami/script…"   4 months ago   Up 4 weeks   0.0.0.0:9093->9092/tcp, 0.0.0.0:10001->10000/tcp   shoot-Kafka01Container
c2f23265cf49   mongo-express:latest            "/sbin/tini -- /dock…"   5 months ago   Up 4 weeks   0.0.0.0:8081->8081/tcp                             mongo-express-shoot
414fa47be544   redis:7.2.3-alpine              "docker-entrypoint.s…"   5 months ago   Up 4 weeks   0.0.0.0:6379->6379/tcp                             redis-shoot
29d6733ada08   mongo:latest                    "docker-entrypoint.s…"   5 months ago   Up 4 weeks   0.0.0.0:27017->27017/tcp                           mongodb-shoot

# Kafka 컨테이너 중 하나에 접속 (예: shoot-Kafka00Container)
docker exec -it shoot-Kafka00Container bash

컨테이너 내부에 들어왔다면 로그 데이터 디렉터리로 이동합니다.

  • Bitnami Kafka 이미지의 경우, 데이터는 /bitnami/kafka/data 경로에 저장됩니다.
# 로그 데이터가 저장된 디렉터리로 이동
cd /bitnami/kafka/data

# 디렉터리 내용 확인
ls -lh

ls -lh를 실행하면 각 토픽의 파티션에 해당하는 디렉터리 목록을 볼 수 있습니다.

# 입력시 아래와 같은 결과를 얻음 (여기서 토픽 선택해야함)
total 188K
drwxr-xr-x 2 1001 root  20K Jul 20 10:31 __cluster_metadata-0
drwxr-xr-x 2 1001 root 4.0K Jun 20 15:20 __consumer_offsets-0
drwxr-xr-x 2 1001 root 4.0K Jun 20 15:15 _schemas-0
-rw-r--r-- 1 1001 root  249 Feb 20 13:35 bootstrap.checkpoint
-rw-r--r-- 1 1001 root  782 May 12 06:05 cleaner-offset-checkpoint
-rw-r--r-- 1 1001 root    4 Jul 20 11:04 log-start-offset-checkpoint
drwxr-xr-x 2 1001 root 4.0K Jun 20 15:15 member-created-outbox-0
-rw-r--r-- 1 1001 root  122 Feb 20 13:35 meta.properties
-rw-r--r-- 1 1001 root  891 Jul 20 11:04 recovery-point-offset-checkpoint
-rw-r--r-- 1 1001 root  891 Jul 20 11:04 replication-offset-checkpoint

특정 파티션 디렉터리 확인

  • member-created-outbox-0 토픽의 0번 파티션 디렉터리로 들어가 내부 파일 목록을 확인합니다.
# 'member-created-outbox-0' 파티션 디렉터리로 이동
cd member-created-outbox-0

# 파티션 디렉터리 내부 파일 목록 확인
ls -lh

아래와 같이 여러 종류의 파일이 나타납니다. 이것이 바로 로그 세그먼트와 그 검색을 돕는 보조 파일들입니다.

# 명령어를 입력하면 아래와 같은 정보가 표시됩니다.
-rw-r--r-- 1 1001 root  180 Feb 22 12:00  00000000000000000000.log
-rw-r--r-- 1 1001 root   10 Jun 20 15:15  00000000000000000000.index
-rw-r--r-- 1 1001 root   10 Jun 20 15:15  00000000000000000000.timeindex
-rw-r--r-- 1 1001 root  150 Feb 22 12:00  00000000000000000020.log
-rw-r--r-- 1 1001 root   10 Jun 20 15:15  00000000000000000020.index
-rw-r--r-- 1 1001 root   10 Jun 20 15:15  00000000000000000020.timeindex
-rw-r--r-- 1 1001 root   9 Jun 20 15:15 leader-epoch-checkpoint
-rw-r--r-- 1 1001 root  43 Feb 22 11:19 partition.metadata

세그먼트 파일을 상세 분석해 봅시다.

  • 각 파일이 어떤 역할을 하는지 자세히 알아보겠습니다.
/bitnami/kafka/data/<토픽>-<파티션>/
├── 000...log         # 레코드 배치가 저장되는 실제 로그 파일
├── 000...index       # (오프셋 → 바이트 위치) 매핑 인덱스
├── 000...timeindex   # (타임스탬프 → 오프셋) 매핑 인덱스
├── 000...snapshot    # 프로듀서 상태 스냅샷 (Idempotence/Transaction용)
├── leader-epoch-checkpoint # 리더 에포크 정보
└── partition.metadata      # 파티션 메타데이터

1. 000...log (로그 세그먼트 파일)

  • 실제 메시지 데이터가 저장되는 핵심 파일입니다. 파일 이름의 숫자는 해당 세그먼트에 저장된 첫 번째 메시지의 오프셋(Base Offset)을 의미합니다. 위 예시에서 00000000000000000020.log는 오프셋 20번부터 시작하는 로그 조각입니다.

    최신 Kafka에서는 메시지를 개별적으로 저장하지 않고, 여러 메시지를 묶은 레코드 배치(Record Batch) 단위로 이 파일에 기록합니다. 레코드 배치는 압축 방식, 타임스탬프, 프로듀서 ID, 트랜잭션 정보 등 각종 메타데이터를 헤더에 포함하고, 그 뒤에 실제 메시지(레코드)들이 이어지는 효율적인 구조입니다. 이 덕분에 네트워크 전송량과 디스크 I/O를 크게 줄일 수 있습니다.

2. 000...index (오프셋 인덱스 파일)

  • 논리적인 오프셋을 .log 파일 내의 물리적인 바이트 위치로 매핑해 주는 색인 파일입니다. 모든 메시지를 인덱싱 하면 파일이 너무 커지므로, 기본적으로 .log 파일에 약 4KB의 데이터가 쌓일 때마다 하나의 인덱스 엔트리(8바이트)를 기록하는 희소 인덱스(sparse index) 방식을 사용합니다. 이를 통해 Kafka는 메모리 사용을 최소화하면서도 특정 오프셋의 위치를 매우 빠르게 찾아낼 수 있습니다.

3. 000...timeindex (타임스탬프 인덱스 파일)

  • 타임스탬프를 오프셋으로 매핑하는 인덱스입니다. "특정 시간 이후의 메시지부터 읽기" 같은 요청을 효율적으로 처리하기 위해 사용됩니다. Kafka는 이 파일을 통해 요청된 시간에 해당하는 오프셋을 빠르게 찾은 뒤, .index 파일을 이용해 실제 데이터 위치를 찾아냅니다.

4. .snapshot (프로듀서 상태 스냅샷 파일)

  • Idempotent Producer(멱등성 프로듀서)나 트랜잭션 기능 사용 시, 정확히 한 번만 전송(Exactly-Once Semantics)을 보장하기 위한 메타데이터 파일입니다. (참고로 프로듀서(Producer) 애플리케이션의 설정에서 멱등성(Idempotence)을 활성화해야 이 파일이 생성됩니다.)

5. leader-epoch-checkpoint (리더 선출)

  • 과거 리더 에폭(epoch) 정보를 저장하여 복제·리더 재선출 시 사용

6. partition.metadata (파티션 고유 메타데이터(토픽 ID, 파티션 epoch 등))

  • 파티션 고유 메타데이터(토픽 ID, 파티션 epoch 등)를 담는 내부 파일로, Kafka 브로커가 재시작·KRaft 메타데이터 로딩 시 참조합니다.

 

액티브 세그먼트와 롤링(Rolling)

한 파티션의 여러 세그먼트 파일 중, 새로운 메시지가 기록되는 파일은 단 하나이며 이를 액티브 세그먼트(Active Segment)라고 부릅니다. 나머지 이전 세그먼트들은 불변의 읽기 전용(Read-Only) 상태로 남아 있습니다.

 

액티브 세그먼트는 아래 조건 중 하나를 만족하면 닫히고(읽기 전용으로 전환), 새로운 세그먼트 파일이 생성되어 액티브 상태가 됩니다. 이 과정을 롤링(Rolling)이라고 합니다.

  1. 크기 기반: 세그먼트 파일 크기가 설정값(log.segment.bytes, 기본값 1GB)에 도달했을 때
  2. 시간 기반: 세그먼트가 열린 후 설정된 시간(log.roll.hours: 168 (7일, 이것이 기본값))이 경과했을 때

예를 들어, 현재 액티브 세그먼트가 오프셋 20부터 1999까지 채워진 상태에서 롤링이 발생하면, 다음 새 세그먼트는 오프셋 2000을 이름으로 하는 00000000000000002000.log 파일로 생성됩니다.

 

롤링 지터(Jitter) 기능

  • log.roll.jitter.ms 또는 log.roll.jitter.hours
  • 이 설정은 실제 롤링 시간을 약간의 무작위 시간만큼 늦추는 역할을 합니다. 예를 들어, 모든 파티션의 세그먼트가 정확히 7일째 되는 순간에 동시에 롤링되면 브로커에 순간적인 부하가 집중될 수 있습니다. 지터는 이 롤링 시점을 분산시켜 부하를 완화하는 매우 유용한 기능입니다.

이처럼 Kafka는 로그 데이터를 효율적으로 파일 단위로 관리하며, 파일 하나가 너무 커지지 않도록 하고, 보존 기간이 지난 데이터를 삭제할 때도 세그먼트 단위로 간편하게 처리합니다. 이 모든 설계는 파일 시스템의 순차 I/O(Sequential I/O) 성능을 극대화하고 데이터 복사를 최소화하여 Kafka가 엄청난 처리량을 가질 수 있게 합니다.

 

 

로그 보존과 삭제: Retention 및 컴팩션


Kafka에 저장된 메시지는 영원히 보관되지 않으며, 보존 정책(Retention Policy)에 따라 데이터의 생명주기가 관리됩니다. 이 정책은 디스크 사용량을 효율적으로 관리하면서도, 비즈니스 요구사항에 맞춰 데이터의 접근성을 보장하는 핵심적인 기능입니다. Kafka는 크게 삭제(delete)와 컴팩션(compact) 두 가지 정책을 제공합니다.

 

기본 정책인 삭제(cleanup.policy=delete)는 지정된 보존 기간이나 용량을 초과한 오래된 로그 세그먼트를 제거하는 방식입니다.

  • 시간 기반 보존
    log.retention.hours 설정을 통해 세그먼트 보존 기간을 정합니다. 기본값은 168시간(7일)이며, 세그먼트 파일의 최종 수정 시간이 이 기간을 초과하면 삭제 대상으로 간주됩니다.

  • 용량 기반 보존
    log.retention.bytes 설정을 사용하면 파티션의 전체 로그 크기가 특정 크기를 넘길 경우 가장 오래된 세그먼트부터 삭제합니다. 기본값은 -1로, 용량 제한이 비활성화된 상태입니다.


중요한 점은 로그 삭제가 보존 기간이 지나는 즉시 이루어지지 않을 수 있다는 것입니다. 현재 메시지가 쓰이고 있는 활성 세그먼트(Active Segment)는 롤링(rolling)되어 닫히기 전까지 삭제되지 않으며, 세그먼트가 닫힌 시점부터 보존 기간이 계산됩니다. 또한 Kafka는 log.retention.check.interval.ms(기본 5분) 주기로 삭제 대상을 확인하므로 약간의 지연이 발생할 수 있습니다. 따라서 보존 기간은 최소 보장 기간으로 이해하는 것이 정확하며, 삭제는 세그먼트 단위로 한 번에 처리되어 성능을 확보합니다.

 

로그 컴팩션(compaction)은 Kafka가 제공하는 또 다른 유지 정책으로, 메시지 키(Key)를 기준으로 각 키의 최신 값만 보존하는 방식입니다. cleanup.policy=compact로 토픽을 설정하면, Kafka는 백그라운드에서 로그를 스캔하여 동일한 키를 가진 이전 값들을 정리합니다. 특정 키의 삭제가 필요할 때는 해당 키에 값이 null인 메시지, 즉 툼스톤(Tombstone)을 전송하며, 이 또한 컴팩션 과정에서 제거됩니다.

 

컴팩션의 목적은 로그의 전체 크기 증가를 억제하면서 각 키의 현재 상태(snapshot)를 유지하는 것입니다. 데이터베이스 변경 이력 스트리밍(CDC), 이벤트 소싱(Event Sourcing) 등에 매우 유용하며, delete 정책과 함께 사용할 수도 있습니다.

 

요약하면, Kafka는 로그를 영구히 쌓아두지 않고 관리 정책에 따라 과거 데이터를 정리합니다. 기본적으로 일정 기간이 지난 세그먼트를 삭제(delete)하며, 필요에 따라 키 기반으로 마지막 상태만 남기는 컴팩션(compact) 옵션도 제공합니다. 이러한 보존 설정은 브로커 전역 기본값을 가질 뿐 아니라 토픽별로 재정의할 수 있어, 데이터의 중요도에 따라 유연한 운영이 가능합니다.

 

 

마무리하며


이번 포스팅에서는 Kafka가 Message Queue에 해당하는지 여부와 어떻게 동작하는지에 대해서 알아보았습니다. 사실 이전까지의 저는  Spring이 아닌 오픈소스에 대한 정보들은 공식 가이드 문서만 읽어보고 그냥 넘어갔었는데 이번 기회에 실제로 Docker에 세팅된 Kafka에 진입해 보며 Kafka는 어떻게 메시지(로그)를 저장하는지 확실하게 알아볼 수 있는 기회가 되었습니다.

만약 여러분들께 여유 시간이 생기신다면 한번쯤은 이렇게 내부의 동작 방식을 조사해 보시는 것을 추천드립니다! 생각보다 많은 시간이 들지만 성장하는 데는 정말 큰 도움이 되었다는 생각이 듭니다.

 

이번 포스팅은 조금 길기도 하고 글의 흐름이 주로 설명이다보니 재미없으셨을 텐데 읽어주셔서 감사합니다 ㅎㅎ

 

 

출처


https://kafka.apache.org/documentation/ 

https://strimzi.io/blog/2021/12/17/kafka-segment-retention/#:~:text=From%20the%20output%2C%20you%20can,is%20called%20the%20active%20segment

반응형

'Apache Kafka' 카테고리의 다른 글

Kafka 메시지 전송 보장 방식 알아보기 (At Most Once, At Least Once, Exactly Once)  (0) 2025.04.12
Kafka에서 Locale.ROOT 사용의 중요성  (3) 2024.12.06
[kafka] Docker로 카프카 실행하기 (KRaft 모드)  (7) 2024.08.25
[kafka] Spring실행 시 consumer 연결문제 해결  (8) 2024.02.18
[kafka] 스프링부트와 kafka를 이용한 slack 예외알림 구현  (1) 2024.02.18
'Apache Kafka' 카테고리의 다른 글
  • Kafka 메시지 전송 보장 방식 알아보기 (At Most Once, At Least Once, Exactly Once)
  • Kafka에서 Locale.ROOT 사용의 중요성
  • [kafka] Docker로 카프카 실행하기 (KRaft 모드)
  • [kafka] Spring실행 시 consumer 연결문제 해결
Stark97
Stark97
소통 및 문의: dig04059@gmail.com (편하게 연락주세요!) 링크드인 소통이나 커피챗도 환영합니다!
  • Stark97
    오늘도 개발중입니다
    Stark97
  • 전체
    오늘
    어제
    • 분류 전체보기 (250)
      • 개발지식 (20)
        • 스레드(Thread) (8)
        • WEB, DB, GIT (3)
        • 디자인패턴 (8)
      • JAVA (21)
      • Spring (88)
        • Spring 기초 지식 (35)
        • Spring 설정 (6)
        • JPA (7)
        • Spring Security (17)
        • Spring에서 Java 활용하기 (8)
        • 테스트 코드 (15)
      • 아키텍처 (6)
      • MSA (15)
      • DDD (12)
      • gRPC (9)
      • Apache Kafka (19)
      • DevOps (23)
        • nGrinder (4)
        • Docker (1)
        • k8s (1)
        • 테라폼(Terraform) (12)
      • AWS (32)
        • ECS, ECR (14)
        • EC2 (2)
        • CodePipeline, CICD (8)
        • SNS, SQS (5)
        • RDS (2)
      • notion&obsidian (3)
      • 채팅 서비스 (1)
      • AI 탐험대 (1)
      • 팀 Pulse (0)
  • 링크

    • notion기록
    • 깃허브
    • 링크드인
  • hELLO· Designed By정상우.v4.10.0
Stark97
Kafka 동작 원리: 큐가 아닌 분산 커밋 로그
상단으로

티스토리툴바