Kafka vs Pulsar 대규모 스트리밍 아키텍처 설계

대적인 분산 시스템에서 일일 수 테라바이트(TB)급의 데이터를 처리할 때, 가장 먼저 마주하는 병목은 애플리케이션 로직이 아닌 데이터 수집 및 전달 계층(Ingestion Layer)입니다. 많은 조직이 "업계 표준"이라는 이유만으로 Apache Kafka를 기본값으로 선택하지만, 데이터 규모가 페타바이트 수준으로 확장되거나 복잡한 테넌트 격리(Multi-tenancy)가 요구될 때 Kafka의 아키텍처적 한계는 운영 비용 증가로 직결됩니다. 본 글에서는 단순히 기능 목록을 나열하는 것을 넘어, 스토리지와 컴퓨팅의 분리, 확장성 모델, 그리고 데이터 일관성 보장 방식의 차이를 기반으로 Kafka와 Apache Pulsar의 기술적 트레이드오프를 분석합니다.

1. 모놀리식 로그 vs 계층형 세그먼트 스토리지

Kafka와 Pulsar를 가르는 가장 결정적인 차이는 스토리지 아키텍처에 있습니다. 이 차이는 클러스터 확장 시의 운영 난이도와 비용 효율성을 결정짓는 핵심 요소입니다.

Kafka는 파티션(Partition)을 물리적인 디스크의 파일(로그)과 1:1로 매핑하는 구조를 가집니다. 이는 순차적 I/O를 통해 강력한 처리량(Throughput)을 보장하지만, 브로커(Broker)가 컴퓨팅과 스토리지를 동시에 담당하게 만듭니다. 즉, 스토리지가 부족하면 브로커를 추가해야 하고, 이는 불필요한 컴퓨팅 자원의 낭비로 이어질 수 있습니다. 또한 브로커 장애 시 해당 브로커가 가진 파티션 데이터를 다른 브로커로 복제하는 과정에서 막대한 네트워크 대역폭이 소모됩니다.

반면, Pulsar는 컴퓨팅(Broker)과 스토리지(BookKeeper)를 완벽하게 분리한 아키텍처를 채택했습니다. Pulsar의 브로커는 상태를 가지지 않는(Stateless) 요청 라우팅 계층이며, 실제 데이터는 Apache BookKeeper라는 별도의 분산 스토리지에 '세그먼트(Segment)' 단위로 분산 저장됩니다.

Architecture Note: Apache BookKeeper
BookKeeper는 Write-Ahead Log(WAL) 기반의 분산 스토리지입니다. Kafka의 파티션이 하나의 거대한 파일이라면, Pulsar(BookKeeper)의 파티션은 수많은 작은 세그먼트들의 집합입니다. 이를 통해 데이터를 클러스터 전체 노드에 균등하게 분산(Striping)시킬 수 있습니다.

2. 확장성과 리밸런싱(Rebalancing)의 고통

대규모 운영 환경에서 가장 두려운 상황 중 하나는 Kafka 클러스터의 확장(Scale-out)입니다. Kafka에서 새로운 브로커를 추가하면, 기존 브로커에 집중된 파티션들을 새 브로커로 이동시키는 리밸런싱 작업이 필수적입니다. 이 과정에서 대량의 데이터 복제가 발생하며, 이는 클러스터 전체의 성능 저하(I/O Wait 증가)를 유발할 수 있습니다. 수 TB의 데이터를 가진 브로커를 리밸런싱하는 데는 수 시간이 소요될 수 있습니다.

Pulsar는 이 문제를 세그먼트 단위 저장 방식으로 해결합니다. 새로운 BookKeeper 노드(Bookie)가 추가되면, Pulsar는 즉시 새로운 세그먼트를 해당 노드에 쓰기 시작합니다. 과거 데이터를 이동시킬 필요가 없으며, 트래픽은 자연스럽게 새로운 노드로 분산됩니다. 이는 즉각적인 확장(Instant Scaling)을 가능하게 하며, 오토스케일링 환경(Kubernetes 등)에 훨씬 적합합니다.

내구성 설정을 위한 설정 비교

데이터 유실 없는 설계를 위해 두 시스템은 서로 다른 설정 파라미터를 제공합니다. 다음은 Production 환경에서 내구성을 최우선으로 고려한 설정 예시입니다.


// Kafka Producer Configuration (Java)
// acks=all: 모든 ISR(In-Sync Replicas)이 확인해야 성공 처리
props.put("acks", "all");
props.put("enable.idempotence", "true"); // 중복 방지 및 순서 보장
props.put("max.in.flight.requests.per.connection", "5");

// Pulsar Producer Configuration (Java)
// Write Quorum(Qw)과 Ack Quorum(Qa)을 통한 세밀한 제어
Producer<byte[]> producer = client.newProducer()
    .topic("persistent://public/default/my-topic")
    .enableBatching(true)
    .blockIfQueueFull(true)
    // 메시지 손실 방지를 위한 전송 모드 설정
    .sendTimeout(0, TimeUnit.SECONDS) 
    .create();
    
// BookKeeper 설정 (broker.conf)
// Ensemble Size(E), Write Quorum(Qw), Ack Quorum(Qa)
// E=3, Qw=3, Qa=2 : 3개 노드에 쓰고 2개 응답 시 성공
managedLedgerDefaultEnsembleSize=3
managedLedgerDefaultWriteQuorum=3
managedLedgerDefaultAckQuorum=2

3. 멀티테넌시와 격리(Isolation)

하나의 클러스터를 여러 팀이나 서비스가 공유해야 하는 환경이라면 격리 수준이 중요합니다. Kafka는 기본적으로 멀티테넌시를 위한 네이티브 기능이 부족합니다. 단순히 토픽 이름에 접두사(Prefix)를 붙여 구분하거나, 별도의 인증/인가 시스템을 복잡하게 구성해야 합니다. 또한, 특정 테넌트(Topic)의 부하가 다른 테넌트의 성능에 영향을 주는 'Noisy Neighbor' 문제를 해결하기 어렵습니다.

Pulsar는 설계 단계부터 멀티테넌시를 고려했습니다. Tenant > Namespace > Topic으로 이어지는 계층 구조를 가지며, 각 네임스페이스별로 스토리지 쿼터, 보존 정책(Retention Policy), 인증 등을 독립적으로 설정할 수 있습니다. 특히 I/O 격리 기능을 통해 특정 네임스페이스의 과도한 트래픽이 전체 클러스터를 마비시키는 것을 방지합니다.

Best Practice: SaaS 형태의 서비스를 구축하거나, 전사 공용 데이터 파이프라인을 구축해야 한다면 Pulsar의 네임스페이스 격리 기능은 운영 복잡도를 획기적으로 낮춰줍니다.

4. 기술적 비교 요약

아키텍처 선정을 위한 핵심 지표 비교입니다.

기능 Apache Kafka Apache Pulsar
아키텍처 Monolithic (Compute + Storage) Tiered (Compute / Storage 분리)
확장성 (Scaling) 리밸런싱 필요 (데이터 이동 발생) 즉시 확장 (데이터 이동 없음)
Geo-Replication MirrorMaker (별도 프로세스) 내장 기능 (Built-in)
메시지 모델 Log 중심 (Streaming) Streaming + Queuing (RabbitMQ 대체 가능)
Tiered Storage 지원 (KIP-405, S3 등 오프로딩) 네이티브 지원 (오래된 데이터 자동 계층화)
Kafka 공식 문서 확인 Pulsar 아키텍처 상세

결론: 생태계 vs 아키텍처 유연성

Kafka와 Pulsar 중 하나를 선택하는 것은 '나쁜 기술'과 '좋은 기술'의 선택이 아니라, '성숙한 생태계'와 '현대적 아키텍처' 사이의 트레이드오프입니다.

이미 Kafka를 안정적으로 운영 중이고, 데이터 처리량이 예측 가능한 범위 내에 있다면 Kafka는 여전히 가장 안전하고 강력한 선택입니다. 풍부한 레퍼런스와 Kafka Connect, Kafka Streams와 같은 생태계는 개발 생산성을 보장합니다. 하지만, Kubernetes 기반의 클라우드 네이티브 환경에서 동적인 오토스케일링이 빈번하거나, 수십 개 이상의 팀이 공유하는 대규모 멀티테넌트 플랫폼을 구축해야 한다면 Pulsar가 제공하는 아키텍처적 이점은 Kafka의 운영 비용(OpEx)을 상회할 것입니다. 현재 조직의 인프라 성숙도와 비즈니스 요구사항(데이터 규모, 테넌트 격리 필요성)을 냉철하게 분석하여 기술 스택을 결정하십시오.

OlderNewest

Post a Comment