KAFKA跨主機部署網絡不通解決思路


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集群,如下圖所示。

那么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寫入數據,發現報錯信息如下:

報錯說明,現在這個partiton沒有leader,我們知道kafka每個partiton都會有一個leader,負責client的讀寫。

為了確認測試用的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才行。

問題總結:

  1. advertised listeners不僅需要讓orderer可連接,還需要讓每個可能成為controller的kafkabroker容器可連才行。

  2. 這種表面可以創建topic,實際集群無法使用的情況,可以考慮查看controller的日志。

  3. kafka自帶的kafka-topic腳本,描述的是zk里面的信息,並不一定於kafka里面的數據一致,需要慎重使用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM