Kafka Multi-Datacenter
Strategies for deploying Apache Kafka across multiple datacenters for disaster recovery and global distribution.
Deployment Models
Section titled “Deployment Models”| Model | RPO | RTO | Complexity | Use Case |
|---|---|---|---|---|
| Active-Passive | Minutes | Minutes | Low | Disaster recovery |
| Active-Active | Near-zero (bounded by replication lag) | Near-zero | High | Global distribution |
| Stretch Cluster | Zero (with synchronous replication) | Seconds | Medium | Low-latency DR |
Active-Passive with MirrorMaker 2
Section titled “Active-Passive with MirrorMaker 2”Primary datacenter handles all traffic. Secondary datacenter receives replicated data for failover.
MirrorMaker 2 Configuration
Section titled “MirrorMaker 2 Configuration”# Define clustersclusters=primary,secondary
primary.bootstrap.servers=kafka-primary-1:9092,kafka-primary-2:9092secondary.bootstrap.servers=kafka-secondary-1:9092,kafka-secondary-2:9092
# Replication flowsprimary->secondary.enabled=trueprimary->secondary.topics=.*primary->secondary.groups=.*
# Exclude internal topicsprimary->secondary.topics.exclude=.*[\-\.]internal,.*\.replica,__.*
# Replication settingsreplication.factor=3checkpoints.topic.replication.factor=3heartbeats.topic.replication.factor=3offset-syncs.topic.replication.factor=3
# Consumer offset sync (for failover)sync.group.offsets.enabled=truesync.group.offsets.interval.seconds=60
# Emit checkpoints for offset translationemit.checkpoints.enabled=trueemit.checkpoints.interval.seconds=60
# Note: replication factors must not exceed the broker count in each target cluster.Failover Procedure
Section titled “Failover Procedure”- Detect failure - Monitor primary cluster health
- Stop MirrorMaker 2 - Prevent split-brain
- Translate offsets - Use checkpoint data
- Redirect producers - Update bootstrap servers
- Start consumers - Resume from translated offsets
# Translate consumer group offsetskafka-consumer-groups.sh --bootstrap-server kafka-secondary:9092 \ --group my-consumer-group \ --reset-offsets \ --to-offset <translated-offset> \ --topic primary.my-topic \ --executeThe translated offsets are derived from the primary.checkpoints.internal topic emitted by MirrorMaker 2.
Active-Active with MirrorMaker 2
Section titled “Active-Active with MirrorMaker 2”Both datacenters handle traffic. Bidirectional replication requires careful handling of data provenance to prevent infinite replication loops and enable correct data aggregation.
The Provenance Problem
Section titled “The Provenance Problem”In active-active replication, the system must track where each record originated. Without provenance tracking, records would replicate infinitely:
1. Producer writes to east.orders in DC East2. MirrorMaker replicates to DC West as east.orders3. Without provenance: MirrorMaker replicates back to DC East4. Infinite loop of replicationMirrorMaker 2 solves this through topic prefixing—each replicated topic carries its origin datacenter in the name.
How Provenance Works
Section titled “How Provenance Works”| Topic in DC East | Origin | Description |
|---|---|---|
orders | DC East | Locally produced records |
west.orders | DC West | Replicated from DC West |
| Topic in DC West | Origin | Description |
|---|---|---|
orders | DC West | Locally produced records |
east.orders | DC East | Replicated from DC East |
MirrorMaker 2 never replicates prefixed topics, preventing loops:
east.ordersin DC West is not replicated back to DC Eastwest.ordersin DC East is not replicated back to DC West
Consuming from Multiple Origins
Section titled “Consuming from Multiple Origins”Consumers that need a global view must subscribe to both local and replicated topics:
// Consumer in DC East wanting all orders globallyconsumer.subscribe(Arrays.asList( "orders", // Local DC East orders "west.orders" // Replicated DC West orders));
// Process records with origin awarenesswhile (true) { ConsumerRecords<String, Order> records = consumer.poll(Duration.ofMillis(100)); for (ConsumerRecord<String, Order> record : records) { String origin = record.topic().startsWith("west.") ? "west" : "east"; processOrder(record.value(), origin); }}Provenance in Record Headers
Section titled “Provenance in Record Headers”For more granular provenance tracking, producers can add origin metadata to record headers:
// Producer adds provenance headersProducerRecord<String, Order> record = new ProducerRecord<>("orders", order.getId(), order);record.headers() .add("origin-dc", "east".getBytes()) .add("origin-timestamp", Long.toString(System.currentTimeMillis()).getBytes()) .add("origin-producer", producerId.getBytes());
producer.send(record);Consumers can then extract provenance regardless of topic name:
Header originHeader = record.headers().lastHeader("origin-dc");String originDc = new String(originHeader.value());Aggregation Patterns
Section titled “Aggregation Patterns”| Pattern | Implementation | Use Case |
|---|---|---|
| Union | Subscribe to orders + west.orders | Global view of all orders |
| Local-first | Subscribe to orders only | DC-local processing |
| Kafka Streams | Merge streams with origin tracking | Complex aggregations |
Kafka Streams aggregation example:
// Merge streams from both originsKStream<String, Order> localOrders = builder.stream("orders");KStream<String, Order> remoteOrders = builder.stream("west.orders");
KStream<String, Order> allOrders = localOrders.merge(remoteOrders);
// Process with origin awareness using headersallOrders.foreach((key, order) -> { // Origin available in record headers});Bidirectional Configuration
Section titled “Bidirectional Configuration”clusters=east,west
east.bootstrap.servers=kafka-east-1:9092,kafka-east-2:9092west.bootstrap.servers=kafka-west-1:9092,kafka-west-2:9092
# East to West replicationeast->west.enabled=trueeast->west.topics=orders,events
# West to East replicationwest->east.enabled=truewest->east.topics=orders,events
# Prevent replication loopsreplication.policy.class=org.apache.kafka.connect.mirror.DefaultReplicationPolicy
# Topic naming (default adds source cluster prefix)# east.orders in west cluster# west.orders in east clusterConflict Avoidance Strategies
Section titled “Conflict Avoidance Strategies”| Strategy | Description | Trade-off |
|---|---|---|
| Topic prefixing | Different topic names per DC | Consumers must aggregate |
| Key partitioning | Route keys to owning DC | Requires consistent routing |
| Last-write-wins | Accept all writes, latest wins | Potential data loss |
| Application merge | Application-level conflict resolution | Complexity |
Stretch Cluster
Section titled “Stretch Cluster”Single Kafka cluster spanning multiple datacenters with synchronous replication.
Configuration
Section titled “Configuration”# Rack awareness for cross-DC placementbroker.rack=dc1
# Minimum ISR spans DCsmin.insync.replicas=2default.replication.factor=3
# Replica placementreplica.selector.class=org.apache.kafka.common.replica.RackAwareReplicaSelectorRequirements
Section titled “Requirements”| Requirement | Threshold |
|---|---|
| Network latency | Typically < 10ms RTT between DCs |
| Network bandwidth | Sufficient for replication traffic |
| Broker count | Odd number for controller quorum |
Comparison
Section titled “Comparison”| Aspect | Active-Passive | Active-Active | Stretch Cluster |
|---|---|---|---|
| RPO | Minutes | Near-zero | Zero |
| RTO | Minutes | Near-zero | Seconds |
| Latency impact | None | None | Cross-DC latency |
| Network requirement | Async-capable | Async-capable | Low-latency |
| Topic namespace | Separate | Separate (prefixed) | Single |
| Failover complexity | Manual/automated | Minimal | Automatic |
Consumer Offset Handling
Section titled “Consumer Offset Handling”MirrorMaker 2 Offset Sync
Section titled “MirrorMaker 2 Offset Sync”MirrorMaker 2 synchronizes consumer group offsets using checkpoints.
Offset Translation
Section titled “Offset Translation”# View checkpoint topickafka-console-consumer.sh --bootstrap-server kafka-secondary:9092 \ --topic primary.checkpoints.internal \ --from-beginning \ --property print.key=trueMonitoring
Section titled “Monitoring”Key Metrics
Section titled “Key Metrics”| Metric | Description | Alert Threshold |
|---|---|---|
kafka.connect.mirror.record-count | Records replicated | Sudden drops |
kafka.connect.mirror.record-age-ms | Replication lag | > 60000 ms |
kafka.connect.mirror.checkpoint-latency-ms | Checkpoint delay | > 120000 ms |
kafka.connect.mirror.replication-latency-ms | End-to-end latency | > 30000 ms |
Health Checks
Section titled “Health Checks”# Check MirrorMaker 2 statuscurl http://connect:8083/connectors/mirror-source-connector/status
# Check replication lagkafka-consumer-groups.sh --bootstrap-server kafka-secondary:9092 \ --group mirror-source-connector \ --describeBest Practices
Section titled “Best Practices”| Practice | Rationale |
|---|---|
| Test failover regularly | Ensure procedures work |
| Monitor replication lag | Detect issues early |
| Use rack awareness | Distribute replicas across DCs |
| Document failover procedures | Reduce MTTR |
| Automate where possible | Reduce human error |
Related Documentation
Section titled “Related Documentation”- Kafka Connect - Connect framework
- Operations - Operational procedures
- Fault Tolerance - HA design
- Backup/Restore - DR procedures