深入解析Kafka ZooKeeper连接问题
通过配置、网络、超时、日志和代理负载等实用检查,排查Kafka ZooKeeper连接故障。
深入解析Kafka ZooKeeper连接问题
Kafka ZooKeeper连接问题主要影响较旧的Kafka集群以及尚未迁移到KRaft模式的集群。较新的Kafka部署可以不依赖ZooKeeper运行,但许多生产系统仍然依赖它。如果你的代理在server.properties中使用了zookeeper.connect,那么ZooKeeper仍然是控制平面的一部分,需要像Kafka本身一样得到重视。
当Kafka代理无法维持其ZooKeeper会话时,症状可能看起来比简单的连接问题更严重。代理可能重启。控制器选举可能重复。分区可能不可用。日志可能显示会话过期、控制器辞职或反复重连尝试。生产者和消费者可能只看到下游影响:元数据错误、超时或不稳定的领导者。
首先了解ZooKeeper的角色。在基于ZooKeeper的Kafka集群中,代理在此注册,控制器选举依赖它,集群元数据协调通过它进行。如果代理失去ZooKeeper会话的时间足够长以致会话过期,Kafka会将该代理视为已从集群中消失。即使代理进程仍在运行,集群也可能将领导权移走。
第一个检查点虽然简单但常常能发现问题:验证每个代理上的zookeeper.connect。
zookeeper.connect=zk01.example.com:2181,zk02.example.com:2181,zk03.example.com:2181/kafka
zookeeper.connection.timeout.ms=18000
zookeeper.session.timeout.ms=18000
连接字符串应列出Kafka可以访问的集群成员。如果使用诸如/kafka的chroot路径,请确保在每个代理上一致地包含它。不要将一半代理配置为使用/kafka而另一半不使用;它们会表现得像是在与不同的Kafka集群通信。如果确实使用chroot,请先创建它或使用ZooKeeper工具确认其存在。
除了检查配置文本,还要检查DNS。在你的笔记本电脑上能正确解析的主机名可能无法从代理子网解析。请从Kafka代理主机运行检查,而不是从堡垒机,除非堡垒机具有相同的网络路径。
getent hosts zk01.example.com
nc -vz zk01.example.com 2181
nc -vz zk02.example.com 2181
nc -vz zk03.example.com 2181
成功的TCP连接并不能证明ZooKeeper健康,但连接失败足以让你深入检查防火墙、安全组、路由、DNS或监听器配置。测试每个Kafka代理到每个ZooKeeper节点。部分连接比完全中断更糟糕,因为故障可能只在代理尝试连接特定集群成员时出现。
ZooKeeper的四字母命令在启用时可以提供帮助。许多安装限制了它们,所以不要假设它们有效。如果允许,ruok应返回imok,mntr可以显示有用的服务器统计信息。
echo ruok | nc zk01.example.com 2181
echo mntr | nc zk01.example.com 2181
如果这些命令被禁用,请使用支持的管理工具或监控栈。关键是回答一个简单的问题:ZooKeeper是否在监听、参与集群并快速响应?
接下来,检查ZooKeeper集群的健康状况。三个节点的集群可以容忍一个ZooKeeper节点宕机。它不能容忍两个。五个节点的集群可以容忍两个。避免使用偶数大小的集群,因为它们会增加成本,但不会像人们期望的那样提高法定人数。三和五是常见的选择。
在ZooKeeper端,查看zoo.cfg。确认clientPort、tickTime、initLimit、syncLimit和服务器行。确保广告的服务器主机名在ZooKeeper节点之间可达,而不仅仅是从Kafka代理可达。ZooKeeper对等节点需要自己的法定人数和领导者选举端口。Kafka代理可能可以访问2181,但ZooKeeper集群本身可能因为对等流量被阻塞而不健康。
会话超时调整是另一个常见的混淆来源。Kafka向ZooKeeper请求会话超时,但ZooKeeper根据自身配置强制执行限制。在ZooKeeper中,最小会话超时通常是2 * tickTime,最大通常是20 * tickTime,除非被特定的服务器设置覆盖。这意味着超出允许范围的Kafka超时值可能会被ZooKeeper调整。
如果tickTime=2000,通常允许的会话范围大约是4秒到40秒。Kafka设置如zookeeper.session.timeout.ms=18000在此范围内。非常低的超时可能在短暂的网络暂停或垃圾回收暂停期间产生虚假故障。非常高的超时可能使真正的代理故障检测时间变长。你需要在敏感性和稳定性之间做出选择。
不要随意更改tickTime。它影响ZooKeeper集群,而不仅仅是Kafka。如果你需要更多的代理暂停容忍度,通常最好先审查Kafka的zookeeper.session.timeout.ms、代理JVM行为和网络健康状况,然后再更改ZooKeeper定时。
日志通常能说明问题,如果你按时间戳对齐它们。在Kafka代理上,搜索与ZooKeeper断开连接和会话过期相关的消息:
rg -i "zookeeper|session|expired|controller|reconnect" /var/log/kafka/server.log
模式比单行更重要。在计划中的ZooKeeper重启期间的一次性重连可能是无害的。每隔几分钟重复过期则指向不稳定。垃圾回收期间的过期指向JVM暂停或代理过载。许多代理同时过期指向ZooKeeper、网络或共享基础设施事件。
在ZooKeeper节点上,检查领导者变更、fsync警告、连接限制和长请求延迟。ZooKeeper对磁盘延迟敏感,因为它写入事务日志。慢速磁盘可能使服务看似可达,但无法足够快地响应以维持稳定会话。
对于ZooKeeper,网络延迟和丢包比原始带宽更重要。Kafka代理不需要与ZooKeeper有巨大的吞吐量,但它们需要可靠、低延迟的通信。如果代理和ZooKeeper分布在遥远的网络上,预计会出现问题。让它们靠近。在云环境中,避免将代理到ZooKeeper的流量路由通过不必要的NAT、过载的防火墙或跨区域路径。
Kafka代理上的资源争用可能看起来完全像ZooKeeper问题。如果JVM因长时间的垃圾回收暂停而停止世界,代理可能错过心跳。如果CPU饱和,心跳处理可能延迟。如果主机陷入高I/O等待,Kafka可能无法跟上协调工作。在与ZooKeeper断开连接相同的时间戳检查代理指标。
有用的代理端问题包括:堆使用量是否在断开连接前攀升,GC暂停时间是否激增,磁盘I/O等待是否高,网络重传是否增加,以及当时是否有大的分区重新分配或领导者移动。一个负载过重的代理可能需要更少的分区领导者、更好的磁盘、JVM调优或流量转移。增加ZooKeeper超时可能掩盖症状而不解决根本原因。
配置一致性很容易被忽视。同一Kafka集群中的所有代理应使用相同的ZooKeeper连接字符串和chroot。它们还应有唯一的broker.id值。重复的代理ID可能导致令人困惑的注册行为,因为两个进程试图代表同一个代理。
如果你最近更改了ZooKeeper主机名、证书、防火墙规则或Kafka代理配置,请比较正常工作的代理和出故障的代理。小的差异很常见:旧的DNS后缀、缺失的chroot路径、附加到两个代理但未附加到第三个的安全组,或一个systemd环境文件中的拼写错误。
恢复取决于什么出了问题。如果缺少防火墙规则,修复它并重启受影响的代理(如果它不能干净地重连)。如果ZooKeeper失去了法定人数,在重启Kafka代理之前先恢复法定人数。如果代理因过载而过期,重启可能暂时恢复,但除非消除压力,否则问题会再次出现。
使用滚动重启。因为ZooKeeper不稳定而同时重启所有Kafka代理可能将部分中断变成完全中断。先恢复ZooKeeper健康,然后逐个重启或恢复代理,同时观察控制器稳定性和分区领导权。
为了长期稳定,监控双方。在ZooKeeper端,监控请求延迟、未完成请求、领导者变更、跟随者同步状态、磁盘空间和进程重启。在Kafka端,监控控制器变更、离线分区、未充分复制分区、代理重启以及提及ZooKeeper会话过期的日志。对重复模式发出警报,而不仅仅是进程完全死亡。
对于计划更大升级的团队,最干净的修复可能是从ZooKeeper迁移到Kafka的KRaft模式。这是一个项目,而不是事件响应步骤。它需要版本规划、兼容性检查和仔细的迁移工作。在此之前,将ZooKeeper视为生产基础设施。保持其小巧、靠近Kafka、配置一致、受监控且稳定。
一个实用的运行手册模式是在事件期间构建一个小矩阵。将Kafka代理放在一个轴上,ZooKeeper节点放在另一个轴上。用nc -vz host 2181的结果填充每个单元格,如果可用,再加上一个简单的ZooKeeper健康检查。这将模糊的“Kafka无法访问ZooKeeper”报告转化为可见的模式。如果每个代理都无法访问zk02,调查zk02或其网络路径。如果只有broker-4无法访问每个ZooKeeper节点,调查该代理的主机、路由表、DNS或防火墙。
时间同步也可能重要。ZooKeeper会话机制不要求每个操作都有完全相同的挂钟时间,但严重偏斜的时钟会使日志更难解释,并可能破坏周围的自动化、证书和监控。保持Kafka和ZooKeeper节点上的NTP或chrony健康。当时间戳在中断期间不一致时,人们会浪费时间追逐错误的事件顺序。
小心容器化或编排的ZooKeeper部署。ZooKeeper将身份和数据存储在磁盘上。如果Pod移动并丢失持久身份,或者服务发现将客户端指向未准备好的节点,Kafka可能会看到不稳定的连接行为。StatefulSet风格的身份、持久卷、稳定的DNS和就绪检查很重要。ZooKeeper集群不应表现得像一组可丢弃的无状态Web Pod。
安全设置增加了另一层。如果最近引入了SASL、TLS或网络策略,连接故障最初可能看起来像简单的可达性问题。检查Kafka日志是否显示身份验证失败、握手失败或授权错误,而不是TCP超时。端口可能开放,但会话仍然失败,因为代理无法向ZooKeeper进行身份验证。
事件发生后,保留简短的确切症状、根本原因和修复记录。ZooKeeper问题经常重复,因为原始修复是局部的:一条防火墙规则、一次代理重启、一次超时调整。一个好的事后记录应说明集群是否有法定人数、哪些代理丢失了会话、ISR是否缩小、控制器是否变更,以及哪些监控将更早地捕获它。
如果你从Kubernetes或其他调度器进行故障排除,还要检查Kafka和ZooKeeper工作负载落在哪里。节点级别的网络问题、磁盘问题或CPU饥饿事件可能只影响调度到那里的Pod。移动Pod可能看起来修复了问题,但真正的问题可能是主机。在宣布应用程序修复之前,比较事件和节点指标。
备份和快照需要谨慎。除非你的备份方法是为其设计的,否则不应在进程活动时随意快照ZooKeeper数据目录。对于Kafka元数据,损坏或过时的ZooKeeper状态可能极具破坏性。遵循ZooKeeper支持的备份实践,并在生产环境之外测试恢复过程。一个没有人恢复过的备份只是一个充满希望的文件。
最好的预防措施是让ZooKeeper保持稳定。如果可能,避免将其与重负载的Kafka代理放在一起。保持其磁盘可靠。保持堆大小保守并受监控。限制谁可以更改集群成员资格。我见过的大多数ZooKeeper事件并非由奇特的错误引起;它们来自围绕一个被遗忘的小服务的普通基础设施漂移。