아키텍처 개요
아키텍처 개요 (Architecture Overview)
Pulsar 인스턴스는 최상위에서 하나 이상의 Pulsar 클러스터로 구성돼요. 인스턴스 내의 클러스터들은 서로 데이터를 복제할 수 있어요. 이 페이지에서는 Pulsar 클러스터를 구성하는 브로커, BookKeeper 클러스터, 메타데이터 스토어의 역할과 상호작용을 설명해요.
출처: 문서
본문
최상위에서 Pulsar 인스턴스는 하나 이상의 Pulsar 클러스터로 구성돼요. 인스턴스 내의 클러스터들은 서로 데이터를 복제(replicate)할 수 있어요.
Pulsar 클러스터는 다음 컴포넌트로 구성돼요.
- 하나 이상의 브로커: 프로듀서의 들어오는 메시지를 처리·로드 밸런싱하고, 컨슈머에게 메시지를 디스패치하며, Pulsar 메타데이터 스토어와 통신해 다양한 조정 작업을 처리하고, BookKeeper 인스턴스(즉 부키)에 메시지를 저장하며, 메타데이터 스토어를 통해 클러스터 연산을 조정해요.
- BookKeeper 클러스터: 하나 이상의 부키로 구성되며 메시지의 영속 저장을 처리해요.
- 메타데이터 스토어 클러스터(Oxia, ZooKeeper 또는 다른 지원 백엔드): 조정 작업과 클러스터별 메타데이터 저장을 처리해요.
아래 다이어그램은 Pulsar 클러스터를 보여줘요.
더 넓은 인스턴스 수준에서, 구성 스토어(Configuration store)라고 불리는 인스턴스 전체 메타데이터 스토어 클러스터가 여러 클러스터를 포함하는 조정 작업, 예를 들어 지리 복제(geo-replication)를 처리해요.
브로커 (Brokers)
Pulsar 메시지 브로커는 무상태(stateless) 컴포넌트로, 주로 두 개의 다른 컴포넌트를 실행하는 책임을 져요.
- HTTP 서버: 관리 작업과 프로듀서·컨슈머를 위한 토픽 룩업 둘 다를 위한 REST API를 노출해요. 프로듀서는 브로커에 연결해 메시지를 게시하고, 컨슈머는 브로커에 연결해 메시지를 소비해요.
- 디스패처(dispatcher): 모든 데이터 전송에 사용되는 커스텀 바이너리 프로토콜을 통한 비동기 TCP 서버예요.
메시지는 성능을 위해 보통 managed ledger 캐시에서 디스패치되지만, 백로그가 캐시 크기를 초과하면 예외예요. 백로그가 캐시에 너무 커지면 브로커는 BookKeeper에서 엔트리를 읽기 시작해요.
마지막으로, 전역 토픽에서 지리 복제를 지원하기 위해 브로커는 로컬 지역에서 게시된 엔트리를 테일링하고 Pulsar Java client 라이브러리를 사용해 원격 지역에 다시 게시하는 복제기(replicator)를 관리해요.
Pulsar 브로커 관리 가이드는 brokers 가이드를 참고해요.
클러스터 (Clusters)
Pulsar 인스턴스는 하나 이상의 Pulsar 클러스터로 구성돼요. 클러스터는 다시 다음으로 구성돼요.
- 하나 이상의 Pulsar 브로커
- 클러스터 수준 구성과 조정에 사용되는 메타데이터 스토어(Oxia 또는 ZooKeeper)
- 메시지 영속 저장에 사용되는 부키 앙상블
클러스터는 지리 복제를 사용해 서로 복제할 수 있어요.
Pulsar 클러스터 관리 가이드는 clusters 가이드를 참고해요.
메타데이터 스토어 (Metadata store)
Pulsar 메타데이터 스토어는 Pulsar 클러스터의 모든 메타데이터, 예를 들어 토픽 메타데이터, 스키마, 브로커 로드 데이터 등을 유지해요. Pulsar는 배포 아키텍처와 운영 요구 사항에 유연성을 제공하기 위해 여러 메타데이터 스토어 백엔드를 지원해요.
지원되는 메타데이터 스토어 백엔드 (Supported Metadata Store Backends)
- Oxia — 새 클러스터에 권장. 대규모 분산 시스템을 위해 설계된 견고하고 확장 가능한 메타데이터 스토어·조정 시스템으로, 실시간 데이터 관리를 최적화하기 위한 스트림 인덱스 저장에 대한 내장 지원을 갖고 있어요.
- Apache ZooKeeper — 강한 일관성 보장을 가진 프로덕션 준비 메타데이터 스토어. Pulsar 바이너리 패키지와 함께 제공돼요.
- RocksDB — standalone Pulsar 배포를 위한 임베디드 key-value 스토어로, 외부 조정 서비스의 필요를 없애요.
구성 (Configuration)
metadataStoreUrl 파라미터로 메타데이터 스토어를 구성할 수 있어요.
# Oxia (recommended)
metadataStoreUrl=oxia://oxia-server:6648/broker
# ZooKeeper
metadataStoreUrl=zk:my-zk-1:2181,my-zk-2:2181,my-zk-3:2181
# RocksDB (standalone)
metadataStoreUrl=rocksdb:///path/to/data
배포 고려 사항 (Deployment Considerations)
Pulsar 메타데이터 스토어는 별도 클러스터에 배포하거나 기존 인프라와 통합할 수 있어요. Pulsar 메타데이터와 BookKeeper 메타데이터 둘 다에 하나의 메타데이터 스토어 클러스터를 사용할 수 있어요. 기존 BookKeeper 클러스터에 연결된 Pulsar 브로커를 배포하려면 Pulsar 메타데이터 스토어와 BookKeeper 메타데이터 스토어를 각각 위한 별도 클러스터를 배포해야 해요.
Pulsar 인스턴스에서:
- 구성 스토어 쿼럼은 테넌트, 네임스페이스 및 전역적으로 일관되어야 하는 다른 엔티티의 구성을 저장해요.
- 각 클러스터는 어떤 브로커가 어떤 토픽을 담당하는지, 소유권 메타데이터, 브로커 로드 보고서, BookKeeper 레저 메타데이터 등 클러스터별 구성과 조정을 저장하는 자체 로컬 메타데이터 스토어 앙상블을 가져요.
구성 스토어 (Configuration store)
구성 스토어는 구성별 작업에 사용되는 메타데이터 스토어 쿼럼(Oxia 또는 ZooKeeper)이며, 클러스터, 테넌트, 네임스페이스, 파티션 토픽 관련 구성 등 Pulsar 인스턴스의 모든 구성을 유지해요. Pulsar 인스턴스는 단일 로컬 클러스터, 여러 로컬 클러스터, 또는 여러 교차-지역 클러스터를 가질 수 있어요. 따라서 구성 스토어는 Pulsar 인스턴스 아래 여러 클러스터에 걸쳐 구성을 공유할 수 있어요. 구성 스토어는 별도 클러스터에 배포하거나 기존 메타데이터 스토어 클러스터를 공유할 수 있어요.
영속 저장 (Persistent storage)
Pulsar는 애플리케이션에 보장된 메시지 전달을 제공해요. 메시지가 Pulsar 브로커에 성공적으로 도착하면 의도한 대상에 전달될 거예요.
이 보장은 미-승인 메시지가 컨슈머에게 전달되고 승인될 때까지 내구성 있게 저장되는 것을 요구해요. 이러한 메시징 모드는 흔히 영속 메시징(persistent messaging)이라 불려요. Pulsar에서는 모든 메시지의 N개 복사본이 디스크에 저장·동기화되며, 예를 들어 각 서버에 미러링된 RAID 볼륨이 있는 두 서버에 걸쳐 4개 복사본이 저장돼요.
Apache BookKeeper
Pulsar는 영속 메시지 저장을 위해 Apache BookKeeper라는 시스템을 사용해요. BookKeeper는 Pulsar에 여러 핵심 장점을 제공하는 분산 쓰기-앞 로그(WAL) 시스템이에요.
- Pulsar가 레저(ledger)라는 많은 독립 로그를 활용할 수 있게 해요. 시간이 지나며 토픽에 여러 레저를 만들 수 있어요.
- 엔트리 복제를 처리하는 순차 데이터에 대해 매우 효율적인 저장을 제공해요.
- 다양한 시스템 실패가 있어도 레저의 읽기 일관성을 보장해요.
- 부키 간에 I/O를 고르게 분산해요.
- 용량과 처리량 모두에서 수평 확장이 가능해요. 클러스터에 부키를 더 추가하면 용량을 즉시 늘릴 수 있어요.
- 부키는 동시 읽기·쓰기가 있는 수천 개 레저를 처리하도록 설계됐어요. 저널용과 일반 저장용으로 여러 디스크 디바이스를 사용해 부키는 읽기 연산의 효과를 진행 중인 쓰기 연산의 지연에서 분리할 수 있어요.
메시지 데이터 외에 커서(cursors)도 BookKeeper에 영속 저장돼요. 커서는 컨슈머의 구독 위치예요. BookKeeper는 Pulsar가 컨슈머 위치를 확장 가능한 방식으로 저장할 수 있게 해요.
현재 Pulsar는 영속 메시지 저장을 지원해요. 이것이 모든 토픽 이름에 있는 영속(persistent)이라는 단어를 설명해요. 예시는 다음과 같아요.
persistent://my-tenant/my-namespace/my-topic
Pulsar는 임시적(ephemeral) 비-영속(non-persistent) 메시지 저장도 지원해요.
브로커와 부키가 어떻게 상호작용하는지 아래 다이어그램에서 볼 수 있어요.
레저 (Ledgers)
레저는 단일 작성자가 여러 BookKeeper 저장 노드(부키)에 할당되는 추가-전용(append-only) 데이터 구조예요. 레저 엔트리는 여러 부키에 복제돼요. 레저 자체는 매우 단순한 의미론을 가져요.
- Pulsar 브로커가 레저를 만들고, 엔트리를 추가하고, 레저를 닫을 수 있어요.
- 레저가 닫힌 후 — 명시적으로든 작성자 프로세스가 크래시해서든 — 읽기 전용 모드로만 열 수 있어요.
- 마지막으로 레저의 엔트리가 더 이상 필요 없으면 전체 레저를 시스템(모든 부키에 걸쳐)에서 삭제할 수 있어요.
레저 읽기 일관성 (Ledger read consistency)
Bookkeeper의 주요 강점은 실패가 있어도 레저에서 읽기 일관성을 보장한다는 것이에요. 레저는 단일 프로세스만 쓸 수 있으므로, 그 프로세스는 합의를 얻을 필요 없이 엔트리를 매우 효율적으로 추가할 수 있어요. 실패 후 레저는 복구 과정을 거쳐 레저의 상태를 확정하고 로그에 마지막으로 커밋된 엔트리를 확립해요. 그 후 레저의 모든 읽는 이는 정확히 같은 내용을 보도록 보장돼요.
관리형 레저 (Managed ledgers)
BookKeeper 레저가 단일 로그 추상화를 제공하므로, 레저 위에 managed ledger라는 라이브러리가 개발됐어요. managed ledger는 단일 토픽의 저장 계층을 나타내요. managed ledger는 각각 자체 위치를 가진, 스트림 끝에 계속 추가하는 단일 작성자와 스트림을 소비하는 여러 커서로 구성된 메시지 스트림의 추상화를 나타내요.
내부적으로 단일 managed ledger는 여러 BookKeeper 레저를 사용해 데이터를 저장해요. 여러 레저를 사용하는 두 가지 이유는 다음과 같아요.
- 실패 후 레저는 더 이상 쓸 수 없고 새 레저를 만들어야 해요.
- 모든 커서가 포함된 메시지를 소비하면 레저를 삭제할 수 있어요. 이는 레저의 주기적 롤오버를 가능하게 해요.
저널 저장 (Journal storage)
BookKeeper에서 저널(journal) 파일은 BookKeeper 트랜잭션 로그를 포함해요. 레저를 업데이트하기 전에 부키는 업데이트를 설명하는 트랜잭션이 영속(비휘발성) 저장소에 기록되는지 확인해야 해요. 새 저널 파일은 부키가 시작되거나 이전 저널 파일이 저널 파일 크기 임계값(journalMaxSizeMB 파라미터로 구성)에 도달하면 생성돼요.
Pulsar 프록시 (Pulsar proxy)
Pulsar 클라이언트가 Pulsar 클러스터와 상호작용하는 한 가지 방법은 Pulsar 메시지 브로커에 직접 연결하는 것이에요. 하지만 어떤 경우에는 클라이언트가 브로커 주소에 직접 접근하지 못해서 이런 직접 연결이 불가능하거나 바람직하지 않아요. 예를 들어 클라우드 환경이나 Kubernetes 또는 유사한 플랫폼에서 Pulsar를 실행한다면 클라이언트의 브로커 직접 연결은 불가능할 가능성이 높아요.
Pulsar proxy는 클러스터의 모든 브로커에 대한 단일 게이트웨이 역할을 해 이 문제의 해결책을 제공해요. Pulsar 프록시(다시 말하지만 선택적)를 실행하면 Pulsar 클러스터와의 모든 클라이언트 연결이 브로커와 직접 통신하는 대신 프록시를 통해 흐르게 돼요.
성능과 내결함성을 위해 원하는 만큼 많은 Pulsar 프록시 인스턴스를 실행할 수 있어요.
아키텍처적으로 Pulsar 프록시는 필요로 하는 모든 정보를 메타데이터 스토어에서 얻어요. 머신에서 프록시를 시작할 때 클러스터별 및 인스턴스 전체 구성 스토어 클러스터의 메타데이터 스토어 연결 문자열만 제공하면 돼요. 예시는 다음과 같아요.
cd /path/to/pulsar/directory
# Using Oxia (recommended)
bin/pulsar proxy \
--metadata-store oxia://oxia-1.example.com:6648/broker \
--configuration-metadata-store oxia://oxia-1.example.com:6648/broker
# Using ZooKeeper
bin/pulsar proxy \
--metadata-store zk:my-zk-1:2181,my-zk-2:2181,my-zk-3:2181 \
--configuration-metadata-store zk:my-zk-1:2181,my-zk-2:2181,my-zk-3:2181
Pulsar 프록시 문서 (Pulsar proxy docs)
Pulsar 프록시 사용에 대한 문서는 Pulsar proxy admin documentation을 참고해요.
Pulsar 프록시에 대해 알아둘 몇 가지 중요한 점은 다음과 같아요.
- 연결하는 클라이언트는 Pulsar 프록시를 사용하기 위해 특정 구성을 제공할 필요가 없어요. 서비스 URL에 사용된 IP를 업데이트하는 것 외에는 기존 애플리케이션의 클라이언트 구성을 업데이트할 필요가 없어요(예를 들어 Pulsar 프록시 위에 로드 밸런서를 실행한다면).
- TLS 암호화와 mTLS 인증이 Pulsar 프록시에서 지원돼요.
서비스 디스커버리 (Service discovery)
서비스 디스커버리는 연결하는 클라이언트가 단일 URL만으로 전체 Pulsar 인스턴스와 상호작용할 수 있게 하는 메커니즘이에요.
원한다면 자체 서비스 디스커버리 시스템을 사용할 수 있어요. 자체 시스템을 사용한다면 요구 사항이 하나 있어요. 클라이언트가 http://pulsar.us-west.example.com:8080 같은 엔드포인트에 HTTP 요청을 수행할 때, 클라이언트가 DNS, HTTP·IP 리다이렉트, 또는 다른 수단을 통해 원하는 클러스터의 어떤 활성 브로커로 리다이렉트되어야 해요.
아래 다이어그램은 Pulsar 서비스 디스커버리를 보여줘요.
이 다이어그램에서 Pulsar 클러스터는 단일 DNS 이름 pulsar-cluster.acme.com으로 접근 가능해요. 예를 들어 Python 클라이언트가 이 Pulsar 클러스터에 다음과 같이 접근할 수 있어요.
from pulsar import Client
client = Client('pulsar://pulsar-cluster.acme.com:6650')
note
Pulsar에서 각 토픽은 단 하나의 브로커가 처리해요. 클라이언트가 토픽을 읽고·업데이트하고·삭제하기 위한 초기 요청은 토픽 소유자가 아닐 수 있는 브로커로 보내져요. 그 브로커가 이 토픽의 요청을 처리할 수 없으면 적절한 브로커로 요청을 리다이렉트해요.
더 알아보기 (Learn more)
- 브로커 관리 가이드는 brokers 문서를 참고해요.
- 클러스터 관리 가이드는 clusters 문서를 참고해요.
- Pulsar 프록시 사용 문서는 Pulsar proxy admin documentation을 참고해요.
- 메타데이터 스토어 구성은 Configure metadata store 문서를 참고해요.
- 지리 복제의 개념은 지리 복제 문서를 참고해요.