사내에서 카프카 스터디를 진행하고 있다. 아래 실전 카프카 개발부터 운영까지 책을 읽고 책 리뷰하는 형태로 진행된다.
실전 카프카 개발부터 운영까지 | 고승범 - 교보문고
실전 카프카 개발부터 운영까지 | 아파치 카프카의 공동 창시자 준 라오(Jun Rao)가 추천한 책!국내 최초이자 유일한 컨플루언트 공인 아파치 카프카 강사(Confluent Certified Trainer for Apache Kafka)와 공
product.kyobobook.co.kr
3.1 카프카 기초 다지기
- 주키퍼(ZooKeeper) : 아파치 프로젝트 애플리케이션 이름. 카프카의 메타데이터 관리 및 브로커의 정상상태 점검(health check)을 담당한다.
- 카프카 / 카프카 클러스터 : 여러 대의 브로커를 구성한 클러스터
- 브로커 : 카프카 애플리케이션이 설치된 서버 또는 노드
- 프로듀서 : 카프카로 메시지를 보내는 역할을 하는 클라이언트
- 컨슈머 : 카프카에서 메시지를 꺼내가는 역할을 하는 클라이언트
- 토픽 : 메시지 피드들을 토픽으로 구분. 각 토픽의 이름은 카프카 클러스터 내에서 고유함.
- 파티션 : 병렬 처리 및 고성능을 얻기 위해 하나의 토픽을 여러 개로 나눈 것.
- 세그먼트 : 프로듀서가 전송한 실제 메시지가 브로커의 로컬 디스크에 저장되는 형태의 파일
- 메시지 / 레코드 : 프로듀서가 브로커로 전송하거나 컨슈머가 읽어가는 데이터 조각
3.1.1 리플리케이션
리플리케이션(replication) : 각 메시지들을 여러 개로 복제해서 카프카 클러스터 내 브로커들에 분산시키는 동작
replication-factor : 원본 포함한 토픽 파티션의 replica가 3개 있다는 뜻
- 팩터 수가 높다는 것은 안정성이 높고, 브로커 리소스 사용도 높다는 뜻.
- 따라서 복제에 대한 오버헤드를 줄여서 최대한 효율적으로 사용하는 것이 중요.
[적절한 수준]
로그성 메시지 : replication 2개
유실 허용 불가한 메시지 : replication 3개
3.1.2 파티션
하나를 여러 개로 나눈 파티션을 이용하면 분산 처리도 가능하다.
- 파티션 번호는 0부터 시작한다.
- 파티션 수는 토픽 생성 시 옵션으로 설정하는데 모호한 경우가 많다.
- 초기 생성 후 언제든지 늘릴 수는 있지만 줄일 수는 없다.
- 처음에는 2 or 4 정도로 시작하고 컨슈머의 lag 등을 모니터링하면서 조금씩 늘려가는 방법이 가장 좋다.
- lag : 프로듀서가 보낸 메시지 수(카프카에 남아 있는 메시지 수) - 컨슈머가 가져간 메시지 수
컨슈머에 지연이 없는지를 확인할 수 있음. - https://eventsizer.io/partitions ← 여기서 공식을 이용해 산정해볼 수도 있지만, 참고만 하삼!
- lag : 프로듀서가 보낸 메시지 수(카프카에 남아 있는 메시지 수) - 컨슈머가 가져간 메시지 수
3.1.3 세그먼트
프로듀서가 보낸 메시지는 토픽의 파티션에 저장되며 세그먼트라는 로그 파일의 형태로 브로커의 로컬 디스크에 저장된다.

3.2 카프카의 핵심 개념
개발자가 카프카를 사랑하는 이유
- 높은 처리량
- 빠른 응답 속도
- 안정성

3.2.1 분산 시스템
높은 성능을 목표로 한 분산 시스템. (장애 대응 + 시스템 확장에 용이)
온라인 상태에서도 간단하게 추가되는 브로커로 그 장점을 누릴 수 있다.
3.2.2 페이지 캐시
OS에서 성능을 높이기 위해 사용하는 방식으로, 직접 디스크에 읽고 쓰는 대신 물리 메모리 중 애플리케이션이 사용하지 않는 일부 잔여 메모리를 활용한다. 페이지 캐시를 이용하여 디스크 I/O에 대한 접근이 줄어들면 성능을 높일 수 있다.
3.2.3 배치 전송 처리
배치 전송으로 통신을 묶어서 처리할 수 있다면 네트워크 오버헤드를 줄이고 장기적으로는 더욱 빠르고 효율적으로 처리가 가능하다. 따라서 카프카는 배치 전송 (= 키네시스의 버퍼)을 권장한다.
3.2.4 압축 전송
압축(gzip, snappy, lz4, zstd) 타입을 권장한다.
- 높은 압축률 : gzip, zstd
- 빠른 응답 속도 : lz4, snappy
3.2.5 토픽, 파티션, 오프셋
토픽 : 여러 개의 파티션으로 나뉨. (for 병렬 처리)
오프셋 : 파티션 내 메시지가 저장되는 위치
- 순차적으로 증가하는 숫자(64비트 정수) 형태
- 메시지의 순서를 보장해주고 컨슈머에서는 마지막까지 읽은 위치를 파악할 수 있다.
3.2.6 고가용성 보장
여러 서버 / 노드에 리플리케이션 기능을 제공하여 하나의 노드 장애도 버틸 수 있는 고가용성을 제공한다. 카프카의 리플리케이션은 토픽 자체를 복제하는 것이 아니라 파티션 단위로 복제를 한다. 토픽 생성 시 사용하는 옵션으로 replication-factor를 지정할 수 있다.
리더 & 팔로워 라는 명칭으로 메인 노드와 레플리카를 구분한다.
- 리더 : 프로듀서, 컨슈머로부터 오는 모든 읽기와 쓰기 요청을 처리한다.
- 팔로워 : 오직 리더로부터 리플리케이션만 한다.
3.2.7 주키퍼의 의존성
주키퍼 : 하둡의 서브 프로젝트, 카프카의 중요한 메타 데이터를 저장하고 각 브로커를 관리한다. 여러 대의 서버를 앙상블(클러스터)로 구성하고 살아 있는 노드 수가 과반 수 이상을 유지하면 지속적인 서비스가 가능한 구조이다.
- 따라서 반드시 홀수로 구성해야 한다.
znode(지노드)를 이용해서 카프카의 메타 정보가 주키퍼에 기록된다. 지노드는 브로커의 노드 관리, 토픽 관리, 컨트롤러 관리 등 매우 중요한 역할을 하고 있다.
- 상태 정보들은 주키퍼의 지노드라는 곳에 key-value 형태로 저장되는 것. (참고 : https://goodgid.github.io/Zookeeper/)
최근 들어 주키퍼 성능의 한계를 마주했고, 이에 대한 의존성 제거를 노력 중.
3.3 프로듀서의 기본 동작
3.3.1 프로듀서 디자인

*ProducerRecord : 카프카로 전송하기 위한 실제 데이터
**레코드에서 토픽과 밸류(메시지 내용)는 필수값 / 레코드의 파티션과 키는 옵션
***파티션을 지정하지 않은 경우는 파티셔너에서 라운드 로빈 방식으로 결정한다.
프로듀서 내부에서는 배치 전송을 위해 레코드들을 파티션 별로 잠시 모아두게 된다. 전송이 실패하면 재시도하게 되고, 재시도 횟수가 오버되면 최종 실패를 전달, 전송이 성공하면 메타데이터를 리턴한다.
3.3.2 프로듀서의 주요 옵션
프로듀서 옵션 | 설명 | 기본값 |
bootstrap.servers | ||
client.dns.lookup | ||
acks | 프로듀서가 카프카 토픽의 리더 측에 메시지를 전송한 후 요청을 완료 처리하는 걸 결정하는 기준 옵션.
|
1 |
buffer.memory | 카프카 서버로 데이터를 보내기 위해 잠시 대기할 수 있는 전체 메모리 바이트 | 32 MB |
batch.size | 동일한 파티션으로 보내는 여러 데이터를 함께 배치로 보내려고 시도할 때 그 배치 크기 설정 | 16 KB |
linger.ms | 배치 형태의 메시지를 보내기 전에 추가적인 메시지를 위해 기다리는 시간. 배치 사이즈에 도달하지 못했더라도 이 시간에 도달했다면 메시지를 전송한다. | 0 ms (대기 x) |
compression.type | none, gzip, snappy, lz4, zstd | none |
enable.idempotence | 설정을 true로 하면 중복 없는 전송이 가능. 동시에 max.in.flight.requests.per.connection = 5 이하, retries = 0 이상, acks = all 로 설정해야 한다. |
False |
max.in.flight.requests.per.connection | 하나의 커넥션에서 프로듀서가 최대한 ACK 없이 전송할 수 있는 요청 수. 메시지의 순서가 중요하다면 1로 설정할 수 있지만 성능이 떨어짐. | 5 |
retries | 재시도 횟수 | 2147483647 |
transactional.id | ‘정확히 한번 전송'을 위해 사용하는 옵션. 동일한 TransactionalId에 한해 정확히 한번을 보장. enable.idempotence = True 로 설정해야 함. | null |
3.3.3 프로듀서 예제
프로듀서의 전송 방법
- 메시지를 보내고 확인하지 않기 (전송 보장 X)
- 동기 전송
- 비동기 전송
3.4 컨슈머의 기본 동작과 예제
컨슈머는 내부적으로 컨슈머 그룹, 리밸런싱 등 여러 동작을 수행한다.
3.4.1 컨슈머의 기본 동작
컨슈머 그룹 : 하나 이상의 컨슈머들이 모여 있는 그룹. 컨슈머는 반드시 컨슈머 그룹에 속하게 된다.
각 파티션의 리더에게 카프카 토픽에 저장된 메시지를 가져오기 위한 요청을 보낸다.
→ 파티션 수와 컨슈머 수 일대일 매핑이 이상적. (파티션 수 < 컨슈머 수는 바람직 X)
3.4.2 컨슈머의 주요 옵션
bootstrap.servers | 브로커의 정보를 입력한다. | |
fetch.min.bytes | 한 번에 가져올 수 있는 최소 데이터 크기. 지정한 크기보다 작은 경우, 요청에 응답하지 않고 데이터가 누적될 때까지 기다린다. | 1 |
group.id | 컨슈머의 컨슈머 그룹을 식별하는 식별자. 동일한 그룹 내의 컨슈머 정보는 모두 공유된다. | |
heartbeat.interval.ms | 하트비트가 있다는 것은 컨슈머의 상태 = active 라는 뜻. session.timeout.ms와 밀접한 관계가 있고, session.timeout.ms보다 낮은 값으로 설정해야 한다. (일반적으로 1/3) | 3000 ms |
max.partition.fetch.bytes | 파티션당 가져올 수 있는 최대 크기 | 1MB |
session.timeout.ms | 이 시간을 이용해 컨슈머가 종료된 것인지를 판단. 컨슈머는 주기적으로 하트비트를 보내야 하고, 이 시간 전까지 하트비트를 보내지 않았다면 해당 컨슈머는 종료된 것으로 간주하고 컨슈머 그룹에서 제외하고 리밸런싱을 시작함. | 10000 ms = 10 s |
enable.auto.commit | 백그라운드로 주기적으로 오프셋을 커밋 | True |
auto.offset.reset | 초기 오프셋이 없거나 현재 오프셋이 더 이상 존재하지 않는 경우 reset하는데 사용할 옵션
|
latest |
fetch.max.bytes | 한 번의 가져오기 요청으로 가져올 수 있는 최대 크기 | 50 MB |
group.instance.id | 컨슈머의 고유한 식별자. 만약 설정하면 static 멤버로 간주되어 불필요한 리밸런싱을 하지 않는다. | |
isolation.level | 트랜잭션 컨슈머*에서 사용되는 옵션.
|
read_uncommitted |
max.poll.records | 한 번의 poll() 요청으로 가져오는 최대 메시지 수 | 500 |
partition.assignment.strategy | 파티션 할당 전략. 기본값은 range | [org.apache.kafka.clients.consumer.RangeAssignor] |
fetch.max.wait.ms | fetch.min.bytes에 의해 설정된 데이터보다 적은 경우 요청에 대한 응답을 기다리는 최대 시간 | 500 ms |
*트랜잭션 컨슈머 : 트랜잭션을 사용하면 프로듀서는 여러 파티션에 걸쳐 메시지를 원자적으로 보내고, 컨슈머는 이러한 트랜잭션으로 보내진 메시지들을 트랜잭션이 완료된 후에만 처리할 수 있다. 트랜잭션에서 보내진 메시지들을 성공적으로 커밋되기 전까지는 볼 수 없다. (=읽기 원자성)
3.4.3 컨슈머 예제
컨슈머에서 메시지를 가져오는 방법
- 오토 커밋 : 오프셋을 주기적으로 커밋하므로 관리자가 따로 오프셋을 관리하지 않아도 됨. but 컨슈머 종료가 빈번히 일어나면 일부 메시지를 못 가져오거나 중복으로 가져오는 경우가 있음.
- 동기 가져오기 : 메시지 손실(토픽에는 메시지가 존재하지만 잘못된 오프셋 커밋으로 위치 변경 되어 컨슈머가 메시지를 가져오지 못하는 경우) 거의 없음
- 비동기 가져오기 : 오프셋 커밋을 실패해도 재시도 하지 않음. 콜백을 같이 사용해 보완하기도 함.
3.4.4 컨슈머 그룹의 이해
하나의 컨슈머 그룹 안에 여러 개의 컨슈머가 구성됨. 토픽의 파티션과 매핑되어 메시지를 가져온다.
'TIL > 카프카' 카테고리의 다른 글
실전 카프카 개발부터 운영까지 - 4. 카프카의 내부 동작 원리와 구현 (0) | 2024.03.20 |
---|
댓글