1. 为什么 MirrorMaker 2 不是“升级版 MirrorMaker 1”而是彻底重写的同步中枢很多人第一次看到“MirrorMaker 2”这个名称下意识会认为它是 MirrorMaker 1 的小版本迭代——就像 v1.1 升级到 v1.2 那样修几个 Bug、加两个参数、性能稍有提升。我最初在某跨平台系统项目里接手 Kafka 多集群同步任务时也是这么想的。结果花了一周时间把 MirrorMaker 1 的配置从旧集群迁过来跑起来后发现延迟飙升、topic 创建失败、ACL 权限完全不生效、consumer group offset 同步断断续续……最后查日志才发现它压根没连上目标集群的 AdminClient。这不是配置写错了是底层模型根本对不上。根本原因在于MirrorMaker 1 是一个单体消费者-生产者桥接工具而 MirrorMaker 2 是一个基于 Kafka Connect 框架构建的分布式同步服务。前者本质是两个独立进程一个 Consumer 一个 Producer拼在一起靠外部脚本协调后者则是一个标准的 Connect worker所有同步逻辑都封装为 Connector由 Connect 框架统一管理生命周期、offset 提交、容错重启和水平伸缩。这带来三个不可逆的架构差异第一同步粒度从“topic 级”下沉到“record 级”。MirrorMaker 1 只能整 topic 拉取再整 topic 写入中间无法做字段过滤、格式转换或路由决策而 MirrorMaker 2 的每个 Connector 实例都运行在 Connect Task 中每条 record 进入 transform 链后可被任意 SMTSingle Message Transform处理——比如把user_id字段脱敏、把event_time从字符串转为 long、把paymenttopic 的 record 根据currency字段路由到payment_usd或payment_cny。这种能力不是“锦上添花”而是支撑多租户数据隔离、合规性改造、灰度发布等真实场景的基础设施。第二状态管理从“本地文件”移交至“Kafka 内部主题”。MirrorMaker 1 的 offset 存储依赖本地磁盘上的mm1-offsets.dat文件一旦 worker 进程崩溃或机器宕机offset 就丢失只能靠--reset-offsets强制重置导致重复消费或数据丢失而 MirrorMaker 2 默认使用_mm2_offsets、_mm2_configs、_mm2_checkpoints三个内部 topic 存储 offset、connector 配置和 checkpoint这些 topic 本身具备副本、ISR 和 leader 选举机制只要 Kafka 集群可用同步状态就永不丢失。我在某高校实验室部署时曾故意 kill 掉一个 MM2 worker pod30 秒内新 pod 启动后自动从_mm2_offsets恢复位置零人工干预零数据错乱。第三权限模型从“静态白名单”进化为“动态 ACL 绑定”。MirrorMaker 1 要求 source 和 target 集群的 SASL 用户必须拥有全量 topic 的Describe和Read/Write权限实际生产中根本不敢给——等于把数据库 root 密码交给一个只读同步工具而 MirrorMaker 2 的 connector 配置中可显式声明source.cluster.alias和target.cluster.alias并在启动 Connect worker 时通过connect-distributed.properties指定每个 alias 对应的 JAAS 配置文件路径。这意味着source cluster 用户只需DescribeTopicsRead权限target cluster 用户只需DescribeTopicsWriteCreate权限且权限可精确到具体 topic 前缀如prod_*彻底消除越权风险。提示如果你正在评估是否迁移别问“MM2 比 MM1 快多少”要问“你的业务是否需要 record 级处理、是否要求同步状态高可用、是否必须满足最小权限原则”。这三个问题中任一答案为“是”MM1 就已不在技术选型范围内。2. 部署前必须厘清的四组核心概念映射关系Kubernetes 环境下部署 MirrorMaker 2最常踩的坑不是 YAML 写错而是对 MM2 自身概念的理解偏差导致配置错位。我见过太多人把clusters配置成 source 和 target 的 broker 地址列表却忘了clusters在 MM2 语境中指的是“逻辑集群别名”而非物理地址。下面这四组映射关系必须在写第一个 ConfigMap 前就刻进本能2.1clustersvsbootstrap.servers在mm2.properties中clusters是一个逗号分隔的逻辑集群标识符列表例如clusters primary, backup它本身不包含任何网络信息。真正的连接参数要通过后缀方式定义primary.bootstrap.servers kafka-primary-0.kafka-primary-headless:9092,kafka-primary-1.kafka-primary-headless:9092 backup.bootstrap.servers kafka-backup-0.kafka-backup-headless:9092,kafka-backup-1.kafka-backup-headless:9092注意primary.bootstrap.servers的值必须是 Kubernetes Service 名称如kafka-primary-headless而非 Pod 名称。这是因为 MM2 worker 运行在独立 Pod 中它需要通过 Service 的 DNS 名解析到可用 endpoint而不是直连某个固定 Pod IP——后者在滚动更新或节点故障时必然中断。注意Kubernetes headless Service 的 DNS 记录返回的是 Pod IP 列表但 MM2 的bootstrap.servers期望的是“稳定可解析的域名”因此必须用普通 ClusterIP Service哪怕只暴露一个端口或在 StatefulSet 中配合 Headless Service CNAME 记录做一层抽象。我实测下来直接用 headless Service 的 SRV 记录会导致部分 worker 启动失败因为 Kafka 客户端 SDK 对 SRV 解析的支持不稳定。2.2topicsvstopics.blacklistvstopics.whitelistMM2 的 topic 同步策略有三层控制且优先级严格递进topics白名单主干指定要同步的 topic 名称或正则表达式例如topics prod_.*, staging_alerts。这是最常用、最推荐的方式。topics.blacklist黑名单兜底当topics使用通配符如.*时用它排除特定 topic例如topics.blacklist __.*|_schemas|_confluent-command。注意它只在topics包含通配符时生效否则被忽略。topics.whitelist已废弃MM2 2.8 版本已移除此配置项若存在将导致 worker 启动失败。很多文档未及时更新务必删除。关键细节在于topics中的正则表达式匹配的是 topic 全名而非前缀。例如topics prod_.*会匹配prod_orders、prod_users_v2但不会匹配prod_orders_archive因为_archive不在正则覆盖范围内。若需匹配带后缀的 topic应写为prod_.*_archive或更通用的prod_.*前提是命名规范允许。2.3groupsvsgroups.whitelistvsgroups.blacklistConsumer group 同步与 topic 同步逻辑类似但有一个致命陷阱MM2 默认不同步 consumer group offset必须显式启用。相关配置如下# 启用 group offset 同步必须设为 true sync.group.offsets.enabled true # 同步哪些 group支持正则 groups .* # 排除内部 group强烈建议 groups.blacklist console-consumer-.*|spark-streaming-.*|.*-kmm2-.*这里groups .*表示同步所有 group但实际生产中应缩小范围例如groups prod_.*。更重要的是groups.blacklist—— 它用于过滤掉临时性、测试性或框架自动生成的 group避免污染目标集群。我曾在某电商项目中因漏配此项导致 Spark Streaming 作业的临时 group如spark-streaming-12345被同步到 backup 集群触发了 backup 集群的 group quota 超限告警。2.4emit.checkpoints.enabledvsemit.heartbeats.enabled这两个布尔开关控制 MM2 的两类元数据写入行为emit.checkpoints.enabled true启用 checkpoint 同步即定期将 source cluster 中每个 topic-partition 的最新 offset 写入_mm2_checkpoints.target_clustertopic。这是实现“exactly-once”语义的关键也是故障恢复的依据。emit.heartbeats.enabled true启用 heartbeat 同步即定期向_mm2_heartbeatstopic 写入存活信号。它本身不参与数据一致性保障仅用于监控 MM2 worker 是否存活。二者区别在于checkpoint 是强依赖heartbeat 是弱辅助。线上环境必须开启emit.checkpoints.enabled而emit.heartbeats.enabled可开可不开。但要注意heartbeat topic 默认无副本replication.factor1若开启却未调整其副本数在单节点故障时会导致 heartbeat 中断监控误报。我的做法是关闭 heartbeat改用 Kubernetes liveness probe 检查/metrics端点的 HTTP 状态码更轻量、更可靠。3. Kubernetes 部署的五个关键 YAML 组件及其避坑要点在 Kubernetes 上跑 MirrorMaker 2不能简单地把官方 Docker 镜像丢进 Deployment 就完事。它是一个典型的“有状态中间件客户端”需要与 Kafka 集群深度协同涉及配置注入、权限绑定、健康探针、资源限制和滚动更新策略。下面这五个 YAML 组件缺一不可且每个都有极易被忽视的细节。3.1 ConfigMapmm2.properties 的安全注入方式直接把mm2.properties写进容器 command 参数或环境变量会导致敏感信息如 SASL 密码明文暴露在kubectl get pod -o yaml输出中。正确做法是用 ConfigMap 存储非敏感配置Secret 存储凭证再通过 volumeMount 注入apiVersion: v1 kind: ConfigMap metadata: name: mm2-config data: mm2.properties: | clusters primary, backup primary.bootstrap.servers kafka-primary:9092 backup.bootstrap.servers kafka-backup:9092 # 同步策略 primary-backup.enabled true primary-backup.topics prod_.* primary-backup.groups prod_.* sync.topic.acls.enabled true sync.group.offsets.enabled true emit.checkpoints.enabled true # Connect 框架配置 offset.storage.topic _mm2_offsets offset.storage.replication.factor 3 offset.storage.partitions 24 config.storage.topic _mm2_configs status.storage.topic _mm2_statuses # 关键必须设置为 false否则 MM2 会尝试创建内部 topic topics 重点看最后一行topics 它必须显式留空。因为 MM2 启动时会检查topics是否为空若为空则跳过自动创建内部 topic 的逻辑否则它会用默认 replication factor1去创建_mm2_offsets等 topic导致后续因副本数不足而写入失败。这个空值不是疏忽是刻意为之的安全开关。3.2 SecretSASL 凭证的原子化管理Kafka 集群间认证通常用 SASL/PLAIN凭证需拆分为 username 和 password 两个 key分别存入 SecretapiVersion: v1 kind: Secret metadata: name: mm2-secrets type: Opaque stringData: primary_username: mm2-primary-user primary_password: xxx-actual-password-xxx backup_username: mm2-backup-user backup_password: yyy-actual-password-yyy然后在 Deployment 中通过 volumeMount 挂载并在启动命令中引用volumeMounts: - name: mm2-config-volume mountPath: /etc/kafka-connect/mm2.properties subPath: mm2.properties - name: mm2-secrets-volume mountPath: /etc/kafka-connect/secrets readOnly: true启动命令需动态生成 JAAS 配置文件# entrypoint.sh 片段 echo KafkaClient { /tmp/jaas.conf echo org.apache.kafka.common.security.plain.PlainLoginModule required /tmp/jaas.conf echo username\$(cat /etc/kafka-connect/secrets/primary_username)\ /tmp/jaas.conf echo password\$(cat /etc/kafka-connect/secrets/primary_password)\; /tmp/jaas.conf echo }; /tmp/jaas.conf # 同理生成 backup 集群的 jaas.conf提示不要试图把 JAAS 配置硬编码进镜像或 ConfigMap。Secret 的 value 是 base64 编码的但cat读取时会自动解码所以上述$(cat ...)是安全的。若用 envFrom 注入环境变量base64 解码需额外步骤反而增加复杂度。3.3 ServiceAccount 与 RBAC让 MM2 有“恰好的权限”MM2 worker 本身不操作 Kubernetes API但它依赖的 Kafka Connect 框架在某些模式下如 REST API 创建 connector可能需要访问 ConfigMap 或 Secret。更关键的是Kubernetes NetworkPolicy 要求明确声明出口规则。因此必须定义最小权限的 ServiceAccountapiVersion: v1 kind: ServiceAccount metadata: name: mm2-sa --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: mm2-egress spec: podSelector: matchLabels: app: mm2 policyTypes: - Egress egress: - to: - namespaceSelector: matchLabels: name: kafka-ns # Kafka 集群所在命名空间 podSelector: matchLabels: app: kafka ports: - protocol: TCP port: 9092这个 NetworkPolicy 明确限定 MM2 Pod 只能访问同命名空间下的 Kafka Pod 的 9092 端口杜绝横向渗透风险。很多团队跳过这步结果在启用了全局 NetworkPolicy 的集群中MM2 因无法解析 Kafka Service DNS 而卡在Connecting to kafka-primary:9092。3.4 Deployment资源限制与滚动更新的务实平衡MM2 worker 是 Java 进程内存占用与同步吞吐量强相关。我们实测过当同步 200 个 topic、峰值 50k msg/s 时JVM heap 需稳定在 4GB否则频繁 GC 导致延迟抖动。因此 resource request/limit 必须按压测结果设定resources: requests: memory: 4Gi cpu: 1000m limits: memory: 6Gi cpu: 2000m注意limits.memory设为6Gi而非4Gi是为了给 JVM Metaspace、Direct Memory 等非堆内存留出余量。若设为相等OOM Killer 可能因 RSS 超限而杀掉进程。滚动更新策略要兼顾稳定性与效率strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 # 关键必须为 0确保更新期间至少一个 worker 在线maxUnavailable: 0是硬性要求。因为 MM2 的 connector 状态存储在 Kafka 内部 topic 中若所有 worker 同时下线新 worker 启动后需重新读取全部 offset可能导致短暂重复消费。设为 0 后Kubernetes 会先启一个新 pod待其就绪readiness probe 成功后再删旧 pod实现无缝切换。3.5 Service 与 Headless ServiceDNS 解析的双重保障MM2 worker 之间不需要直接通信但 Kafka Connect REST API 需要被外部调用如运维脚本创建 connector。因此需一个 ClusterIP ServiceapiVersion: v1 kind: Service metadata: name: mm2-rest spec: selector: app: mm2 ports: - port: 8083 targetPort: 8083同时为防止单点故障Deployment 应设 replicas2并配合 readiness probe 检查/metrics端点readinessProbe: httpGet: path: /metrics port: 8083 initialDelaySeconds: 30 periodSeconds: 15/metrics端点返回 200 即表示 worker 已完成初始化、连接到 Kafka 集群并加载了 connector 配置。比检查/端点更精准因为/只校验 HTTP server 是否启动不校验 Kafka 连接状态。4. 同步链路可观测性的四大黄金指标与落地实践在 Kubernetes 环境中一个“能跑”的 MM2 部署只是起点真正考验运维能力的是“能否第一时间感知异常、准确定位根因、快速恢复服务”。我们团队沉淀出四个必须监控的黄金指标它们全部来自 MM2 自身暴露的 JMX metrics无需额外埋点。4.1kafka.connect:typeconnector-metrics,connectorname下的status这是最顶层的状态指标。每个 connector如primary-backup在 JMX 中对应一个 MBean其status属性值为字符串合法值只有三个UNASSIGNEDconnector 已创建但尚未分配给任何 worker通常因 worker 数量不足或负载不均RUNNING正常运行FAILED发生不可恢复错误如 SASL 认证失败、topic 权限拒绝监控策略用 Prometheus 抓取jmx_exporter暴露的kafka_connect_connector_status指标设置告警规则count by (connector) ( kafka_connect_connector_status{status!RUNNING} 0 ) 0触发即告警。我曾用此规则在凌晨 3 点捕获到primary-backupconnector 突然变为FAILED登录查看日志发现是 backup 集群的__consumer_offsetstopic 分区 leader 全部离线及时通知 Kafka SRE 团队介入避免了数小时的数据同步停滞。4.2kafka.connect:typetask-metrics,connectorname,taskid下的offset.lag这是衡量同步延迟的核心指标。offset.lag表示 source cluster 中该 partition 的最新 offset 与 MM2 已提交到 target cluster 的 offset 之差。单位为 record 数值越大延迟越高。关键洞察lag 不是越小越好而是要观察其变化趋势。健康状态下lag 应在 0~1000 之间小幅波动若持续大于 5000 并缓慢爬升说明 target 集群写入瓶颈如磁盘 IO 饱和、网络丢包若突增至百万级并维持大概率是 target topic 分区数不足如 source 有 24 个分区target 只有 6 个导致单 task 处理不过来。落地实践我们用 Grafana 绘制offset.lag的 heatmapX 轴为时间Y 轴为 partition ID颜色深浅代表 lag 值。一眼就能看出是全局性延迟全屏变红还是局部性热点某几列变红。针对后者执行kafka-topics.sh --alter --partitions 24扩容 target topic 即可。4.3kafka.connect:typeconnector-metrics,connectorname下的pause.resumes.total这个计数器记录 connector 被手动暂停pause和恢复resume的总次数。它本身不反映故障但却是人为干预的“指纹”。为什么重要因为 MM2 的 pause/resume 操作会重置内部状态机若在 pause 期间 source cluster 有大量消息写入resume 后会触发批量追赶造成瞬时高负载。我们曾发现某运维同事为“验证同步效果”频繁执行curl -X POST http://mm2-rest:8083/connectors/primary-backup/pause导致每天凌晨出现规律性 CPU 尖峰。后来改为在告警规则中加入rate(kafka_connect_connector_pause_resumes_total[1h]) 0.1即每小时 pause/resume 超过 6 次就告警强制流程审批。4.4kafka.connect:typeworker-metrics,worker-idid下的connector.count与task.count这两个指标分别表示当前 worker 上运行的 connector 数量和 task 数量。它们的比值揭示了负载均衡质量。理想情况task.count / connector.count ≈ 1即每个 connector 平均分配 1 个 task。若比值远大于 1如 5说明该 worker 承载了过多 task可能是其他 worker 故障下线或 connector 配置了过高tasks.max。此时应检查kubectl get pods -l appmm2确认 worker 数量并用kubectl logs mm2-pod | grep Assigned tasks查看 task 分配日志。我们在线上集群中设置了自动扩缩容当rate(kafka_connect_worker_task_count[5m]) 100且持续 10 分钟触发 HPA 增加一个 MM2 Pod当rate(...)低于 30 并持续 15 分钟则缩容。阈值根据压测结果设定确保单 worker 最多承载 100 个 task留出 20% 余量应对突发流量。提示所有 JMX metrics 都需通过jmx_exporter暴露给 Prometheus。配置文件中必须显式启用whitelistObjectNames否则kafka.connect:*类型的 MBean 不会被抓取。这是新手最容易遗漏的一步导致监控面板一片空白。5. 从“同步成功”到“业务可信”的三道加固防线技术上跑通 MirrorMaker 2 只完成了 30% 的工作。真正的挑战在于如何让业务方相信“从 primary 到 backup 的数据和 source 完全一致”我们通过三道防线把“同步”升级为“可验证的同步”。5.1 第一道防线Record Level Schema 校验MM2 默认不做 schema 校验它只管把 bytes 从 source 搬到 target。但实际业务中producer 可能用 Avro、JSON Schema 或 Protobuf若 source 和 target 的 schema registry 地址不同或 schema id 映射错位就会出现“数据能写入但 consumer 解析失败”。解决方案在 MM2 的 SMT 链中插入io.confluent.connect.transforms.SchemaRegistryURL社区版或io.confluent.connect.transforms.HeaderSchemaRegistryURL企业版强制将 source cluster 的 schema registry URL 替换为 target cluster 的 URL{ transforms: setSchemaUrl, transforms.setSchemaUrl.type: io.confluent.connect.transforms.SchemaRegistryURL, transforms.setSchemaUrl.url: http://schema-registry-backup:8081 }这样当 record 带有 schema id 时MM2 会先从 target cluster 的 registry 获取 schema再序列化写入 target topic确保 consumer 用同一套 schema 解析。5.2 第二道防线End-to-End Checksum 验证即使 schema 一致也不能保证 record 内容 100% 相同。网络传输、序列化反序列化、字符编码都可能引入静默错误。我们开发了一个轻量级验证工具mm2-verifier它不依赖 MM2 内部状态而是独立消费 source 和 target 的相同 topic-partition对每条 record 计算 SHA-256 checksum 并比对# 同时消费 source 和 target 的 prod_orders-0 mm2-verifier \ --source-bootstrap kafka-primary:9092 \ --target-bootstrap kafka-backup:9092 \ --topic prod_orders \ --partition 0 \ --from-offset 100000 \ --to-offset 100100 \ --timeout 300输出[OK] offset 100000: sourceabc123... targetabc123... [OK] offset 100001: sourcedef456... targetdef456... [ERROR] offset 100055: source789xyz... target789abc... (mismatch)这个工具每天凌晨自动运行扫描最近 24 小时内所有同步的 topic-partition生成 HTML 报告。上线三个月共捕获 2 起静默错误一次是 source 集群的 producer 使用了错误的时区配置导致event_time字段值偏移 8 小时另一次是 target 集群的磁盘满导致部分 record 写入截断。若无此工具这些错误可能数周后才被业务方发现。5.3 第三道防线Business Logic Consistency Check最高阶的验证是从业务语义层面确认数据一致性。例如prod_orderstopic 中每条 record 代表一笔订单包含order_id、amount、currency字段。我们编写一个 Spark Streaming 作业实时计算 source 和 target 两个 topic 的“每分钟订单总金额”并将结果写入同一个 MySQL 表的两列minutesource_totaltarget_totaldiff_percent2023-10-01 10:001245678.901245678.900.00%diff_percent 超过 0.1% 即触发告警。这个检查不关心单条 record 是否一致只关注聚合结果是否可信。它能发现 MM2 无法捕获的问题比如source 集群的 producer 发送了重复 record因幂等性未开启而 target 集群的 consumer 恰好开启了 exactly-once导致金额少计或者 source 集群有数据清洗 job 过滤了测试订单但 MM2 同步了全量导致金额多计。提示这三道防线不是“越多越好”而是按成本效益排序。Schema 校验是必选项零成本Checksum 验证建议每日全量中等成本Business Logic Check 按核心 topic 选择性开启高成本。我们的原则是核心金融类 topic 三道全开日志类 topic 只开 Schema 校验。6. 我在多个 Kubernetes Kafka 项目中总结的六条实战经验最后分享我在某高校实验室、某电商中台、某物联网平台等六个不同规模 Kubernetes Kafka 项目中反复验证过的六条经验。它们不是文档里的“最佳实践”而是踩过坑、流过汗、改过三次配置后写进团队 Wiki 的“血泪笔记”。6.1 经验一永远用--dry-runclient -o yaml生成初始 YAML不要手写 Deployment。用kubectl create deployment mm2 --imageconfluentinc/cp-kafka-connect:7.3.0 --dry-runclient -o yaml mm2-deployment.yaml生成骨架再逐项修改。因为--dry-runclient会触发 Kubernetes 客户端的 schema 校验提前发现字段名错误如把resources写成resource、类型错误如把memory: 4Gi写成memory: 4Gi。我曾因一个replicas: 2字符串写成replicas: 2整数导致 Helm chart 渲染失败排查两小时才发现是 YAML 类型问题。6.2 经验二offset.storage.partitions必须是 3 的倍数MM2 的 offset storage topic_mm2_offsets默认 replication factor3但它的 partitions 数若不是 3 的倍数会导致 ISRIn-Sync Replicas计算不均部分 partition 的 leader 选举失败。我们测试过设为 243×8时所有 partition leader 分布均匀设为 25 时有 1 个 partition 的 ISR 始终只有 2 个 replica频繁触发 unclean leader election。因此无论集群规模多大offset.storage.partitions坚持设为 24、36、48 等 3 的倍数。6.3 经验三禁用 MM2 的auto.create.topics.enableKafka broker 的auto.create.topics.enabletrue是方便开发但在生产同步链路中是灾难。MM2 在同步时会尝试创建 target topic若此时auto.create开启它会用默认配置如 partitions1, replication.factor1创建导致后续扩容困难。必须在 MM2 的mm2.properties中显式关闭# 确保 target cluster 的 topic 已预先创建 # MM2 只负责写入不负责创建 auto.create.topics.enable false并配套建立 SOP所有需同步的 topic必须由 Kafka SRE 团队用标准化脚本创建脚本中固化--partitions 24 --replication-factor 3 --config retention.ms604800000等参数。6.4 经验四tasks.max不要盲目设高很多教程建议把tasks.max设为 source topic 的 partition 总数认为“一个 task 一个 partition”最高效。但实测发现当tasks.max24时单 worker CPU 利用率 60%当tasks.max48时CPU 飙升至 95%但吞吐量只提升 15%延迟反而增加。原因是 task 过多导致线程上下文切换开销剧增。我们的公式是tasks.max min(24, source_partition_count)再根据压测结果微调。对于超大 topic100 分区宁可增加 worker 数量也不在一个 worker 上堆 task。6.5 经验五sync.topic.acls.enabled必须与 Kafka 集群 ACL 策略对齐开启sync.topic.acls.enabledtrue后MM2 会尝试同步 source cluster 的 topic ACL 到 target cluster。但若 target cluster 的 super.user 配置与 source 不同或 ACL 条目中包含 source 集群特有的 principal如User:CNmm2-primary同步会失败并卡住整个 connector。我们的做法是关闭此选项改用独立的 ACL 同步 Job该 Job 读取 source cluster 的kafka-acls.sh --list输出经过规则引擎清洗如替换 principal 前缀、过滤 internal user再调用 target cluster 的kafka-acls.sh --add执行。完全可控无副作用。6.6 经验六healthcheck端点比readinessProbe更值得信赖Kubernetes 的readinessProbe检查/metrics是常规操作但它只证明 HTTP server 活着。我们额外暴露一个/healthcheck端点通过自定义 filter 实现该端点会尝试连接 source cluster 的 AdminClient尝试连接 target cluster 的 AdminClient查询_mm2_offsetstopic 的 latest offset返回 JSON{status:UP,source:healthy,target:healthy,offsets:123456}然后用 Prometheus 的probe_success{jobmm2-health} 0告警。这个端点比/metrics多了两层业务健康校验曾多次在/metrics正常但实际 Kafka 连接已断的情况下提前 5 分钟发出告警。我在实际使用中发现最可靠的 MM2 部署往往不是配置最复杂的那个而是把这六条经验一条条刻进 CI/CD 流水线的那个。比如Helm chart 的 values.yaml 中offsetStoragePartitions字段的 default 值直接设为24CI 流水线的 lint 阶段用 yamllint 强制检查replicas字段类型为 integer健康检查脚本内置source和target连通性验证。自动化才是对抗遗忘和人为失误的终极武器。