Kafka MirrorMaker 2: 멀티 리전 클러스터 복제와 재해 복구 운영 패턴
요약
Kafka 클러스터를 하나의 리전에서만 운영하는 시대는 지났다. 글로벌 서비스는 지연 시간 최소화를 위해 리전별 클러스터를 두고, 재해 복구(DR)를 위해 클러스터 간 데이터를 복제한다.
MirrorMaker 2(MM2)는 Kafka 2.4부터 Kafka 공식 배포에 포함된 크로스 클러스터 복제 솔루션이다. Kafka Connect 프레임워크 위에 구현되어 확장성과 운영 편의성을 갖췄으며, Kafka 4.x KRaft 환경에서 가장 성숙한 상태에 있다.
MM2의 핵심 설계 결정은 두 가지다: 오프셋 변환(offset translation)과 자동 토픽 프로비저닝. 이 두 기능이 단순한 데이터 복사를 넘어 실제 DR 전환과 멀티 리전 운영을 가능하게 한다.
이 챕터는 Kafka 4.3(database-frontier/11)에서 KRaft가 안정화된 환경을 기준으로, MM2를 사용한 멀티 리전 복제 패턴과 운영 체크리스트를 정리한다.
MirrorMaker 2 아키텍처
MM2는 세 개의 Kafka Connect 커넥터로 구성된다.
세 커넥터의 역할
MirrorSourceConnector는 소스 클러스터의 토픽 레코드를 타깃 클러스터로 복제한다. 기본적으로 source-cluster.topic-name 형식으로 타깃에 새 토픽을 생성한다. 파티션 수, 설정(retention.ms 등)도 자동으로 미러링한다.
MirrorCheckpointConnector는 소스 클러스터의 컨슈머 그룹 오프셋을 읽어 타깃 클러스터의 오프셋으로 변환한다. mm2-offsets.<source-cluster>.<topic> 토픽에 매핑을 저장한다. 이 정보가 있어야 DR 전환 후 컨슈머가 정확한 위치부터 재개할 수 있다.
MirrorHeartbeatConnector는 heartbeats 토픽에 주기적으로 레코드를 쓴다. 이를 통해 복제 파이프라인의 생존 여부와 지연을 모니터링할 수 있다.
토픽 네이밍 규칙과 사이클 방지
MM2의 토픽 네이밍 규칙은 중요한 설계 결정이다. 기본 패턴은 다음과 같다:
소스 클러스터 ID: us-east-1
소스 토픽: orders
타깃 토픽 이름: us-east-1.orders이 네이밍 규칙은 복제 사이클을 자동으로 방지한다. MM2는 이미 다른 클러스터에서 온 토픽(. 포함)은 재복제하지 않는다. 양방향(Active-Active) 복제를 구성해도 무한 루프가 발생하지 않는다.
단방향 vs 양방향 복제
[단방향 Active-Passive DR]
us-east-1 (Primary) → ap-northeast-2 (Standby)
타깃 토픽: us-east-1.orders, us-east-1.payments
---
[양방향 Active-Active 지역 복제]
us-east-1 ⇄ ap-northeast-2
us-east-1 타깃: ap-northeast-2.orders (서울에서 온 메시지)
ap-northeast-2 타깃: us-east-1.orders (버지니아에서 온 메시지)
각 리전의 컨슈머는 자신의 리전 토픽을 주로 소비설정 가이드
기본 MM2 설정 (mm2.properties)
# 클러스터 별칭
clusters = us-east-1, ap-northeast-2
# 소스 클러스터 접속
us-east-1.bootstrap.servers = kafka-us-east.example.com:9092
ap-northeast-2.bootstrap.servers = kafka-ap-ne.example.com:9092
# 복제 방향 (단방향 DR)
us-east-1->ap-northeast-2.enabled = true
us-east-1->ap-northeast-2.topics = orders, payments, events
us-east-1->ap-northeast-2.groups = order-processor, payment-handler
# 타깃 토픽 자동 생성
us-east-1->ap-northeast-2.topics.auto.create = true
us-east-1->ap-northeast-2.config.properties.exclude = follower.replication.throttled.replicas, leader.replication.throttled.replicas
# 오프셋 동기화 주기 (기본 60초)
us-east-1->ap-northeast-2.emit.checkpoints.interval.seconds = 30
# 하트비트 주기
us-east-1->ap-northeast-2.emit.heartbeats.interval.seconds = 5
# 복제 팩터
replication.factor = 3Kafka Connect 클러스터에서 실행
MM2는 Kafka Connect 클러스터 위에서 실행하는 것이 권장된다. 독립 실행(kafka-mirror-maker.sh)은 소규모 테스트용으로만 사용한다.
# Connect 클러스터 시작 (distributed mode)
connect-distributed.sh /etc/kafka/connect-distributed.properties
# MM2 커넥터 배포
curl -X POST http://connect-host:8083/connectors \
-H "Content-Type: application/json" \
-d @mm2-source-connector.json
# 상태 확인
curl http://connect-host:8083/connectors/MirrorSourceConnector/status오프셋 변환: DR 전환의 핵심
소스와 타깃의 파티션 오프셋이 일치한다는 보장이 없다. 복제된 레코드는 타깃에서 새로운 오프셋을 할당받는다. DR 전환 후 컨슈머가 소스 오프셋 5000에서 커밋 중이었다면, 타깃에서는 몇 번 오프셋부터 읽어야 하는가?
MirrorCheckpointConnector가 이 답을 제공한다.
RemoteClusterUtils API 사용
// Kafka 클라이언트에서 오프셋 변환
Map<TopicPartition, OffsetAndMetadata> sourceOffsets = consumer.committed(partitions);
// 소스 오프셋 → 타깃 오프셋 변환
Map<TopicPartition, OffsetAndMetadata> targetOffsets =
RemoteClusterUtils.translateOffsets(
targetConsumer.listTopics().keySet(),
"us-east-1", // 소스 클러스터 별칭
sourceOffsets,
Duration.ofSeconds(10),
targetConsumerConfig
);
// 타깃 오프셋으로 재개
consumer.assign(targetOffsets.keySet());
targetOffsets.forEach((tp, om) -> consumer.seek(tp, om.offset()));변환 정밀도와 중복/유실
오프셋 변환은 정밀한 1:1 매핑이 아니라 근사치다. emit.checkpoints.interval.seconds 주기마다 스냅샷을 찍기 때문에, 전환 시점에 따라 약간의 중복이 발생할 수 있다. 유실은 일반적으로 없다 — 변환된 오프셋은 실제보다 약간 이전 위치를 가리키는 경향이 있기 때문이다.
운영 원칙: 컨슈머 로직을 멱등적(idempotent)으로 설계하면 중복은 무해하다. MM2 DR 패턴에서 정확히 한 번(exactly-once)을 기대하는 것은 현실적이지 않다.
재해 복구 전환 절차
DR 전환 단계 (소스 클러스터 장애 시)
1. 장애 감지
- 소스 클러스터 접근 불가 확인
- 타깃 클러스터 복제 lag 확인 (마지막 동기화 시점)
2. MM2 일시 중단
- MirrorSourceConnector 중지 (새 복제 차단)
- 타깃에서 복제 lag이 0인지 확인
3. 오프셋 변환 실행
- RemoteClusterUtils.translateOffsets() 호출
- 각 컨슈머 그룹의 타깃 오프셋 목록 생성
4. 컨슈머 전환
- 타깃 클러스터의 네이티브 토픽 이름으로 재구독
- 변환된 오프셋부터 재개
5. 소스 복구 후 재동기화
- 소스 복구 완료 후 역방향 MM2 구성
- 또는 타깃 → 소스로 데이터 역복제 후 소스 복원전환 소요 시간(RTO) 현실적 기대치
| 단계 | 예상 시간 |
|---|---|
| 장애 감지 | 1~5분 |
| 오프셋 변환 실행 | 30초~3분 |
| 컨슈머 전환 | 1~5분 (재배포 또는 설정 변경) |
| 총 RTO | 3~15분 |
자동화(헬스체크 → 알림 → 배포 파이프라인)가 잘 구성된 팀에서는 5분 이내 가능하다.
모니터링 체크리스트
핵심 지표
복제 지연(Replication Lag):
kafka.server:type=FetcherLagMetrics,name=ConsumerLag,clientId=MirrorSourceConnector-*
→ 타깃 파티션이 소스보다 얼마나 뒤처져 있는가하트비트 지연(Heartbeat Lag):
heartbeats 토픽의 최신 레코드 타임스탬프 - 현재 시각
→ 5분 이상이면 복제 파이프라인 문제 의심체크포인트 동기화 주기:
emit.checkpoints.interval.seconds 설정값 확인
→ 값이 너무 크면 DR 전환 시 오프셋 손실 범위 증가커넥터 태스크 상태:
# 태스크 상태 확인
curl http://connect-host:8083/connectors/MirrorSourceConnector/status \
| jq '.tasks[].state'
# 모든 태스크가 RUNNING 이어야 함Prometheus/Grafana 알림 규칙 예시
- alert: MM2ReplicationLagHigh
expr: kafka_consumer_fetch_manager_metrics_records_lag_max{client_id=~".*Mirror.*"} > 100000
for: 5m
labels:
severity: warning
annotations:
summary: "MM2 복제 lag 10만 레코드 초과"
- alert: MM2ConnectorDown
expr: kafka_connect_connector_status{connector=~"Mirror.*"} != 1
for: 2m
labels:
severity: critical흔한 운영 이슈와 해결
토픽이 타깃에 자동 생성되지 않음
원인: topics.auto.create = false 또는 소스 토픽이 패턴에 매칭되지 않음
확인: us-east-1->ap-northeast-2.topics 설정이 정규식인지 명시적 이름인지 확인
- 정규식 예:
topics = orders.*(orders로 시작하는 모든 토픽) - 명시적 예:
topics = orders, payments
오프셋 변환이 없거나 비어 있음
원인: MirrorCheckpointConnector가 실행 중이지 않거나, emit.checkpoints.interval.seconds 주기가 아직 도달하지 않음
확인: mm2-offsets.us-east-1.orders 토픽에 레코드가 있는지 확인
kafka-console-consumer.sh --bootstrap-server target:9092 \
--topic mm2-offsets.us-east-1.orders --from-beginning --max-messages 10ACL이 복제되지 않음
MM2는 기본적으로 ACL을 복제하지 않는다. ACL 동기화가 필요하면 MirrorAclConnector를 별도로 구성해야 한다. Schema Registry 스키마는 MM2 범위 밖이며, Schema Registry 자체 복제 기능을 사용해야 한다.
양방향 복제에서 토픽 증식
Active-Active 설정에서 시간이 지나면 us-east-1.ap-northeast-2.orders 같은 중첩 토픽이 생성되는 것처럼 보일 수 있다. 실제 MM2는 이미 다른 클러스터에서 미러된 토픽(. 포함 이름)의 재복제를 차단하므로 무한 증식은 없지만, 토픽 수가 늘어나는 것은 예상된 동작이다. topics.exclude 설정으로 불필요한 토픽 복제를 차단할 수 있다.
Kafka 4.x KRaft 환경에서의 변화
Kafka 4.0부터 ZooKeeper가 완전히 제거됐다. KRaft 환경에서 MM2 운영의 달라진 점:
- Connect 클러스터 토폴로지 서비스: ZooKeeper 대신 KRaft Kafka 자체를 메타데이터 스토어로 사용
- 메타데이터 전파 지연 감소: KRaft의 Raft 기반 메타데이터 복제가 ZooKeeper보다 빠른 토폴로지 갱신을 제공
- 클러스터 ID 기반 격리: KRaft 환경에서 각 클러스터가 고유한 UUID를 가지며, MM2의 사이클 감지가 클러스터 ID를 기반으로 더 신뢰성 있게 동작
Open question: Kafka 4.4 이상에서 KRaft 기반 Connect 클러스터의 오프셋 변환 지연 특성에 대한 공식 벤치마크가 아직 발표되지 않았다.
요점 정리
MirrorMaker 2는 "단순 데이터 복사" 수준을 넘어 실제 DR과 멀티 리전 운영을 가능하게 하는 오프셋 변환 기능이 핵심이다.
운영자가 기억할 세 가지:
- MirrorCheckpointConnector 없이는 DR 전환이 불완전하다. 데이터만 복제하고 오프셋 매핑을 안 하면 재처리나 유실이 발생한다.
- emit.checkpoints.interval.seconds가 RPO를 결정한다. 이 값이 클수록 DR 전환 시 컨슈머가 되감아야 하는 범위가 넓어진다. 중요한 토픽에는 30초 이하를 권장한다.
- 컨슈머 멱등성은 선택이 아니다. 오프셋 변환은 근사치이므로 중복이 발생할 수 있다. DR 전환을 안전하게 하려면 다운스트림 시스템이 같은 메시지를 두 번 받아도 괜찮아야 한다.
References
- Apache Kafka MirrorMaker 2 공식 문서: https://kafka.apache.org/documentation/#georeplication
- KIP-382: MirrorMaker 2.0 설계 제안 (Kafka Improvement Proposal)
- Kafka Connect Distributed Mode 설정: https://kafka.apache.org/documentation/#connect_running
- RemoteClusterUtils Javadoc (Kafka 3.x): https://kafka.apache.org/36/javadoc/org/apache/kafka/clients/consumer/internals/RemoteClusterUtils.html
- Confluent "Kafka Multi-Region Replication": https://docs.confluent.io/platform/current/multi-dc-deployments/replicator/index.html (참고용, Confluent Replicator는 상용)
- "Kafka: The Definitive Guide" Chapter 10 — Cross-Cluster Data Mirroring (O'Reilly, 2023 edition)
- Kafka 4.0 KRaft 마이그레이션 가이드: https://kafka.apache.org/documentation/#kraft_zk_migration