KAFKA跨主机部署网络不通解决思路

问题背景:

Kafka的部署不仅需要集群可用,同时需要对orderer节点可连,这就是为什么有的时候,kafka集群本身没问题,但是orderer却总是报错。为了试验kafka剥离方案的可行性,跨阿里云网络和内网进行BAAS部署。

部署环境如下:

K8s部署在阿里云环境上,
192.168.8.108可连外网,作为master;
192.168.8.107不能连外网,作为slave;

Kafka集群部署在内网,
192.168.9.21等机器上,都可以连外网。

因为orderer节点会起在slave机器上,也就是107这台机器,它无法直连外网。因此,通过nginx转发来保证orderer可以连上kafka集群,如下图所示。
1592847-20190530105150256-1674394745.png

那么advertised listeners的配置就尤为重要,毕竟这是kafka节点保存在zookeeper集群中的brokers元信息,orderer最终是通过这些地址去访问kafka的。

如果将kafka0的KAFKA_ADVERTISED_LISTENERS地址设为192.168.9.21:9092,虽然集群创建正常,但是orderer无法连上内网地址,也就是无法连上kafka。所以,选择将kafka0的KAFKA_ADVERTISED_LISTENERS地址设为192.168.8.108:9092,然后在108上设置nginx代理,转发到 内网代理IP:9092(反向代理,通过该ip,外网可以连接内网。),这样就可以连上kafka0节点了。

尝试Setupbaas 发现orderer仍然报错kafka集群异常,但是kafka的启动日志没有任何异常。

setup的流程很长,还要清理环境,用kafkaclient来调试会方便很多。 fabric用的go语言client是sarama,简单改一下fabric_test里面的producer就可以起一个简单的client,来测试kafka集群是否可用了。

用producer向kafka写入数据,发现报错信息如下:

1592847-20190530105212745-2078013124.png

报错说明,现在这个partiton没有leader,我们知道kafka每个partiton都会有一个leader,负责client的读写。

为了确认测试用的partition到底有没有leader,通过kafka内部的kafka-topic.sh来查看详细信息,结果如下图所示:

1592847-20190530105222228-75484452.png

结果发现,topic首先是创建成功了,partition leader也是存在的,那么为什么client没有获取到该partition的leader信息呢?
带着疑问,查看sarama的部分源码,发现传给kafkaclient(例如orderer里面的producer)的addrlist只是作为seedbrokers,从seedbrokers里面尝试去连接kafka server来获取metadata。
这个metadata里面包括了,注册在zk里面的所有brokers的信息, kafkaclient实际上是与这些brokers进行交互的,所以即使seedbroker填的不全,有时候也不影响kafka集群的使用。
1592847-20190530105230893-2074637193.png

流程如下图所示:
1592847-20190530105239517-1728819725.png

根据报错信息,可以发现GetMetadata返回的信息里面有ErrLeaderNotAvailable报错。
1592847-20190530105249060-1739877215.png
1592847-20190530105256173-937705352.png

由上图可知,GetMetadata向kafkabroker发送了获取metadata的请求,并且key是3。查看kafka源码,可以找到kafkaAPI如何处理key为3的请求。
1592847-20190530105658045-378451285.png
跳转到 handleTopcMetadataRequest里面:
1592847-20190530105712858-1560456550.png
跳转到getTopicMetadata:

1592847-20190530105727792-1701208281.png

跳转到createTopic:

1592847-20190530105734742-1676441787.png

如果topic不存在,GetMetadata在zk里面注册topic,然而在kafka里面把该topic标记为无leader状态。实际上,每个新建的topic都是处于LEADER_NOT_AVAILABLE的状态的,那问题应该出现在metadata的更新上面,负责管理各个partition状态的组件是controller,是不是controller哪里出了问题了?难道kafka启动日志里有报错被忽略了吗?搜索Controller相关log,发现并没有报错。

1592847-20190530105741581-1467106508.png

ZookeeperLeaderElector: 主要用于KafkController Leader选举,选举出Controller是broker1,但是后续却没有给出controller报错信息。实际上,controller作为kafka的组件,日志另有输出,报错如下,确实是访问不到broker的地址。

1592847-20190530105748092-2030858965.png

controller是随机选择一个kafka节点上启动的,为了同步副本状态,controller需要连接上每一个kafka节点,因为advertised listener地址在容器里访问不到,所以controller与各个broker的连接出现异常。进入容器查看网络连接情况,通过netstat –ae发现其中一个kafka有不正常的连接。

1592847-20190530105755290-1560021524.png

通过zkCli.sh发现,这正是controller所在的kafka,可以坐实是controller的问题了。

1592847-20190530105802986-1633564699.png

问题的原因找到了,但是为什么用kafka自带的脚本查出来的topic状态却是正常的呢?
查看该脚本调用的函数,发现改脚本调用的函数查询的数据居然来自于zk,并不是从kafka中获得。因为所有kafka连接zk并不存在问题,所以可以得出一致的topic 描述,看来使用这个脚本去查看topic状态也得慎重。

1592847-20190530105813053-1649104726.png

GetMetadata有报错,kafka-topic.sh却显示正常,终于有了解释。

Client在GetMetadata的时候,第一次创建了无主topic,在retry的时候,kafkaclient获取的metadata信息是来自于kafka的MetadataCache,因为controller的原因partitionState没有更新,所以返回的topic信息仍然有LEADER_NOT_AVAILABLE报错。

但是为什么正常情况,却没有返回这个LEADER_NOT_AVIALABLE呢?继续往下看:

1592847-20190530105820572-320044825.png

跳转到getPartitionMetadata:

1592847-20190530105828114-1706955971.png

可见查询partitionMetadata时,是通过partitionState来判断存活的brokers里面是否有leader。如果有partitionState未更新,就返回LEADER_NOT_AVIALABLE的metadata,否则就可以返回最新的metadata。
Controller是如何更新partitionState的呢?
集群所有partition状态是由PartitionStateMachine来管理的。

1592847-20190530105835298-81425411.png

controller的日志中也可看到:

1592847-20190530105846096-1890240473.png

初始化partition的时候:
1592847-20190530105900078-1754762511.png

进入addLeaderAndIsrRequestForBrokers:
1592847-20190530110033843-491886722.png

由以上代码可见,partitionState更新需要通过ControllerChannelManager。

ControllerChannelManager负责维护Controller Leader与集群中其他broker之间连接,是管理这个集群的基础。然而,ControllerChannelManager在启动时就出问题了,连不上其他的broker,因此所有的kafka metadata都没能更新。因此,controller必须连上advertised listeners,包括其自身所在的broker。

问题解决方案:

​ 如果将kafka0的KAFKA_ADVERTISED_LISTENERS设为 内网服务映射到外网的IP:9092,阿里云192.168.8.107上倒是可以通过修改host文件,把 内网服务映射到外网的IP 解析成192.168.8.108。这样,107在访问内网IP时,会连到108并通过nginx转发到192.168.9.21:9092。orderer需要连kafka集群的话,需要在k8s容器里添加host才行。

问题总结:

  1. advertised listeners不仅需要让orderer可连接,还需要让每个可能成为controller的kafkabroker容器可连才行。

  2. 这种表面可以创建topic,实际集群无法使用的情况,可以考虑查看controller的日志。

  3. kafka自带的kafka-topic脚本,描述的是zk里面的信息,并不一定于kafka里面的数据一致,需要慎重使用。

转载于:https://www.cnblogs.com/zooqkl/p/10948376.html

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐