메타데이터 스토어와 BookKeeper 관리
메타데이터 스토어와 BookKeeper 관리 (Metadata store and BookKeeper administration)
Pulsar는 필수 작업을 위해 두 외부 시스템에 의존해요. 메타데이터 스토어는 다양한 구성·조정 관련 작업을 담당하고, BookKeeper는 메시지 데이터의 영속 저장을 담당해요. 이 페이지에서는 메타데이터 스토어(ZooKeeper 중심)와 BookKeeper의 배포·구성·관리 방법을 설명해요.
출처: 문서
본문
Pulsar는 필수 작업을 위해 두 외부 시스템에 의존해요.
- 메타데이터 스토어는 다양한 구성 관련 및 조정 관련 작업을 담당해요. 새 클러스터에는 Oxia가 권장 메타데이터 스토어이며, ZooKeeper도 완전히 지원되고 이 페이지에서 다뤄요. 지원되는 모든 백엔드는 Configure metadata store를 참고해요.
- BookKeeper는 메시지 데이터의 영속 저장을 담당해요.
이 다이어그램은 Pulsar 클러스터에서 메타데이터 스토어와 BookKeeper의 역할을 보여줘요.
각 Pulsar 클러스터는 하나 이상의 메시지 브로커로 구성돼요. 각 브로커는 부키 앙상블에 의존해요.
Oxia
Oxia는 새 Pulsar 클러스터의 권장 메타데이터 스토어예요. Pulsar가 필요로 하는 구성과 조정 서비스를 제공하며, ZooKeeper보다 대규모 클러스터에서 더 나은 확장성을 제공해요.
Oxia를 메타데이터 스토어로 사용하려면:
- Oxia 클러스터를 배포하고 대상 네임스페이스를 만들어요. 배포 지침은 Oxia 문서를 참고해요.
- Configure metadata store에서 설명한 대로
metadataStoreUrl과configurationMetadataStoreUrl을 설정해 Pulsar가 그것을 사용하도록 구성해요.
다운타임 없이 기존 ZooKeeper 기반 클러스터를 Oxia로 옮기려면 Migrate metadata store를 참고해요.
ZooKeeper
ZooKeeper는 전통적인 메타데이터 스토어 옵션이며 Pulsar 바이너리 패키지와 함께 제공돼요. ZooKeeper를 메타데이터 스토어로 사용할 때 각 Pulsar 인스턴스는 두 개의 별도 ZooKeeper 쿼럼에 의존해요.
- Local ZooKeeper는 클러스터 수준에서 작동하며 클러스터별 구성 관리와 조정을 제공해요. 각 Pulsar 클러스터는 전용 ZooKeeper 클러스터가 필요해요.
- Configuration Store는 인스턴스 수준에서 작동하며 전체 시스템(즉 클러스터 전체)의 구성 관리를 제공해요. 독립된 머신 클러스터 또는 local ZooKeeper가 사용하는 것과 같은 머신이 구성 스토어 쿼럼을 제공할 수 있어요.
Local ZooKeeper 배포 (Deploy local ZooKeeper)
ZooKeeper는 Pulsar의 다양한 필수 조정 관련 및 구성 관련 작업을 관리해요.
Pulsar 인스턴스를 배포하려면 Pulsar 클러스터마다 하나의 local ZooKeeper 클러스터를 구축해야 해요.
시작하려면 conf/zookeeper.conf 파일에 지정된 쿼럼 구성에 모든 ZooKeeper 서버를 추가해요. 클러스터의 각 노드에 대해 server.N 줄을 구성에 추가하는데, 여기서 N은 ZooKeeper 노드의 번호예요. 다음은 3-노드 클러스터의 예시예요.
server.1=zk1.us-west.example.com:2888:3888
server.2=zk2.us-west.example.com:2888:3888
server.3=zk3.us-west.example.com:2888:3888
각 호스트에서 각 노드의 myid 파일에 노드 ID를 지정해야 해요. myid 파일은 기본적으로 각 서버의 data/zookeeper 폴더에 있어요(dataDir 파라미터로 파일 위치를 바꿀 수 있어요).
myid와 더 자세한 정보는 ZooKeeper 문서의 Multi-server setup guide를 참고해요.
예를 들어 zk1.us-west.example.com의 ZooKeeper 서버에서 myid 값을 다음과 같이 설정할 수 있어요.
mkdir -p data/zookeeper
echo 1 > data/zookeeper/myid
zk2.us-west.example.com에서는 명령이 echo 2 > data/zookeeper/myid이고 그 다음도 마찬가지예요.
각 서버를 zookeeper.conf 구성에 추가하고 각 서버가 적절한 myid 엔트리를 가지면, pulsar-daemon CLI 도구로 모든 호스트에서 ZooKeeper를 시작할 수 있어요(백그라운드, nohup 사용).
bin/pulsar-daemon start zookeeper
구성 스토어 배포 (Deploy configuration store)
위 섹션에서 구성·시작한 ZooKeeper 클러스터는 local ZooKeeper 클러스터로, 단일 Pulsar 클러스터를 관리하는 데 사용할 수 있어요. local 클러스터 외에도 전체 Pulsar 인스턴스는 일부 인스턴스 수준 구성·조정 작업을 처리하기 위해 구성 스토어도 필요해요.
단일-클러스터 인스턴스를 배포한다면 구성 스토어용으로 별도 클러스터가 필요하지 않아요. 하지만 멀티-클러스터 인스턴스를 배포한다면 구성 작업용으로 별도 ZooKeeper 클러스터를 구축해야 해요.
단일-클러스터 Pulsar 인스턴스 (Single-cluster Pulsar instance)
Pulsar 인스턴스가 단일 클러스터로만 구성된다면, local ZooKeeper 쿼럼과 같은 머신에 구성 스토어를 배포하되 다른 TCP 포트에서 실행할 수 있어요.
단일-클러스터 인스턴스에서 ZooKeeper 구성 스토어를 배포하려면 local ZooKeeper와 같은 방법으로 conf/global_zookeeper.conf의 구성 파일에 local 쿼럼이 사용하는 것과 같은 ZooKeeper 서버를 추가하되, 다른 포트(2181은 ZooKeeper 기본)를 사용해야 해요. 다음은 3-노드 ZooKeeper 클러스터에 포트 2184를 사용하는 예시예요.
clientPort=2184
server.1=zk1.us-west.example.com:2185:2186
server.2=zk2.us-west.example.com:2185:2186
server.3=zk3.us-west.example.com:2185:2186
이전처럼 각 서버의 data/global-zookeeper/myid에 myid 파일을 만들어요.
멀티-클러스터 Pulsar 인스턴스 (Multi-cluster Pulsar instance)
클러스터가 서로 다른 지리적 지역에 분산된 전역 Pulsar 인스턴스를 배포할 때, 구성 스토어는 전체 지역을 아우르는 실패와 파티션도 견딜 수 있는 고가용성·강한 일관성의 메타데이터 스토어 역할을 해요.
핵심은 ZK 쿼럼 멤버를 최소 3개 지역에 걸쳐 분산하고 다른 지역은 옵저버(observer)로 실행하는 것이에요.
구성 스토어 서버의 예상 부하가 매우 낮으므로 local ZooKeeper 쿼럼에 사용한 것과 같은 호스트를 공유할 수 있어요.
예를 들어 us-west, us-east, us-central, eu-central, ap-south 클러스터가 있는 Pulsar 인스턴스를 가정할 수 있어요. 각 클러스터는 zk[1-3].${CLUSTER}.example.com 같은 자체 local ZK 서버가 있다고 가정해요.
이 시나리오에서 몇 개 클러스터에서 쿼럼 참가자를 고르고 나머지는 모두 ZK 옵저버가 되게 하려 해요. 예를 들어 7-서버 쿼럼을 만들려면 us-west에서 3개, us-central에서 2개, us-east에서 2개를 고를 수 있어요.
이렇게 하면 이 지역 중 하나에 연결할 수 없어도 구성 스토어에 쓰는 것이 가능해져요.
모든 서버의 ZK 구성은 아래와 같아요.
clientPort=2184
server.1=zk1.us-west.example.com:2185:2186
server.2=zk2.us-west.example.com:2185:2186
server.3=zk3.us-west.example.com:2185:2186
server.4=zk1.us-central.example.com:2185:2186
server.5=zk2.us-central.example.com:2185:2186
server.6=zk3.us-central.example.com:2185:2186:observer
server.7=zk1.us-east.example.com:2185:2186
server.8=zk2.us-east.example.com:2185:2186
server.9=zk3.us-east.example.com:2185:2186:observer
server.10=zk1.eu-central.example.com:2185:2186:observer
server.11=zk2.eu-central.example.com:2185:2186:observer
server.12=zk3.eu-central.example.com:2185:2186:observer
server.13=zk1.ap-south.example.com:2185:2186:observer
server.14=zk2.ap-south.example.com:2185:2186:observer
server.15=zk3.ap-south.example.com:2185:2186:observer
또한 ZK 옵저버에는 다음이 필요해요.
peerType=observer
서비스 시작 (Start the service)
구성 스토어 구성이 마련되면 pulsar-daemon으로 서비스를 시작할 수 있어요.
bin/pulsar-daemon start configuration-store
ZooKeeper 구성 (ZooKeeper configuration)
Pulsar에서 ZooKeeper 구성은 Pulsar 설치본의 conf 디렉터리에 있는 두 개의 별도 구성 파일로 처리돼요.
conf/zookeeper.conf파일은 local ZooKeeper의 구성을 처리해요.conf/global-zookeeper.conf파일은 구성 스토어의 구성을 처리해요.- 자세한 내용은 parameters를 참고해요.
배치 연산 구성 (Configure batching operations)
배치 연산을 사용하면 ZooKeeper 클라이언트와 서버 간 원격 프로시저 호출(RPC) 트래픽이 줄어요. 각 배치 연산이 여러 읽기·쓰기 연산을 포함하는 단일 ZooKeeper 트랜잭션에 해당하므로 쓰기 트랜잭션 수도 줄어요.
다음 그림은 1초에 ZooKeeper에 요청할 수 있는 배치 읽기/쓰기 연산의 기본 벤치마크를 보여줘요.

배치 연산을 활성화하려면 브로커 쪽에서 metadataStoreBatchingEnabled 파라미터를 true로 설정해요.
BookKeeper
BookKeeper는 Pulsar가 모든 영속 데이터를 저장하는 데 사용하는 확장 가능하고 저지연의 영속 로그 저장 서비스예요. BookKeeper는 레저(ledger)라고 불리는 독립 메시지 로그의 읽기 일관성을 보장하는 분산 쓰기-앞 로그(WAL) 시스템이에요. 개별 BookKeeper 서버는 부키(bookie)라고도 불러요.
Pulsar에서 메시지 영속화, 보존, 만료를 관리하려면 cookbook을 참조해요.
하드웨어 요구 사항 (Hardware requirements)
부키 호스트는 메시지 데이터를 디스크에 저장해요. 최적의 성능을 제공하려면 부키에 적합한 하드웨어 구성이 있어야 해요. 다음은 부키 하드웨어 용량의 두 가지 핵심 차원이에요.
- 디스크 I/O 용량 읽기/쓰기
- 저장 용량
부키에 기록되는 메시지 엔트리는 기본적으로 Pulsar 브로커에 승인을 반환하기 전에 항상 디스크에 동기화돼요. 낮은 쓰기 지연을 보장하기 위해 BookKeeper는 여러 디바이스를 사용하도록 설계됐어요.
- **저널(journal)**은 내구성을 보장해요. 순차 쓰기의 경우 부키 호스트에서 빠른 fsync 연산이 중요해요. 일반적으로 작고 빠른 SSD면 충분하거나, RAID 컨트롤러와 배터리 백업 쓰기 캐시가 있는 HDD도 충분해요. 두 솔루션 모두 ~0.4ms의 fsync 지연에 도달할 수 있어요.
- 레저 저장 디바이스는 데이터를 저장해요. 쓰기는 백그라운드에서 일어나므로 쓰기 I/O는 큰 문제가 아니에요. 읽기는 대부분 순차적으로 일어나고 백로그는 컨슈머 배수의 경우에만 비워져요. 많은 양의 데이터를 저장하려면 일반적으로 RAID 컨트롤러가 있는 여러 HDD가 포함돼요.
BookKeeper 구성 (Configure BookKeeper)
conf/bookkeeper.conf 구성 파일로 BookKeeper 부키를 구성할 수 있어요. 각 부키를 구성할 때 zkServers 파라미터가 Pulsar 클러스터의 local ZooKeeper 연결 문자열로 설정되어 있는지 확인해요.
conf/bookkeeper.conf에서 필요한 최소 구성 변경은 다음과 같아요.
note
journalDirectory와ledgerDirectories를 신중히 설정해요. 나중에 바꾸기 어려워요.
# Change to point to journal disk mount point
journalDirectory=data/bookkeeper/journal
# Point to ledger storage disk mount point
ledgerDirectories=data/bookkeeper/ledgers
# Point to local ZK quorum
zkServers=zk1.example.com:2181,zk2.example.com:2181,zk3.example.com:2181
#It is recommended to set this parameter. Otherwise, BookKeeper can't start normally in certain environments (for example, Huawei Cloud).
advertisedAddress=
BookKeeper가 사용하는 ZooKeeper 루트 경로를 바꾸려면 zkServers=localhost:2181/MY-PREFIX 대신 zkLedgersRootPath=/MY-PREFIX/ledgers를 사용해요.
BookKeeper에 대한 자세한 내용은 공식 BookKeeper docs를 참조해요.
BookKeeper 배포 (Deploy BookKeeper)
BookKeeper는 Pulsar에 영속 메시지 저장을 제공해요. 각 Pulsar 브로커는 자체 부키 클러스터를 가져요. BookKeeper 클러스터는 Pulsar 클러스터와 local ZooKeeper 쿼럼을 공유해요.
부키 수동 시작 (Start bookies manually)
부키를 포그라운드 또는 백그라운드 데몬으로 시작할 수 있어요.
포그라운드로 부키를 시작하려면 bookkeeper CLI 도구를 사용해요.
bin/bookkeeper bookie
백그라운드로 부키를 시작하려면 pulsar-daemon CLI 도구를 사용해요.
bin/pulsar-daemon start bookie
BookKeeper shell의 bookiesanity 명령으로 부키가 제대로 동작하는지 확인할 수 있어요.
bin/bookkeeper shell bookiesanity
이 명령을 사용하면 로컬 부키에 새 레저를 만들고, 몇 개 엔트리를 기록하고, 읽고, 마지막으로 레저를 삭제해요.
부키 정상 해체 (Decommission bookies cleanly)
부키를 해체하기 전에 환경을 확인하고 다음 요구 사항을 충족해야 해요.
- 클러스터 상태가 대상 부키 해체를 지원하는지 확인해요. 부키 하나가 적을 때
EnsembleSize >= Write Quorum >= Ack Quorum이 참인지 확인해요. listbookies명령 사용 후 대상 부키가 나열되는지 확인해요.- 진행 중인 다른 프로세스(업그레이드 등)가 없는지 확인해요.
그런 다음 안전하게 부키를 해체할 수 있어요. 부키를 해체하려면 다음 단계를 완료해요.
-
부키 노드에 로그인하고 미-복제(under-replicated) 레저가 있는지 확인해요. 해체 명령은 미-복제 레저를 복제하도록 강제해요.
bin/bookkeeper shell listunderreplicated -
부키 프로세스를 종료해 부키를 중지해요. Kubernetes 환경에 배포했다면 부키를 다시 띄우는 liveness/readiness 프로브가 없는지 확인해요.
-
해체 명령을 실행해요.
해체할 노드에 로그인했다면 -bookieid를 제공할 필요가 없어요.
대상 부키 노드에 대해 다른 부키 노드에서 해체 명령을 실행한다면 인자에 대상 부키 ID를 -bookieid로 언급해야 해요.
bin/bookkeeper shell decommissionbookie
또는
bin/bookkeeper shell decommissionbookie -bookieid <target bookieid>
-
해체된 부키에 레저가 없는지 검증해요.
bin/bookkeeper shell listledgers -bookieid <target bookieid>
다음 명령으로 해체한 부키가 나열되는지 확인할 수 있어요.
bin/bookkeeper shell listbookies -rw -h
bin/bookkeeper shell listbookies -ro -h
자동 복구 (AutoRecovery)
BookKeeper AutoRecovery는 사용 불가능한 부키를 자동으로 감지하고 미-복제 레저 조각을 건강한 부키에 수동 개입 없이 재복제해요. AutoRecovery 동작 방식에 대한 전체 설명은 BookKeeper AutoRecovery documentation을 참고해요.
AutoRecovery 배포 (Deploy AutoRecovery)
AutoRecovery는 각 부키 프로세스 안에 내장되어 실행되거나(기본) 전용 노드에서 실행될 수 있어요.
- 내장(Embedded, 기본):
conf/bookkeeper.conf에서autoRecoveryDaemonEnabled=true— 각 부키가 정상 부키 연산과 함께 AutoRecovery 스레드를 실행해요. - 전용 노드: 모든 부키에서
autoRecoveryDaemonEnabled=false를 설정하고 AutoRecovery를 별도 프로세스로 실행해요. 복구 I/O를 부키 트래픽과 분리하려는 대규모 클러스터에 권장해요.
AutoRecovery를 독립 프로세스로 시작하려면:
bin/bookkeeper autorecovery
또는 백그라운드 데몬으로:
bin/pulsar-daemon start autorecovery
AutoRecovery 활성화·비활성화 (Enable and disable AutoRecovery)
계획된 유지보수 중 AutoRecovery를 클러스터 전체에서 일시적으로 비활성화하고 나중에 다시 활성화할 수 있어요.
# Disable AutoRecovery
bin/bookkeeper shell autorecovery -disable
# Enable AutoRecovery
bin/bookkeeper shell autorecovery -enable
# Check current status
bin/bookkeeper shell autorecovery -status
Pulsar 특정 구성 (Pulsar-specific configuration)
롤링 재시작을 겪는 프로덕션 클러스터에서는 conf/bookkeeper.conf의 lostBookieRecoveryDelay를 0보다 큰 값(예: 60초)으로 설정해요. 이는 일시적으로 사용 불가능한 부키에 대한 불필요한 재복제를 AutoRecovery가 트리거하지 않게 해요.
부키 또는 AutoRecovery 노드에서 Prometheus 메트릭을 활성화하려면 conf/bookkeeper.conf에서 다음을 설정해요.
statsProviderClass=org.apache.pulsar.metrics.prometheus.bookkeeper.PrometheusMetricsProvider
note
이 Pulsar 특정 stats 제공자 클래스는 Pulsar 4.2.0 / 4.0.10부터 필요해요. 자세한 내용은 Pulsar 4.0.10 release notes를 참고해요.
BookKeeper 영속성 정책 (BookKeeper persistence policies)
Pulsar에서 네임스페이스 수준에서 영속성 정책을 설정할 수 있으며, 이는 BookKeeper가 메시지를 영속 저장하는 방식을 결정해요. 정책은 네 가지를 결정해요.
- Ensemble(E) 크기: 레저에 엔트리를 저장하는 데 사용할 부키 수
- Write quorum(Qw) 크기: 레저에 엔트리(메시지)를 저장하는 복제 인자
- Ack quorum(Qa) 크기: 보장된 복제본 수(쓰기가 완료된 것으로 간주되기 전에 기다리는 ack)
- mark-delete 연산의 스로틀링 비율
영속성 정책 설정 (Set persistence policies)
네임스페이스 수준에서 BookKeeper의 영속성 정책을 설정할 수 있어요.
pulsar-admin · REST API · Java
set-persistence 하위 명령에 네임스페이스와 적용할 정책을 지정해요. 사용 가능한 플래그는 다음과 같아요.
| Flag | Description | Default |
|---|---|---|
-e, --bookkeeper-ensemble |
Ensemble(E) 크기, 레저에 엔트리를 저장하는 데 사용할 부키 수 | 0 |
-w, --bookkeeper-write-quorum |
Write quorum(Qw) 크기, 레저에 엔트리(메시지)를 저장하는 복제 인자 | 0 |
-a, --bookkeeper-ack-quorum |
Ack quorum(Qa) 크기, 보장된 복제본 수(쓰기가 완료된 것으로 간주되기 전에 기다리는 ack) | 0 |
-r, --ml-mark-delete-max-rate |
mark-delete 연산의 스로틀링 비율(0이면 스로틀링 없음) | 0 |
bookkeeperEnableStickyReads=true로 활성화된 스티키 읽기는 ensemble 크기(E)가 write quorum(Qw) 크기와 같지 않으면 사용되지 않는다는 점을 유의해요. 스티키 읽기는 단일 레저의 모든 읽기가 단일 부키로 전송될 때 BookKeeper read ahead 캐시의 효율을 개선해요.
값을 선택하는 몇 가지 규칙:
| Rule | Description |
|---|---|
E >= Qw >= Qa |
Ensemble 크기는 write quorum 크기보다 크거나 같아야 하고, write quorum 크기는 ack quorum 크기보다 크거나 같아야 해요. |
Max bookie failures = Qa-1 |
부키 실패 시 데이터 내구성을 원한다면 이 규칙이 충족되어야 해요. 앙상블에서 한 번에 최소 한 번의 부키 실패를 안전하게 견디려면 Qa를 최소 2로 설정해야 해요. |
E == Qw |
bookkeeperEnableStickyReads=true로 활성화된 스티키 읽기는 ensemble 크기(E)가 write quorum(Qw) 크기와 같지 않으면 사용되지 않아요. |
다음은 예시예요.
pulsar-admin namespaces set-persistence my-tenant/my-ns \
--bookkeeper-ensemble 3 \
--bookkeeper-write-quorum 3 \
--bookkeeper-ack-quorum 3
짧은 예시:
pulsar-admin namespaces set-persistence my-tenant/my-ns -e 3 -w 3 -a 3
REST API: POST /admin/v2/namespaces/{tenant}/{namespace}/persistence
Java
// The following must be true: bkEnsemble >= bkWriteQuorum >= bkAckQuorum
// Please notice that sticky reads cannot be used unless bkEnsemble == bkWriteQuorum.
int bkEnsemble = 3;
int bkWriteQuorum = 3;
int bkAckQuorum = 3;
double markDeleteRate = 0.7;
PersistencePolicies policies =
new PersistencePolicies(bkEnsemble, bkWriteQuorum, bkAckQuorum, markDeleteRate);
admin.namespaces().setPersistence(namespace, policies);
영속성 정책 나열 (List persistence policies)
네임스페이스에 현재 적용되는 영속성 정책을 볼 수 있어요.
pulsar-admin · REST API · Java
get-persistence 하위 명령에 네임스페이스를 지정해요.
다음은 예시예요.
pulsar-admin namespaces get-persistence my-tenant/my-ns
{
"bookkeeperEnsemble": 1,
"bookkeeperWriteQuorum": 1,
"bookkeeperAckQuorum", 1,
"managedLedgerMaxMarkDeleteRate": 0
}
REST API: GET /admin/v2/namespaces/{tenant}/{namespace}/persistence
Java
PersistencePolicies policies = admin.namespaces().getPersistence(namespace);
더 알아보기 (Learn more)
- 메타데이터 스토어 모든 백엔드 구성은 Configure metadata store 문서를 참고해요.
- ZooKeeper에서 Oxia로 실시간 마이그레이션은 Migrate metadata store 문서를 참고해요.
- BookKeeper의 자세한 내용은 공식 BookKeeper docs를 참고해요.
- 메시지 영속화·보존·만료 관리는 Pulsar cookbook을 참고해요.