백엔드 데이터 인프라 103편. Kafka Geo-Replication — MirrorMaker 2 의 모든 것. 4가지 connector(MirrorSource·MirrorCheckpoint·MirrorHeartbeat·MirrorClient), Active-Active vs Active-Passive 운영, topic prefix·remote 토픽 패턴, DR 시나리오 step-by-step 까지 풀어쓴 학습 노트.
이 글은 백엔드 데이터 인프라 시리즈 130편 중 103편이에요. 102편 에서 다중 DC(데이터센터) 패턴 을 잡았다면, 이번 103편은 cluster 간 복제 도구 — MirrorMaker 2 깊이.
MirrorMaker 2가 어렵게 느껴지는 이유
MM2(MirrorMaker 2) 가 어려운 건 옛 MM1 의 한계를 거의 다 해결하면서 구조 자체가 그만큼 복잡해졌기 때문이에요. MM1 은 consumer offset 추적도, ACL(접근 제어 목록) sync 도 못 했는데, MM2 는 둘 다 한다. 대신 봐야 할 게 늘었다.
첫째, 4가지 connector(복제 작업 단위) 의 역할 이 각각 다르다. MirrorSource·MirrorCheckpoint·MirrorHeartbeat·MirrorClient 가 각자 다른 일을 맡는다.
둘째, Topic 이름 변환 규칙. 원본 cluster 의 orders 가 mirror 후 primary.orders 로 바뀐다. 이 prefix 가 운영의 핵심.
셋째, Active-Active 시 무한 루프 방지. 양방향 mirror 는 같은 메시지가 끝없이 왕복 할 위험을 안고 있어서 heartbeat 와 prefix 로 차단 한다.
이 글에서 MM2 구조·4 connector·운영 패턴·DR(재해 복구) 시나리오까지 본다.
MirrorMaker 2 구조
MirrorMaker 2 = Kafka Connect cluster + 4 specialized connector
[Source Cluster A] ──→ [MM2 Worker] ──→ [Target Cluster B]
│
├─ MirrorSourceConnector (topic 데이터 복제)
├─ MirrorCheckpointConnector (consumer offset 추적)
├─ MirrorHeartbeatConnector (heartbeat)
└─ MirrorMaker2 Driver (전체 조율)
MM2 는 내부적으로 Kafka Connect(분산 데이터 파이프라인 프레임워크) 인프라 를 가져다 쓴다. Connect Worker 가 connector 들을 실행 하는 구조.
4가지 Connector
1. MirrorSourceConnector
Topic 데이터 복제 를 맡는다. Source topic 의 메시지를 Target topic 으로 그대로 옮긴다.
Source: orders (partition 0~9, 1M messages)
↓ MirrorSourceConnector
Target: primary.orders (partition 0~9, 동일 메시지)
특징:
- Source partition → Target partition 1:1 매핑 (partition 순서 유지)
- Source key·value·timestamp·headers 모두 복제
- Source-specific header 추가 (
__MM2_SOURCE_PARTITION)
2. MirrorCheckpointConnector
Consumer offset 추적·변환 을 담당한다. DR 시나리오에서 가장 중요한 부품.
Source Cluster:
Topic "orders", Consumer Group "workers" → offset 12345
↓ MirrorCheckpointConnector
Target Cluster:
Topic "primary.orders" → offset 12345 (대략)
Special topic "primary.checkpoints.internal"
DR 이 터져서 consumer 가 Target cluster 로 이동할 때 어디부터 다시 처리해야 하는지 가 여기서 결정난다. Source 에서 처리한 offset 을 Target 의 동등 offset 으로 변환해 둔다.
3. MirrorHeartbeatConnector
Cluster 간 heartbeat 을 쏜다. mirror 가 살아 있는지 를 모니터링하는 신호.
Source → "heartbeats" topic 에 주기적 heartbeat
↓ Mirror
Target → "primary.heartbeats" 받음
- Heartbeat 받으면 = mirror 정상
- 일정 시간 끊김 = alert
- Heartbeat lag = mirror lag 측정 가능
4. MirrorMaker 2 Driver
위 3 connector 의 전체 lifecycle 을 조율 한다. 사용자가 직접 다루지는 않는다.
설정 — mm2.properties
# Cluster 정의
clusters = primary, secondary
primary.bootstrap.servers = kafka-dc1:9092
secondary.bootstrap.servers = kafka-dc2:9092
# Replication 흐름
primary->secondary.enabled = true
primary->secondary.topics = orders, payments, users.*
# 옵션
primary->secondary.replication.factor = 3
primary->secondary.tasks.max = 10
primary->secondary.refresh.topics.enabled = true
primary->secondary.refresh.topics.interval.seconds = 60
# Active-Active 도 가능
# secondary->primary.enabled = true
# secondary->primary.topics = ...
# Topic naming
primary->secondary.replication.policy.separator = .
primary->secondary.replication.policy.class = \
org.apache.kafka.connect.mirror.DefaultReplicationPolicy
실행:
$ bin/connect-mirror-maker.sh mm2.properties
Topic Naming — primary.orders 패턴
기본 정책은 <source-cluster>.<topic> 이에요.
Source: kafka-dc1 의 "orders"
↓
Target: kafka-dc2 의 "primary.orders"
이렇게 prefix 를 붙이는 이유는 무한 루프 방지 때문.
Active-Active 환경:
DC1 의 "orders" → DC2 의 "primary.orders"
DC2 의 "primary.orders" → DC1 의 "secondary.primary.orders" (이걸 방지)
MirrorSourceConnector 는 자기가 mirror 한 topic 은 다시 mirror 하지 않는다. Prefix 로 자기 origin 을 식별하는 거.
IdentityReplicationPolicy — Prefix 없음 (특수)
replication.policy.class = \
org.apache.kafka.connect.mirror.IdentityReplicationPolicy
원본 이름을 그대로 가져간다 (orders → orders). Active-Passive DR 에 잘 맞는다. consumer 가 Target 으로 갈아탈 때 같은 topic 이름 이라 자연스러우니까.
단점은 Active-Active 무한 루프 위험. heartbeat 와 source header 로 막긴 하지만 신중하게 봐야 한다.
Consumer Offset Translation — DR 의 핵심
시나리오
DC1 (Source):
Consumer "workers" 가 orders 의 offset 50000 까지 처리
↓ MirrorCheckpoint 가 추적
DC2 (Target):
primary.orders 에 같은 메시지들이 있음 (다른 offset 일 수 있음)
"primary.checkpoints.internal" 에 mapping 저장:
workers: orders[0]=50000 → primary.orders[0]=49997
DR 트리거 시:
Consumer 가 DC2 로 이동
↓
RemoteClusterUtils.translateOffsets() 호출
↓
DC2 의 적절한 offset 으로 자동 변환
↓
처리 재개 (가능한 한 정확한 지점)
at-least-once 보장. 약간의 중복은 생길 수 있지만 손실은 없다.
ACL · Config Sync
primary->secondary.sync.topic.acls.enabled = true
primary->secondary.sync.topic.configs.enabled = true
Source topic 의 ACL 과 config 도 Target 으로 같이 sync 된다. DR 시 동일 환경 을 만들어 두는 장치.
운영 패턴
Pattern 1: Active-Passive DR
clusters = primary, dr
primary.bootstrap.servers = ...
dr.bootstrap.servers = ...
primary->dr.enabled = true
primary->dr.topics = .*
# Identity policy (DR 용)
primary->dr.replication.policy.class = \
org.apache.kafka.connect.mirror.IdentityReplicationPolicy
평소엔 primary 만 운영하고, DR 이 터지면 dr 로 failover.
Pattern 2: Active-Active
clusters = us-east, eu-west
us-east->eu-west.enabled = true
us-east->eu-west.topics = orders
eu-west->us-east.enabled = true
eu-west->us-east.topics = orders
각 region 의 사용자가 local 에 read/write 하고, 데이터는 양방향으로 sync 된다. naming convention 이 정말 중요해진다 (prefix 로 region 을 구분).
Pattern 3: Aggregate
clusters = us, eu, asia, global
us->global.enabled = true
eu->global.enabled = true
asia->global.enabled = true
3개의 local 을 1개의 global 로 모으는 형태. 분석·ML 용도로 자주 쓴다.
실행 — Connect Worker 모드
Standalone (개발용)
$ bin/connect-mirror-maker.sh mm2.properties
Distributed (운영)
운영에서는 여러 MM2 worker 를 cluster 형태로 띄운다 (Kafka Connect distributed mode).
# 각 worker 에서
$ bin/connect-mirror-maker.sh mm2-worker.properties
worker 간 자동으로 작업을 분담하고, 한 worker 가 죽어도 다른 worker 가 인수한다.
DR 시나리오 Step-by-Step
Step 1: Mirror 정상 작동 확인
# heartbeat topic 확인
$ kafka-console-consumer.sh --bootstrap-server kafka-dr:9092 \
--topic primary.heartbeats --from-beginning
# Mirror lag 확인
$ kafka-consumer-groups.sh --bootstrap-server kafka-dr:9092 \
--describe --group connect-mirror-maker-2-source
Step 2: DR 트리거 (primary 죽음)
primary 장애 발생
→ DNS 변경: kafka-app.example.com → kafka-dr.example.com
→ Consumer 들이 dr cluster 로 자동 재연결
Step 3: Consumer Offset 변환
DR 시 consumer 가 새 cluster 에서 시작 offset 을 자동으로 변환받는다.
// Consumer 측 코드 (RemoteClusterUtils 활용)
Map<TopicPartition, OffsetAndMetadata> offsets =
RemoteClusterUtils.translateOffsets(props, "primary", "workers",
Duration.ofSeconds(10));
consumer.commitSync(offsets);
Step 4: 운영 회복 확인
- Consumer lag 모니터링
- 데이터 일관성 확인
- Application metric 정상 확인
Step 5: Primary 복구 후 (Optional)
- DC1 복구
- Mirror 방향 임시 역전 (DR → Primary 로)
- 동기 완료 후 다시 Primary 로 전환
모니터링 메트릭
MM2 JMX(Java 모니터링 표준 인터페이스):
mirror-source-connector— 복제 throughputrecord-byte-rate— 초당 바이트record-age-ms-avg— 메시지 age (mirror lag 추정)checkpoint-latency-ms— checkpoint 동기 latency
핵심은 record-age-ms-avg = source 메시지가 target 에 도착하기까지의 평균 지연.
한계·실무 함정
1. MM2 Worker 자체 가용성
MM2 worker 도 cluster 로 운영해야 한다. 한 worker 가 죽으면 자동으로 인수하지만, 최소 3 worker 는 띄워두는 게 안전.
2. Source ↔ Target 보안 매핑
cluster 마다 ACL 과 인증이 다르다. MM2 가 양쪽에 연결할 자격증명 을 모두 들고 있어야 한다.
3. Topic auto-create
primary->secondary.refresh.topics.enabled=true (기본값) 이면 source 에 새 topic 이 생기는 즉시 자동으로 mirror 가 시작된다. 원치 않으면 topic whitelist 를 명시.
4. Offset Translation 의 한계
완벽한 1:1 mapping 은 아니다. 일부 중복 처리가 생길 수 있으니 consumer 쪽에서 멱등성을 챙긴다.
5. WAN 대역폭
Mirror 는 모든 mirror topic 의 트래픽이 WAN(광역 네트워크) 을 통과 하는 구조. 불필요한 topic 은 mirror 하지 말 것.
6. Active-Active 의 cycle
Identity policy 와 Active-Active 를 같이 쓰면 prefix 가 없어서 무한 루프 위험 이 있다. Source header 로 막지만 신중하게 검증해야 한다.
시험 직전 한 번 더 — MirrorMaker 2 함정 압축 노트
- MirrorMaker 2 = DC 간 mirror 표준 도구, Kafka Connect 기반
- 4가지 Connector — MirrorSource (데이터) · MirrorCheckpoint (offset 추적) · MirrorHeartbeat (cluster 간 heartbeat) · MirrorMaker 2 Driver (조율)
- MirrorSourceConnector = topic 데이터 복제, partition 1:1 매핑
- MirrorCheckpointConnector = consumer offset 추적·translation (DR 핵심)
- MirrorHeartbeatConnector = mirror 정상 모니터링
- Topic Naming = 기본
<source-cluster>.<topic>(예:primary.orders) - 이유 = 무한 루프 방지 (이미 mirror 된 topic 은 다시 mirror X)
- IdentityReplicationPolicy = prefix 없음, DR 시 같은 topic 이름 (Active-Passive 용)
- Consumer Offset Translation =
RemoteClusterUtils.translateOffsets() - DR 시 consumer 이동 → 자동 offset 변환 → at-least-once 보장
- ACL·Config Sync =
sync.topic.acls.enabled·sync.topic.configs.enabled - 운영 패턴 3가지 = Active-Passive DR · Active-Active · Aggregate
- 실행 = Standalone (개발) vs Distributed (운영, 여러 worker)
- 한 worker 죽으면 다른 worker 자동 인수
- DR Step-by-Step = 정상 확인 → DR 트리거 (DNS) → Offset 변환 → 모니터링 → 복구 후 reverse mirror
- 모니터링 =
record-age-ms-avg(mirror lag 핵심) - 함정 — MM2 worker 가용성 (3+ worker)
- 함정 — 양쪽 cluster 인증 매핑
- 함정 —
refresh.topics.enabled자동 mirror - 함정 — Offset Translation 완벽 1:1 X (멱등성)
- 함정 — WAN 대역폭 비용
- 함정 — Active-Active + Identity policy = cycle 위험
공식 문서: Kafka Geo-Replication 에서 MM2 자세한 설정·시나리오를 확인할 수 있어요.
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 98편 — Kafka Admin Client 설정 (간결 정리)
- 99편 — Kafka 운영 기본 (Topic·Partition·Reassign·Rolling Restart)
- 100편 — Kafka Monitoring (JMX 메트릭 30가지)
- 101편 — Kafka Multi-tenancy (Quota · Naming · ACL 분리)
- 102편 — Kafka 다중 데이터센터 (Stretched · Local + Mirror)
다음 글: