ネットワーク全体KAFKAホストの展開は、ソリューションをブロック
問題の背景:
カフカのクラスタの展開は時々、カフカクラスタ自体は問題ありませんが、発注者が常にエラーである理由であるだけでなく、利用できる必要がありますが、ノードを接続することができます発注する必要があります。カフカピールプログラム、BAAS展開のためのネットワークと内部ネットワークを介してアリクラウドの実現可能性を試験するために。
展開環境は以下の通り:
K8s部署在阿里云环境上,
192.168.8.108可连外网,作为master;
192.168.8.107不能连外网,作为slave;
Kafka集群部署在内网,
192.168.9.21等机器上,都可以连外网。
発注者は、スレーブノードのマシン上で再生されますので、このマシンは、それがインターネットに直接接続することができない、107です。図に示すような、nginxの発注者カフカクラスタを確保するために転送することによって、接続することができます。
だから、アドバタイズされたリスナーの構成が特に重要であり、結局のところ、これは飼育係のカフカのノードクラスタに保存されているブローカーメタ情報、カフカにアクセスするために、これらのアドレスを通じて最終的に発注者です。
KAFKA_ADVERTISED_LISTENERSアドレスkafka0が192.168.9.21:9092を設定した場合は、通常のクラスタの作成が、しかし、発注者はカフカにさえないネットワークアドレスに接続することはできません。したがって、KAFKA_ADVERTISED_LISTENERS kafka0アドレス選択は、ネットワークプロキシIPに転送次いでエージェント108に配置されたnginxの、192.168.8.108:9092に設定されます(ネットワークが外部のネットワークに接続することができる、IPを介して、リバースプロキシ)9092、それはノードkafka0に接続できるようにします。
Setupbaasはまだ異常な発注・エラー・カフカクラスタを見つけようとしますが、カフカはいかなる例外なくログを開始します。
セットアッププロセスは非常に長いですが、また、非常に簡単にデバッグするkafkaclientで、環境をクリーンアップします。サラマーを使用して言語クライアントの生地を行く、単にそれを変更カフカクラスタが利用可能かどうかをテストするために、シンプルなクライアントを再生することができプロデューサーの内側fabric_test。
カフカは、次のように見つかった情報を与え、プロデューサにデータを書き込みます。
エラーの説明、そして今、このpartitonいいえリーダーは、我々は各partitonは、クライアントの読み取りおよび書き込みを担当する指導者を、持っていますカフカ知っています。
为了确认测试用的partition到底有没有leader,通过kafka内部的kafka-topic.sh来查看详细信息,结果如下图所示:
结果发现,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才行。
问题总结:
advertised listeners不仅需要让orderer可连接,还需要让每个可能成为controller的kafkabroker容器可连才行。
这种表面可以创建topic,实际集群无法使用的情况,可以考虑查看controller的日志。
kafka自带的kafka-topic脚本,描述的是zk里面的信息,并不一定于kafka里面的数据一致,需要慎重使用。