wsl 中 docker-compose 搭建 kafka 叢集出現的外部訪問錯誤

Assassin007發表於2023-03-25

在 wsl 中用 docker-compose 搭建了一臺 zookeeper + 三臺 broker 的 kafka 叢集,使用的映象是 bitnami/kafka,在按照映象文件執行容器後,發現執行在宿主機裡的客戶端程式無法正確的推送/消費訊息,研究後發現映象文件只適用於客戶端程式和 kafka 叢集同屬於一個 docker 網段,外部訪問還需要一些額外的配置,過程中出現過以下幾個主要的錯誤:

  • dial tcp: lookup 333be5d4e335 on 172.30.96.1:53: no such host
  • kafka: client has run out of available brokers to talk to: dial tcp 127.0.0.1:19092: connect: connection refused
  • [Controller id=1, targetBrokerId=3] Client requested connection close from node 3 (org.apache.kafka.clients.NetworkClient)

這裡先貼一個可以用的 docker-compose.yml 配置,後面對其中的關鍵配置做一個解釋,最後再解釋出現上面錯誤的原因,檔案最後的 kafka-ui 是一個視覺化管理介面,可以不要

version: "3"

services:
  zookeeper:
    container_name: kafka_zookeeper
    image: bitnami/zookeeper
    user: root
    ports:
      - "2181:2181"
    environment:
      - ALLOW_ANONYMOUS_LOGIN=yes
    volumes:
      - ./zookeeper:/bitnami/zookeeper
  broker1:
    container_name: kafka_broker1
    image: bitnami/kafka
    user: root
    ports:
      - "19092:9092"
    environment:
      - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
      - ALLOW_PLAINTEXT_LISTENER=yes
      - KAFKA_BROKER_ID=1
      - KAFKA_LISTENERS=INTERNAL://0.0.0.0:9000,EXTERNAL://0.0.0.0:9092
      - KAFKA_ADVERTISED_LISTENERS=INTERNAL://broker1:9000,EXTERNAL://localhost:19092
      - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
      - KAFKA_INTER_BROKER_LISTENER_NAME=INTERNAL
    volumes:
      - ./broker1:/bitnami/kafka
    depends_on:
      - zookeeper
  broker2:
    container_name: kafka_broker2
    image: bitnami/kafka
    user: root
    ports:
      - "29092:9092"
    environment:
      - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
      - ALLOW_PLAINTEXT_LISTENER=yes
      - KAFKA_BROKER_ID=2
      - KAFKA_LISTENERS=INTERNAL://0.0.0.0:9000,EXTERNAL://0.0.0.0:9092
      - KAFKA_ADVERTISED_LISTENERS=INTERNAL://broker2:9000,EXTERNAL://localhost:29092
      - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
      - KAFKA_INTER_BROKER_LISTENER_NAME=INTERNAL
    volumes:
      - ./broker2:/bitnami/kafka
    depends_on:
      - broker1
  broker3:
    container_name: kafka_broker3
    image: bitnami/kafka
    user: root
    ports:
      - "39092:9092"
    environment:
      - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
      - ALLOW_PLAINTEXT_LISTENER=yes
      - KAFKA_BROKER_ID=3
      - KAFKA_LISTENERS=INTERNAL://0.0.0.0:9000,EXTERNAL://0.0.0.0:9092
      - KAFKA_ADVERTISED_LISTENERS=INTERNAL://broker3:9000,EXTERNAL://localhost:39092
      - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
      - KAFKA_INTER_BROKER_LISTENER_NAME=INTERNAL
    volumes:
      - ./broker3:/bitnami/kafka
    depends_on:
      - broker2
  kafka-ui:
    container_name: kafka-ui
    image: provectuslabs/kafka-ui
    ports:
      - "8080:8080"
    restart: always
    environment:
      - KAFKA_CLUSTERS_0_NAME=broker1
      - KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=broker1:9000
      - KAFKA_CLUSTERS_1_NAME=broker2
      - KAFKA_CLUSTERS_1_BOOTSTRAPSERVERS=broker2:9000
      - KAFKA_CLUSTERS_2_NAME=broker3
      - KAFKA_CLUSTERS_2_BOOTSTRAPSERVERS=broker3:9000
    depends_on:
      - broker3

其中幾個容易搞錯的關鍵配置如下:

ports:
  - "19092:9092"
environment:
  - KAFKA_LISTENERS=INTERNAL://0.0.0.0:9000,EXTERNAL://0.0.0.0:9092
  - KAFKA_ADVERTISED_LISTENERS=INTERNAL://broker1:9000,EXTERNAL://localhost:19092

引數 KAFKA_LISTENERS 和 KAFKA_ADVERTISED_LISTENERS 的作用:

  • KAFKA_LISTENERS 代表 broker 的監聽地址,kafka客戶端首先需要與這個地址建立連線,完成必要的認證工作
  • KAFKA_ADVERTISED_LISTENERS 代表 broker 的資料傳輸地址,這裡配置的地址會註冊到 zookeeper 中,在客戶端完成身份認證後會從 zk 原封不動地獲得這裡配置的 ip+port 用於訊息推送/消費

在上面的配置中,KAFKA_ADVERTISED_LISTENERS 的 EXTERNAL 配置了 localhost:19092,這是因為我的客戶端程式執行在 wsl 中,而 19092 埠已經對映到了容器的 9092 埠上所以可以正確訪問,如果 kafka 叢集和客戶端程式執行在兩個不同的伺服器上,這裡應該配置 kfaka 叢集所在的主機 ip,只需要記住這一串地址的 ip+port 部分是原封不動的傳給客戶端的,想想客戶端程式所在的機器能不能解析它吧

另外,關於 KAFKA_LISTENERS 中 port 的配置與上面 ports 屬性中的埠對映的關係是:先有埠監聽後有埠對映,這個地方沒理解清楚的話就很容易對這兩個配置項感到迷惑,例如上面配置了 9000 和 9092 兩個監聽埠,然後將 9092 對映到了宿主機的 19092,9000 作為未公開的埠只有同屬一個 docker 網路的機器才能訪問

一開始出現的幾個主要錯誤也都是由這幾個配置引起:

  • dial tcp: lookup 333be5d4e335 on 172.30.96.1:53: no such host

未配置 KAFKA_LISTENERS 的情況下預設是該 broker 容器的主機名+9092,未配置 KAFKA_ADVERTISED_LISTENERS 的情況下該值等於 KAFKA_LISTENERS,這種情況下宿主機的程式建立連線後拿到了一個未知的主機名 333be5d4e335 傳送訊息,當然行不通(宿主機無法將該主機名轉換成 ip 訪問)

  • kafka: client has run out of available brokers to talk to: dial tcp 127.0.0.1:19092: connect: connection refused

埠配置沒有理解清楚,KAFKA_LISTENERS 中對外的監聽埠必須是被對映出去的 9092 本身,否則宿主機無法訪問

  • [Controller id=1, targetBrokerId=3] Client requested connection close from node 3 (org.apache.kafka.clients.NetworkClient)

埠配置沒有理解清楚,brokers 之間的通訊是內部通訊,內部監聽埠可以不公開對映出去,但是流程是一樣的

另外在配置項變更後,最好刪除容器,並刪除各個目錄裡面的 data 目錄裡面的檔案再重新建立容器,不確定是哪一個配置變更會出現以下錯誤:

  • org.apache.zookeeper.KeeperException$NodeExistsException: KeeperErrorCode = NodeExists

相關文章