Pulsar의 논리적 아키텍처, Logical Architecture ("Apache Pulsar in Action" 챕터 2.2)
본 포스트는 “Manning Publications”에서 출판하고 현재 StreamNative 에서 무료 배포 중인 책 “Apache Pulsar in Action”의 내용을 정리한 내용입니다. 본 포스트는 비상업적이며 학습 목적으로 작성되었습니다. 전문적인 내용은 반드시 원서를 참조하시기 바랍니다. 모든 저작권은 해당 출판사와 저자에게 있습니다.
링크:
1. 원서(출판사) : https://www.manning.com/books/apache-pulsar-in-action
2. 원서(배포본) : https://streamnative.io/ebooks/get-your-free-copy-of-mannings-apache-pulsar-in-action
2.2 Pulsar’s logical architecture
다른 메시징 시스템들과 마찬가지로, Pulsar는 토픽의 개념을 사용해서 프로듀서와 컨슈머 간 데이터를 전송하는 메시지 채널을 나타냅니다. 그러나 Pulsar에서는 토픽의 네이밍 방식이 다른 메시징 시스템과 다릅니다. 다음 섹션에서는 Pulsar가 토픽을 저장하고 관리하기 위해 사용하는 기본적인 논리적 구조에 대해 다룰 것입니다.
2.2.1 Tenants, namespaces, and topics
이 섹션에서는 클러스터 내에서 데이터가 어떻게 구조화되고 저장되는지를 표현하는 Pulsar의 논리적 구성 요소에 대해 다룰 것입니다. Pulsar는 멀티 테넌시 시스템으로 서비스되도록 설계되어, 각각의 테넌트에게 안전하고 격리된 메시징 환경을 제공함으로써 조직 내의 여러 부서들이 활용할 수 있습니다. 이러한 Pulsar 의 설계는 단일 Pulsar 인스턴스가 기업 전체에 걸쳐 메시징 PaaS(Platform-as-a-Service)로 효과적으로 동작할 수 있도록 합니다. Figure 2.8에서처럼 Pulsar의 논리적 아키텍처는 테넌트, 네임스페이스, 그리고 토픽의 계층 구조를 통해 다중 테넌시를 지원합니다.
TENANTS
Pulsar의 계층 구조의 맨 위에는 테넌트가 있습니다. 테넌트는 특정 비즈니스 유닛, 핵심 기능 또는 제품 라인을 나타낼 수 있습니다. 테넌트는 클러스터에 분산되어 있을 수 있으며, 각각에 자체 인증 및 권한 부여 체계를 적용하여 데이터에 액세스할 수 있는 사용자를 제어합니다. 또한 테넌트는 저장 용량 할당, 메시지 유효 기간 및 격리 정책을 관리할 수 있는 관리 단위입니다.
NAMESPACES
각 테넌트는 여러 개의 네임스페이스를 가질 수 있으며, 네임스페이스는 정책을 통해 관련된 토픽을 관리하기 위한 논리적인 그룹화 메커니즘입니다. 네임스페이스 수준에서 액세스 권한을 설정하고, 복제 설정을 세밀하게 조정하며, 클러스터 간 메시지 데이터의 geo-replication을 관리하고, 네임스페이스 내의 모든 토픽의 메시지 만료를 제어할 수 있습니다.
전자 상거래 애플리케이션을 위해 Pulsar의 네임스페이스를 어떻게 구성할지 살펴보겠습니다. 보안에 민감한 결제 관련 데이터를 격리하고 접근 권한을 재무팀의 멤버들로 제한시키기 위해 별도의 "E-payments" 테넌트를 구성할 수 있습니다 (figure 2.8 참조). 그리고 감사를 수행하고 신용 카드 거래를 처리할 수 있도록 오직 재무 조직의 구성원에게만 전체 권한을 할당하는 액세스 정책을 적용할 수 있습니다.
E-payments 테넌트 내에서는 두 개의 네임스페이스를 생성하는 것을 고려할 수 있습니다. 하나는 "payments" 네임스페이스로, 신용 카드 결제 및 상품권 사용 등의 결제 내역을 포함하고, 다른 하나는 "fraud detection" 네임스페이스로, 이상금융거래로 감지되어(fraud detection) 추가 처리가 필요한 결제내역을 포함할 것입니다. 이러한 배포 전략에서 사용자 애플리케이션에게는 "payments" 네임스페이스에 대해 쓰기 전용 액세스를 제한하고, "fraud detection" 애플리케이션에게는 읽기 전용 액세스를 부여하여 이상금융거래를 분석할 수 있도록 합니다.
“fraud detection” 네임스페이스에서는 이상금융거래탐지(fraud detection) 애플리케이션에게 쓰기 액세스를 구성하여 잠재적으로 사기성 결제를 "risk score"(위험 등급) 토픽에 저장할 수 있도록 할 수 있습니다. 또한 동일한 네임스페이스에 대해 전자 상거래 애플리케이션에게 읽기 전용 액세스를 부여하여 감지된 이상거래에 대한 알림을 받아 판매 금지과 같은 적절한 조치를 취할 수 있도록 할 수 있습니다.
TOPICS
Pulsar에서는 토픽이 유일한 통신 채널 유형입니다. 모든 메시지는 토픽에 기록되고 조회(write&read)됩니다. 다른 메시징 시스템은 여러 통신 채널 유형을 지원합니다 (예: 토픽과 큐는 지원하는 메시지 소비 유형에 따라 구분됩니다). 1장에서 논의했듯이, 큐는 먼저 들어온 메시지를 독점적으로 소비하는 선입선출(FIFO) 메시지 소비를 지원하며, 토픽은 발행-구독(pub-sub) 방식으로 one-to-many 메시지 소비를 지원합니다. Pulsar는 이러한 구분을 하지 않으며, 대신 다양한 구독 타입(subscritpion type)을 활용하여 메시지 소비 패턴을 제어합니다.
Pulsar에서 파티션되지 않은 토픽은 하나의 브로커에 의해 처리됩니다. 이 브로커는 해당 토픽의 모든 메시지의 수신과 전달을 담당합니다. 따라서 단일 토픽의 처리량은 해당 토픽을 처리하는 브로커의 컴퓨팅 성능에 의해 제한됩니다.
PARTITIONED TOPICS
Pulsar 또한 파티션 토픽의 개념을 지원하며 여러 개의 브로커들로 구성할 수 있습니다. 이를 통해 부하가 여러 서버에 분산되어 훨씬 더 높은 처리량을 얻을 수 있습니다. 파티션된 토픽은 내부적으로 N개의 토픽들로 구현됩니다 --여기서 N은 파티션의 개수입니다. 여러 브로커들에게 파티션을 분배하는 작업은 Pulsar에 의해 자동으로 처리되어 사용자에게는 단순한 프로세스로 보여집니다.
토픽 파티셔닝을 개별 토픽들의 집합으로 구현함으로써 사용자는 전체 토픽을 리밸런싱하지 않고도 파티션의 수를 증가시킬 수 있습니다. 대신에, 새로운 파티션을 위해 내부 토픽들이 생성되고, 기존의 내부 토픽들에는 전혀 영향을 미치지 않으면서도 바로 새로운 파티션으로부터의 메시지를 수신할 수 있게 됩니다 (예: 기존 파티션에 대한 메시지의 읽기/쓰기가 중단되지 않습니다).
컨슈머 관점에서는 파티션된 토픽과 일반적인 토픽의 차이는 없습니다. 모든 컨슈머 구독(consumer subscription)은 파티션되지 않은 토픽과 정확히 동일하게 동작합니다. 그러나 메시지가 파티션된 토픽에 발행될 때는 큰 차이가 있습니다. 메시지 프로듀서는 메시지가 최종적으로 게시될 내부 토픽을 결정하는 책임을 갖습니다. 메시지의 키(key) 메타데이터 필드에 값(value)이 있는 경우, 프로듀서는 해당 값을 해싱(hashing)하여 발행할 토픽을 결정합니다. 이는 동일한 키를 갖는 모든 메시지가 동일한 토픽에 저장되며, 발행된 순서대로 정렬되는것을 보장합니다.
메시지를 키 없이 발행하는 경우, 프로듀서는 토픽의 파티션에 메시지를 어떻게 라우팅할지를 결정하기 위해 라우팅 모드를 설정해야 합니다. 기본 라우팅 모드는 RoundRobinPartition
으로, 이는 이름 그대로 메시지를 모든 파티션에 라운드로빈 방식으로 발행합니다. 이 접근 방식은 메시지를 파티션 간에 고르게 분산하여 발행 처리량을 극대화합니다. 또 다른 옵션으로는 SinglePartition
라우팅 모드를 사용할 수 있으며, 이는 임의로 선택한 단일 파티션에 모든 메시지를 발행합니다. 이 접근 방식은 특정 프로듀서의 메시지를 함께 그룹화하여 메시지 순서를 유지하는 데 사용될 수 있습니다. 또한 필요한 경우 사용자 정의 라우팅 구현을 통해 토픽 파티셔닝과 메시지 분배를 더욱 세밀하게 제어할 수도 있습니다.
Figure 2.9의 메시지 흐름을 살펴봅시다. 이 경우, 프로듀서는 RoundRobinPartition
모드로 구성되어 있습니다. 이 시나리오에서 프로듀서는 Pulsar 프록시에 연결하고, 작성 중인 토픽에 할당된 브로커의 IP 주소를 반환받으려 합니다. 프록시는 이를 위해 로컬 메타스토어(ZooKeeper)를 참조하여 토픽이 파티션으로 구성되어 있는지 확인하고, 지정된 번호의 파티션을 구성하는 내부 토픽들의 이름으로 변환해야 함을 확인합니다.
Figure 2.9에서 프로듀서의 라운드 로빈 라우팅 전략은 메시지를 파티션 P3 에 발행해야 함을 결정했습니다. 이 파티션은 내부 토픽 p3로 구현되어 있습니다. 프록시는 또한 내부 토픽 p3가 현재 broker-0에서 서비스되고 있음을 확인할 수 있습니다. 따라서 메시지는 해당 브로커로 라우팅되어 p3 토픽에 기록됩니다. 라우팅 모드가 라운드 로빈이므로 동일한 프로듀서의 후속 호출은 메시지를 broker-1의 내부 토픽 p4로 라우팅하게 됩니다.
2.2.2 Addressing topics in Pulsar
Pulsar의 논리적인 구성의 계층 구조는 Pulsar 내 토픽에 접근 하기 위해 사용되는 엔드포인트의 명명 규칙에 반영됩니다. Figure 2.10에서 볼 수 있듯이, Pulsar 내에서 각 토픽은 해당하는 테넌트와 네임스페이스 정보를 포함합니다. 주소에는 또한 메시지 내용이 영속 저장소에 유지되는지 아니면 bookie의 메모리 공간에만 유지되는지를 나타내는 영속성에 대한 접두사(prefix)도 포함됩니다. persistent://
접두사(prefix)로 생성된 토픽 이름인 경우, 수신되었지만 아직 확인되지 않은 모든 메시지는 여러 bookie 노드에 저장되어 브로커 장애에 대비할 수 있습니다.
Pulsar는 비영속(non-persistent) 토픽도 지원합니다. 비영속 토픽은 모든 확인되지 않은 메시지를 브로커의 메모리에 보관합니다. 비영속 토픽 이름은 non-persistent:// 접두사(prefix)로 시작하여 이 동작을 나타냅니다. 비영속 토픽을 사용할 때, 브로커는 메시지를 영속(persist)시키지 않고 즉시 모든 연결된 구독자에게 전달합니다.
비영속 전송(non-persistent delivery)을 사용할 때, 모든 브로커 장애와 토픽-구독자 간 커넥션 유실은 비영속 토픽에 있는 전송중(in-transit)인 모든 메시지들 손실로 이어집니다. 이는 토픽 구독자들이 재연결 이후에도 해당 메시지를 받을 수 없음을 의미합니다. 비영속 메시징은 데이터를 디스크에 영속화하는 데 필요한 지연 시간을 단축해서 일반적으로 영속 메시징보다 빠르지만, 메시지의 손실을 감수하는 것에 대해 확신을 가진 경우에만 사용하는 것을 권장합니다.
2.2.3 Producers, consumers, and subscriptions
Pulsar는 발행-구독(pub-sub) 패턴을 기반으로 구축되었습니다. 이 패턴에서 프로듀서는 메시지를 토픽에 발행합니다. 컨슈머는 이러한 토픽에 구독하고, 수신된 메시지를 처리하고 처리가 완료되면 ack(acknowledge) 응답을 보냅니다.
프로듀서(Producer)는 Pulsar 브로커에 직접적으로, 또는 Pulsar 프록시를 통해 간접적으로 연결해서 토픽에 메시지를 발행하는 모든 프로세스를 의미합니다. 컨슈머(Consumer)는 토픽으로부터 메시지를 수신하기 위해 Pulsar 브로커에 연결하는 모든 프로세스를 의미합니다. 컨슈머가 메시지를 성공적으로 처리한 경우, 브로커에게 확인 응답(acknowledgment)을 보내야 합니다. 이를 통해 브로커는 메시지가 수신되고 처리되었음을 알 수 있습니다. 확인 응답이 설정된 시간 내에 수신되지 않은 경우, 브로커는 해당하는 구독의 컨슈머에게 메시지를 재전달합니다.
컨슈머가 Pulsar 토픽에 연결하면, 한 개 이상의 컨슈머 그룹에게 메시지가 전달되는 방식을 지정하는 구독(subscription)이 설정됩니다. Pulsar에서는 네 가지 구독 모드인 독점(exclusive), 장애 극복(failover), 키 공유(key-shared) 및 공유(shared)가 제공됩니다. 구독 유형에 관계없이 메시지는 수신된 순서대로 전달됩니다.
구독(subscription)에 대한 정보는 로컬 Pulsar ZooKeeper 메타데이터에 보관되며, 모든 컨슈머의 HTTP 주소를 포함한 여러 정보들을 보관합니다. 각 구독에는 또한 구독에 대해 수신 및 확인된 마지막 메시지의 위치를 나타내는 커서(cursor)가 있습니다. 메시지 재전달을 방지하기 위해 이 구독 커서는 브로커 수준의 장애가 발생해도 유지되도록 북키(bookie)에 보관됩니다.
Pulsar는 토픽 당 다중 구독을 지원하며, 이를 통해 여러 컨슈머가 토픽에서 데이터를 읽을 수 있습니다. Figure 2.11에서 확인할 수 있듯이, 해당 토픽에는 Sub-A와 Sub-B라는 두 개의 다른 구독이 존재합니다. 토픽에 먼저 연결된 Sub-A 의 Consumer-A는 독점(Exlusive) 모드로 동작하고 있습니다. 이는 토픽의 모든 메시지가 Consumer-A에 의해 소비된다는 것을 의미합니다. 현재까지 Consumer-A는 처음 네 개의 메시지만 확인한 상태이므로, 구독 Sub-A의 커서 위치는 현재 5로 설정되어 있습니다.
Sub-B라는 이름의 구독은 처음 세 개의 메시지 1,2,3 이 발행된 이후에 생성되었습니다. 그러므로, 해당 구독에는 그 메시지 1,2,3 이 전달되지 않았습니다. 토픽에 대해 생성된 모든 구독은 해당 토픽의 가장 처음 메시지부터 시작된다는 오해가 일반적이지만, 구독을 시작한 후에 토픽에 발행된 메시지만을 수신할 수 있다는 것을 여기서 그 점을 설명하고 Sub-B 를 나타낸 것입니다.
또한 Sub-B가 Shared 모드로 동작하고 있기 때문에 메시지는 그룹 내의 모든 컨슈머에게 분산되며 각 메시지는 한 컨슈머에서만 처리됩니다. 또한 Sub-B의 커서가 Sub-A의 커서보다 앞에 위치한 것을 볼 수 있는데, 여러 컨슈머에게 메시지를 분산하여 처리할 때 이런 상황은 자주 발생하비다.
2.2.4 Subscription types
Pulsar에서 모든 컨슈머는 토픽에서 데이터를 소비(consume)하기 위해 구독(subscription)을 사용합니다. 구독은 단순히 주어진 토픽의 컨슈머에게 메시지가 전달되는 방식을 설정하는 규칙입니다. Pulsar 구독은 여러 애플리케이션 간에 공유될 수 있으며, 사실상 대부분의 구독 유형은 그러한 사용 패턴을 위해 특별히 설계되었습니다. Figure 2.12에서 처럼 Pulsar는 독점(Exclusive), 장애 극복(Failover), 공유(Shared), 키 공유(Key-Shared)의 네 가지 다른 구독 유형을 지원합니다.
Pulsar 토픽은 동시에 여러 subscription 타입의 사용을 지원하며 서로 다른 consumption 패턴을 가진 많은 애플리케이션들이 단 하나의 토픽을 사용할 수 있게 합니다. 또한, 동일한 토픽에서 여러 subsription 들이 동일한 구독 유형일 필요가 없다는 점도 중요합니다. 이를 통해 하나의 토픽을 사용하여 큐(Queuing) 및 스트리밍(Streaming) 유즈케이스를 한번에 처리할 수 있습니다.
Pulsar의 각 구독 유형은 다양한 유즈케이스에 적합한 서비스를 제공하므로, 각 구독 유형들을 제대로 이해하고 적절하게 사용하는 것이 중요합니다. 이전에 다룬, 실시간으로 주식 시세 정보를 "stock quotes"라는 토픽에 스트리밍하는 금융 서비스 회사의 시나리오를 다시 살펴보겠습니다. 이 정보를 기업 전체에서 공유하려고 하는 상황에서 각각의 구독 모드가 동일한 사용 사례에 대해 어떻게 사용되는지 살펴보겠습니다.
EXCLUSIVE
독점(exclusive) 구독은 해당 구독에 대해 단 하나의 컨슈머만 메시지를 consume 할 수 있도록 허용합니다. 다른 컨슈머가 동일한 구독으로 토픽을 구독하려고 시도하면 연결은 되지 않으며 예외가 발생됩니다. 이 구독 모드는 각 메시지가 명시된 컨슈머에 의해 정확히 한 번만 처리되고 처리되는 것을 보장하고자 할 때 사용됩니다.
금융 서비스 기관 내에서 데이터 사이언스팀은 독점 구독을 사용하여 주식 토픽 데이터를 머신러닝 학습 모델에 공급하면서 모델을 훈련하거나 검증할 것입니다. 이를 통해 주식 기록을 정확히 수신된 순서대로 처리하여 정확한 시간 순서의 주식 시세 스트림을 제공할 수 있게 됩니다. 각 모델은 자신만의 복사본을 받기위해 독점 구독이 필요하며, Figure 2.13 에서 나타냅니다.
FAILOVER SUBSCRIPTIONS
Failover 구독은 여러 컨슈머가 구독에 연결되지만, 메시지를 수신하는 컨슈머는 하나만 선택됩니다. 이 설정은 컨슈머의 장애 발생 시 메시지 처리를 계속할 수 있는 장애 극복(failover) 컨슈머를 제공하는 데 사용됩니다. 활성 컨슈머가 메시지를 처리하지 못하면 Pulsar는 자동으로 목록에서 다음 컨슈머로 장애 극복(failover)하며 메시지 전달 작업을 지속합니다.
이 구독 유형은 컨슈머의 높은 가용성과 더불어 단일 처리 시맨틱(single processing semantics)가 요구될 때 유용합니다. 시스템 장애, 또는 그 어떤 이유로든 첫 번째 컨슈머가 다운되는 경우에도 다른 컨슈머가 그 역할을 맡으며 애플리케이션이 메시지 처리를 지속하고자 하는 경우에 유용합니다. 일반적으로 이런 컨슈머들은 서로 다른 호스트 머신(하드웨어), 또는 데이터 센터에 분산되어 있어 응용 프로그램이 다중 장애(multiple outage) 상황을 견딜 수 있도록 보장합니다. Figure 2.14에서 확인할 수 있듯이, Consumer-A는 Active 컨슈머이며, Consumer-A가 어떤 이유로든 연결이 끊어지면 다음으로 메시지를 받을 예비 컨슈머인 Consumer-B가 대기하고 있습니다.
예시로, 금융 서비스 회사의 데이터 사이언스 팀이 주식 시세 토픽에서 생성된 시장 변동성 점수를 다른 모델의 점수와 결합하여 트레이딩 팀을 위한 종합적인 추천을 생성하는 모델 중 하나를 배포한 경우를 생각해 볼 수 있습니다. 여기서 트레이딩 팀이 정보를 바탕으로 거래 결정을 내리는 데 정확히 한 개의 이 모델 인스턴스가 계속 실행되어야 하는 것이 매우 중요합니다. 여러 인스턴스를 실행하고 추천을 생성하는 경우 추천에 편향(bais)이 발생 할 수 있습니다.
SHARED SUBSCRIPTIONS
Shared 구독 유형 또한 여러 컨슈머를 해당 구독에 연결 시킬 수 있습니다. Sharedd 구독은 한 번에 하나의 활성 컨슈머만 지원하는 장애 극복 구독과는 달리, 여러 컨슈머가 메시지를 동시에 수신할 수 있습니다. 메시지는 라운드 로빈 방식으로 등록된 컨슈머들에게 전달되며, 특정 메시지는 한 컨슈머에게만 전달됩니다. Figure 2.15와 같습니다.
이 구독 유형은 메시지 순서가 중요하지 않은 작업 큐를 구현하는 데 유용합니다. 들어오는 메시지를 처리하기 위해 토픽의 컨슈머 수를 손쉽게 확장할 수 있기 때문입니다. Shared 구독당 컨슈머 수에 상한선이 없으며, 이를 통해 컨슈머 수를 늘리며 처리량을 스토리지 레이어의 물리적인 제한 그 이상으로까지도 증가시킬 수 있습니다.
예시에서의 금융 서비스 기관 내에서는, “내부 거래 플랫폼”, “알고리즘 트레이딩 시스템” 및 “고객을 대상으로 하는 웹사이트”와 같은 중요한 업무 애플리케이션은 이러한 구독 유형에서 혜택을 받을 수 있습니다. Figure 2.15에서 처럼 각각의 애플리케이션은 자체 공유 구독을 사용하여 주식 토픽에 게시된 모든 메시지를 수신하도록 보장할 것입니다.
KEY-SHARED SUBSCRIPTIONS
키 공유(key-shared) 구독 또한 여러 개의 동시 컨슈머를 허용합니다. 하지만 메시지를 라운드로빈(round-robin) 방식으로 컨슈머에게 전달하는 Shared 구독 유형과 달리, 보조 키 인덱스를 추가하여 동일한 키를 가진 메시지가 동일한 컨슈머에게 전달되도록 보장합니다. 이 구독은 SQL의 분산 GROUP BY
와 유사하게 동작하며, 유사한 키를 가진 데이터가 함께 그룹화됩니다. 이점은 데이터를 소비하기 전에 사전에 정렬하고자 하는 경우에 특히 유용합니다.
예시로 “stock” 토픽의 데이터에 대해 비즈니스 분석 팀이 분석 작업을 수행해야 하는 시나리오를 고려해 보겠습니다. 키 공유(key-shared) 구독을 사용함으로써, 해당 주식 심볼(ticker symbol)에 대한 모든 데이터가 동일한 컨슈머에 의해 처리되어, Figure 2.16에 나타난 것과 같이, 다른 데이터 스트림과 이 데이터를 조인하는 작업이 용이해집니다.
요약하면, Exclusive(독점) 및 Failover (장애극복) 구독 유형은 하나의 구독에 하나의 토픽 파티션당 하나의 컨슈머만 허용하며 메시지가 수신된 순서대로 소비되는 것을 보장합니다. 이러한 구독은 엄격한 순서가 필요한 스트리밍 사용 사례에 가장 적합합니다.
반면에, Shared 구독은 하나의 토픽 파티션에 여러 컨슈머를 허용합니다. 구독 내의 각 컨슈머는 토픽에 발행된 메시지 중 일부만을 수신합니다. Shared 구독은 엄격한 메시지 순서가 필요하지 않지만 높은 처리량이 요구되는 작업 큐(Queue) 사용 사례에 가장 적합합니다.