Kafka 기본 개념과 구조
Kafka를 이해할 때는 "메시지를 꺼내면 사라지는 큐"보다 여러 consumer가 각자 offset으로 읽는 이벤트 로그로 보는 편이 안전합니다.
전체 그림
Kafka는 producer가 이벤트를 넣고, broker가 이벤트를 보관하고, consumer group이 각자 필요한 속도로 읽어가는 구조입니다.
flowchart LR
P1[주문 서비스<br/>Producer] --> K[(Kafka Cluster)]
P2[결제 서비스<br/>Producer] --> K
K --> T1[topic: order-events]
T1 --> PA[partition 0]
T1 --> PB[partition 1]
T1 --> PC[partition 2]
PA --> C1[재고 Consumer Group]
PB --> C1
PC --> C1
PA --> C2[알림 Consumer Group]
PB --> C2
PC --> C2
C1 --> D1[(재고 DB)]
C2 --> D2[문자·메일 발송]
그림을 이렇게 읽으면 됩니다.
| 구성요소 | 쉬운 설명 |
|---|---|
| Producer | 이벤트를 Kafka에 넣는 쪽 |
| Topic | 이벤트를 종류별로 담는 이름 |
| Partition | topic 안에서 실제로 쌓이는 로그 파일 같은 단위 |
| Consumer Group | 같은 일을 나눠 처리하는 소비자 묶음 |
| Offset | consumer group이 어디까지 읽었는지 표시하는 책갈피 |
Kafka를 처음 배울 때 가장 중요한 문장입니다.
Producer는 topic에 쓴다.
Kafka는 partition에 순서대로 보관한다.
Consumer group은 offset을 기억하며 읽는다.
서로 다른 consumer group은 같은 이벤트를 따로 읽을 수 있다.
Topic, Partition, Offset
Topic은 이벤트의 논리적 분류이고, Partition은 topic을 나누어 저장하는 append-only log입니다. Offset은 partition 안에서 메시지 위치를 나타냅니다.
flowchart LR
subgraph T[topic: order-events]
P0[partition 0<br/>offset 0,1,2,3]
P1[partition 1<br/>offset 0,1,2,3]
P2[partition 2<br/>offset 0,1,2,3]
end
| 개념 | 설명 |
|---|---|
| Topic | 이벤트 종류 |
| Partition | 병렬 처리와 순서 보장 단위 |
| Offset | partition 안의 위치 |
| Record | key, value, headers, timestamp를 가진 메시지 |
조금 더 실제 로그처럼 보면 아래와 같습니다.
consumer group offset이 2라면 보통 "다음에 offset 2부터 읽는다"는 의미입니다. 이미 처리한 마지막 메시지 번호가 아니라, 다음에 읽을 위치를 저장한다고 이해하면 헷갈림이 줄어듭니다.
Broker, Leader, Follower, ISR
Kafka cluster는 여러 broker로 구성됩니다. 각 partition에는 leader replica가 있고, follower replica가 leader를 따라갑니다.
flowchart TB
subgraph B1[broker-1]
L0[p0 leader]
F1[p1 follower]
end
subgraph B2[broker-2]
F0[p0 follower]
L1[p1 leader]
end
subgraph B3[broker-3]
F02[p0 follower]
F12[p1 follower]
end
| 개념 | 설명 |
|---|---|
| Leader | producer write와 consumer read를 담당하는 replica |
| Follower | leader 데이터를 복제 |
| ISR | leader와 충분히 동기화된 replica 집합 |
| High Watermark | consumer에게 노출 가능한 안전한 offset 경계 |
| Controller | partition leader 선출과 metadata 관리 |
acks=all은 producer가 leader에게 보낸 메시지가 ISR 조건을 만족할 때 성공으로 봅니다. min.insync.replicas=2인데 ISR이 leader 1개뿐이면 producer는 실패를 받습니다. 이것은 장애가 아니라 유실을 막기 위해 쓰기를 거부하는 정상 보호 동작일 수 있습니다.
정상 쓰기와 쓰기 거부를 나누면 이렇게 볼 수 있습니다.
flowchart TB
P[Producer<br/>acks=all]
P --> L[Leader]
L --> F1[Follower 1<br/>ISR]
L --> F2[Follower 2<br/>ISR]
F1 --> OK[ack 가능<br/>ISR 2개 이상]
F2 --> OK
L --> X[Follower 지연 또는 장애]
X --> FAIL[ISR 부족<br/>NotEnoughReplicas]
NotEnoughReplicas가 보이면 무조건 설정을 낮춰서 성공시키는 것이 답은 아닙니다. Kafka가 "지금은 안전하게 복제할 수 없으니 쓰기를 실패시키겠다"고 알려주는 신호일 수 있습니다.
Producer 전송 흐름
sequenceDiagram
participant P as Producer
participant L as Partition Leader
participant F as Followers in ISR
P->>L: produce request
L->>L: append to leader log
L->>F: replicate
F-->>L: ack
L-->>P: ack
Producer 성능은 하나씩 보내는 것이 아니라 batch, compression, linger로 묶어 보내는 방식에서 나옵니다. 처리량이 중요하면 배치를 키우고, 지연이 중요하면 linger와 batch를 줄입니다.
Retention과 Compaction
Kafka는 소비했다고 메시지를 바로 지우지 않습니다. 보관 정책에 따라 지웁니다.
flowchart LR
A[메시지 생산] --> B[Kafka에 저장]
B --> C[Consumer가 읽음]
C --> D[메시지는 바로 삭제되지 않음]
D --> E{보관 정책}
E -->|retention 시간/크기 초과| F[삭제]
E -->|compaction| G[같은 key의 최신값 중심 유지]
이 점이 일반 큐와 크게 다릅니다. consumer가 읽었다고 Kafka 메시지가 사라지는 것이 아니기 때문에, 장애 후 offset을 되돌려 재처리할 수 있습니다. 대신 retention 기간이 지나 삭제된 메시지는 다시 읽을 수 없습니다.
| 정책 | 설명 | 사용처 |
|---|---|---|
delete |
시간 또는 크기 기준 삭제 | 일반 이벤트, 로그 |
compact |
같은 key의 최신 값 중심 유지 | 상태 변경 최신값, 설정, CDC 일부 |
delete,compact |
둘을 함께 사용 | 최신값 유지 + 오래된 tombstone 정리 |
Retention을 너무 짧게 잡으면 장애 복구 전에 메시지가 사라질 수 있고, 너무 길게 잡으면 디스크가 위험해집니다.
관련 파일: - Kafka 개요 - Producer와 이벤트 설계 - Consumer와 전달 보장