카프카 아키텍처 설명: 핵심 구성 요소 및 역할

Apache Kafka의 분산 이벤트 스트리밍 아키텍처를 구성하는 기본적인 빌딩 블록을 살펴보십시오. 이 가이드는 Kafka 브로커, 토픽, 파티션, 생산자, 소비자, 그리고 ZooKeeper의 조정 역할에 대해 명확히 설명합니다. 이러한 구성 요소들이 어떻게 상호 작용하여 높은 처리량과 내결함성을 갖춘 데이터 처리 및 저장을 보장하는지 학습하세요. 이는 모든 Kafka 구현을 위한 필수 지식입니다.

Kafka 아키텍처 설명: 핵심 구성 요소와 그 역할

Kafka 아키텍처는 동일한 시스템이 저장, 스트리밍, 복제 및 컨슈머 진행 상황을 처리하기 때문에 처음에는 혼란스러워 보일 수 있습니다. 주요 부분을 분리하면 모델이 훨씬 쉬워집니다. 프로듀서는 토픽 파티션에 레코드를 쓰고, 브로커는 해당 파티션을 저장하며, 컨슈머는 오프셋을 기준으로 레코드를 읽습니다.

이 가이드는 핵심 Kafka 구성 요소와 실제 클러스터에서 이들이 어떻게 함께 작동하는지 설명합니다.

브로커: Kafka 서버

Kafka 클러스터는 하나 이상의 브로커로 구성됩니다. 브로커는 파티션 데이터를 저장하고 프로듀서와 컨슈머의 클라이언트 요청을 처리하는 Kafka 서버입니다.

프로듀서가 레코드를 보내면 대상 파티션을 현재 리드하는 브로커에 씁니다. 컨슈머가 레코드를 읽으면 해당 파티션을 제공하는 브로커에서 가져옵니다. 일반적인 설정에서 각 브로커는 많은 토픽의 여러 파티션을 처리합니다.

브로커를 추가하면 저장 용량을 늘리고 트래픽을 분산할 수 있지만 모든 병목 현상이 자동으로 해결되지는 않습니다. 충분한 파티션, 균형 잡힌 복제본 배치, 정상적인 디스크 및 네트워크 용량도 필요합니다.

토픽: 레코드의 명명된 스트림

토픽은 orders, payments 또는 user_activity와 같은 레코드의 명명된 스트림입니다. 프로듀서는 토픽에 쓰고 컨슈머는 토픽을 구독합니다.

토픽은 파티션으로 분할됩니다. 각 파티션은 순서가 지정된 추가 전용 로그입니다. Kafka는 단일 파티션 내에서 레코드 순서를 보장하지만 전체 토픽에서는 보장하지 않습니다.

이 세부 사항이 중요합니다. 한 고객의 모든 이벤트를 순서대로 처리해야 하는 경우 customer_id와 같은 안정적인 키를 사용하세요. Kafka의 기본 파티셔너는 키를 사용하여 파티션을 선택하므로 동일한 키를 가진 레코드는 일반적으로 동일한 파티션으로 이동합니다.

파티션 및 오프셋

파티션의 각 레코드는 오프셋을 얻습니다. 오프셋은 해당 파티션에서 레코드의 위치를 식별하는 숫자입니다.

예를 들어, 세 개의 파티션이 있는 orders라는 토픽은 다음과 같을 수 있습니다.

orders-0: offset 0, offset 1, offset 2
orders-1: offset 0, offset 1
orders-2: offset 0, offset 1, offset 2, offset 3

오프셋은 자체 파티션 내에서만 의미가 있습니다. orders-2의 오프셋 3은 다른 파티션의 오프셋 3과 관련이 없습니다.

파티션은 Kafka에 병렬 처리를 제공합니다. 더 많은 파티션을 사용하면 동일한 컨슈머 그룹 내에서 더 많은 컨슈머가 동시에 작업할 수 있으며, 해당 그룹 내에서 파티션당 하나의 활성 컨슈머로 제한됩니다.

복제 및 리더

Kafka는 브로커에 장애가 발생할 때 데이터를 계속 사용할 수 있도록 복제를 사용합니다. 각 파티션은 다른 브로커에 여러 복제본을 가질 수 있습니다.

하나의 복제본은 리더입니다. 프로듀서와 컨슈머는 일반적으로 해당 파티션의 리더와 통신합니다. 다른 복제본은 팔로워입니다. 팔로워는 리더로부터 데이터를 복사하고 리더에 장애가 발생하면 인계받을 준비를 유지합니다.

복제 팩터는 Kafka가 유지하는 복사본 수를 제어합니다. 복제 팩터가 3이면 충분한 브로커를 사용할 수 있을 때 Kafka가 세 개의 브로커에 각 파티션의 세 복사본을 저장함을 의미합니다.

다음과 같이 토픽을 생성할 수 있습니다.

kafka-topics.sh --create \
  --topic user_activity \
  --bootstrap-server localhost:9092 \
  --partitions 3 \
  --replication-factor 3

이 명령은 최소 세 개의 브로커가 있는 클러스터가 필요합니다. 단일 브로커 로컬 설정에서는 복제 팩터를 1로 사용하세요.

프로듀서: 이벤트를 쓰는 애플리케이션

프로듀서는 Kafka 토픽에 레코드를 보냅니다. 레코드에는 키, 값, 타임스탬프 및 헤더가 포함될 수 있습니다.

프로듀서는 먼저 클러스터에 메타데이터를 요청하여 각 파티션을 리드하는 브로커를 확인합니다. 그런 다음 레코드를 올바른 브로커로 직접 보냅니다.

프로듀서 안정성은 다음과 같은 설정에 크게 의존합니다.

설정 영향
acks 쓰기가 성공한 것으로 간주되기 전에 필요한 브로커 승인 수
retries 프로듀서가 일시적인 실패를 재시도하는지 여부
enable.idempotence 프로듀서 재시도로 인한 중복 방지에 도움
compression.type 많은 워크로드에서 네트워크 및 디스크 사용량 감소

중요한 데이터의 경우 acks=all이 일반적입니다. 리더가 쓰기를 승인하기 전에 동기화된 복제본을 기다리기 때문입니다. 정확한 동작은 min.insync.replicas와 같은 브로커 설정에도 따라 다릅니다.

컨슈머 및 컨슈머 그룹

컨슈머는 토픽에서 레코드를 읽습니다. 대부분의 프로덕션 컨슈머는 컨슈머 그룹 내에서 실행됩니다.

하나의 컨슈머 그룹 내에서 Kafka는 각 파티션을 한 번에 하나의 활성 컨슈머에만 할당합니다. 이것이 Kafka가 각 파티션 내에서 순서를 유지하면서 처리를 확장할 수 있게 하는 방법입니다.

예를 들어, orders에 세 개의 파티션이 있고 서비스에 동일한 그룹에 세 개의 컨슈머가 있는 경우 각 컨슈머는 하나의 파티션을 처리할 수 있습니다. 동일한 그룹에 네 번째 컨슈머를 추가하면 할당할 파티션이 세 개뿐이므로 하나의 컨슈머는 유휴 상태가 됩니다.

다른 컨슈머 그룹은 독립적으로 읽습니다. 청구 서비스와 분석 서비스는 서로의 레코드를 가로채지 않고 orders 토픽을 읽을 수 있습니다.

오프셋 및 컨슈머 진행 상황

컨슈머는 오프셋을 커밋하여 진행 상황을 추적합니다. Kafka는 컨슈머 그룹에 대해 커밋된 오프셋을 __consumer_offsets라는 내부 토픽에 저장합니다.

컨슈머가 충돌하여 다시 시작하면 커밋된 오프셋을 사용하여 재개합니다. 커밋 시점은 처리 동작에 영향을 미칩니다.

커밋 시점 가능한 결과
처리가 완료되기 전에 커밋 충돌 시 레코드 건너뛸 수 있음
처리가 완료된 후 커밋 충돌 시 레코드 다시 처리할 수 있음

많은 시스템이 최소 한 번 처리를 선택합니다. 레코드를 처리한 다음 오프셋을 커밋합니다. 이는 충돌 후 중복을 생성할 수 있으므로 가능하면 다운스트림 쓰기는 멱등성이 있어야 합니다.

클러스터 메타데이터: ZooKeeper 및 KRaft

이전 Kafka 클러스터는 Apache ZooKeeper를 사용하여 클러스터 메타데이터 및 컨트롤러 선거를 관리합니다. 많은 기존 설치가 여전히 이 방식으로 실행됩니다.

최신 Kafka 배포는 Kafka의 내장 메타데이터 쿼럼인 KRaft 모드를 사용할 수 있습니다. KRaft 클러스터에서 Kafka는 더 이상 메타데이터 관리를 위해 ZooKeeper에 의존하지 않습니다.

이전 Kafka 튜토리얼을 읽을 때 ZooKeeper 또는 KRaft를 가정하는지 확인하세요. 명령, 구성 파일 및 운영 단계가 다를 수 있습니다.

레코드가 Kafka를 통해 이동하는 방법

일반적인 쓰기 및 읽기 흐름은 다음과 같습니다.

  1. 프로듀서가 부트스트랩 브로커에 연결하고 메타데이터를 가져옵니다.
  2. 프로듀서가 레코드 키 또는 파티셔닝 전략에 따라 파티션을 선택합니다.
  3. 프로듀서가 해당 파티션의 리더 브로커에 레코드를 보냅니다.
  4. 리더가 레코드를 로그에 추가하고 팔로워가 이를 복제합니다.
  5. 리더가 프로듀서의 acks 설정에 따라 쓰기를 승인합니다.
  6. 컨슈머가 파티션을 폴링하고 현재 오프셋부터 레코드를 수신합니다.
  7. 컨슈머가 레코드를 처리하고 컨슈머 그룹에 대한 오프셋을 커밋합니다.

이 흐름이 Kafka가 실시간 처리와 재생을 모두 지원할 수 있는 이유입니다. 컨슈머는 레코드를 읽을 때 제거하지 않습니다.

보존: Kafka는 정책에 따라 데이터를 유지합니다.

Kafka는 메시지가 컨슈머가 읽자마자 사라지는 기존 큐가 아닙니다. Kafka는 보존 설정에 따라 레코드를 유지합니다.

일반적인 토픽 설정은 다음과 같습니다.

retention.ms=604800000
retention.bytes=10737418240

retention.ms는 시간 기반 보존을 제어합니다. retention.bytes는 크기 기반 보존을 제어합니다. 실제 정리는 세그먼트 설정 및 브로커 구성에 따라 다릅니다.

일부 토픽은 삭제 기반 보존 대신 또는 함께 로그 압축을 사용합니다. 압축은 각 키의 최신 값을 유지하므로 사용자 프로필 또는 구성 변경과 같은 상태와 유사한 토픽에 유용합니다.

기억해야 할 사항

Kafka의 아키텍처는 분할된 로그를 중심으로 구축되었습니다. 브로커는 파티션을 저장하고, 프로듀서는 파티션 리더에 쓰고, 컨슈머는 오프셋으로 읽으며, 컨슈머 그룹은 파티션 간에 작업을 분할합니다.

Kafka 토픽을 설계할 때 순서, 파티션 수, 복제 팩터, 보존 및 컨슈머 그룹 동작을 함께 고려하세요. 이러한 선택은 시스템이 확장되고, 장애로부터 복구되며, 이전 이벤트를 재생하는 방식을 결정합니다.