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裏面的數據一致,須要慎重使用。

相關文章
相關標籤/搜索