Kafka+zookeeper 启动timeout故障的完整排查
Kafka 启动超时故障的完整排查
每次发现在内部部署一套新的 zk + kafka 集群,都发现Kafka 起不来。
日志是这个:
1 | |
看了一圈,三个节点全是 running,echo ruok | nc 10.0.181.141 9009 打过去全是 imok;
端口在,进程在,nc 手工探一下也没任何异常。
盯着日志多看了一会儿,发现中间空了将近 36 秒,但 Kafka 默认的 ZooKeeper 连接超时是 6 秒。
1 | |
也就是说,Kafka 不是在”连接失败”上超时的,而是在”TCP 握手都还没开始”之前就已经超时了。这两件事差别很大——前者是 ZooKeeper 的问题,后者说明 Kafka 在真正发起连接之前,卡在了别的地方。
应该就是网络问题了
反向查询阻塞
分布式的 Java 组件有个出了名的行为,InetAddress.getHostName() 在拿到 IP 之后会反过来做一次 PTR 查询,把 IP 解析回主机名。这个动作发生在连接建立之前,而且是同步阻塞的。
以这个视角尝试一下下面的命令,如果读者也发现这步阻塞了很久,那也一样是网络问题:
1 | |
等了将近 18 秒才返回 ( -. - ) 。
三个 ZooKeeper 节点,每个 18 秒,加起来 54 秒,有 JVM 缓存命中会少一些,最终体现在日志里就是那个 36 秒的空洞。时间线完全吻合。
继续往下查,看 /etc/nsswitch.conf:
1 | |
顺序是先查 /etc/hosts,再走 DNS。打开 /etc/hosts 一看,只有 127.0.0.1 localhost,没有任何内网地址。
1 | |
于是解析请求落到了 DNS,去做 PTR 反查:
1 | |
1 | |
这台内网 DNS 根本没有配 RFC1918 私网地址的反向解析区域,每次查询等到超时才返回 SERVFAIL,每次 4 秒左右。
整个链路就清楚了:
Kafka → ZooKeeper Java Client → InetAddress.getHostName() → NSS → /etc/hosts 没有 → DNS PTR 查询 → SERVFAIL 等 4 秒 → 三个节点累计 → 超过 6 秒超时阈值 → Broker 启动失败
最终修复
修起来很简单,在 /etc/hosts 里把这几个地址补上:
1 | |
再跑一次 time getent hosts 10.0.181.142,毫秒级返回。Kafka 随即正常启动。
1 | |
这个问题本身不复杂,但它有一个很有迷惑性的地方:所有基础探测都是正常的。端口通、进程活、nc 无异常,但 JVM 网络栈在 DNS 层悄悄地 hang 住了,表现出来的是应用层的超时。
如果只看 Kafka 日志,很容易把锅甩给 ZooKeeper;如果去查 ZooKeeper,又什么问题都查不出来。真正的线索藏在那个 36 秒的时间空洞里,以及一条 time getent hosts 命令上。