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

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

问题背景:

Kafka的部署不只须要集群可用,同时须要对orderer节点可连,这就是为何有的时候,kafka集群自己没问题,可是orderer却老是报错。为了试验kafka剥离方案的可行性,跨阿里云网络和内网进行BAAS部署。nginx

部署环境以下:网络

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

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

由于orderer节点会起在slave机器上,也就是107这台机器,它没法直连外网。所以,经过nginx转发来保证orderer能够连上kafka集群,以下图所示。
函数

那么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的启动日志没有任何异常。3d

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

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

报错说明,如今这个partiton没有leader,咱们知道kafka每一个partiton都会有一个leader,负责client的读写。日志

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

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

流程以下图所示:

根据报错信息,能够发现GetMetadata返回的信息里面有ErrLeaderNotAvailable报错。

由上图可知,GetMetadata向kafkabroker发送了获取metadata的请求,而且key是3。查看kafka源码,能够找到kafkaAPI如何处理key为3的请求。

跳转到 handleTopcMetadataRequest里面:

跳转到getTopicMetadata:

跳转到createTopic:

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

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

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

经过zkCli.sh发现,这正是controller所在的kafka,能够坐实是controller的问题了。

问题的缘由找到了,可是为何用kafka自带的脚本查出来的topic状态倒是正常的呢?
查看该脚本调用的函数,发现改脚本调用的函数查询的数据竟然来自于zk,并非从kafka中得到。由于全部kafka链接zk并不存在问题,因此能够得出一致的topic 描述,看来使用这个脚本去查看topic状态也得慎重。

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

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

可是为何正常状况,却没有返回这个LEADER_NOT_AVIALABLE呢?继续往下看:

跳转到getPartitionMetadata:

可见查询partitionMetadata时,是经过partitionState来判断存活的brokers里面是否有leader。若是有partitionState未更新,就返回LEADER_NOT_AVIALABLE的metadata,不然就能够返回最新的metadata。
Controller是如何更新partitionState的呢?
集群全部partition状态是由PartitionStateMachine来管理的。

controller的日志中也可看到:

初始化partition的时候:

进入addLeaderAndIsrRequestForBrokers:

由以上代码可见,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里面的数据一致,须要慎重使用。

相关文章
相关标签/搜索