簡體   English   中英

在 dockerized 環境中無法從 Flask 連接到 Kafka

[英]Cannot connect to Kafka from Flask in a dockerized environement

我正在嘗試構建一個以 Kafka 作為界面的 Flask 應用程序。 我使用了 Python 連接器kafka-python和用於 Kafka 的 Docker 映像spotify/kafkaproxy

下面是 docker-compose 文件。

version: '3.3'
services:
  kafka:
    image: spotify/kafkaproxy
    container_name: kafka_dev
    ports:
      - '9092:9092'
      - '2181:2181'
    environment:
      - ADVERTISED_HOST=0.0.0.0
      - ADVERTISED_PORT=9092
      - CONSUMER_THREADS=1
      - TOPICS=PROFILE_CREATED,IMG_RATED
      - ZK_CONNECT=kafka7zookeeper:2181/root/path
  flaskapp:
    build: ./flask-app
    container_name: flask_dev
    ports:
      - '9000:5000'
    volumes:
      - ./flask-app:/app
    depends_on:
      - kafka

下面是我用來連接到 kafka 的 Python 片段。 在這里,我使用 Kafka 容器的別名kafka進行連接,因為 Docker 會負責將別名映射到它的 IP 地址。

from kafka import KafkaConsumer, KafkaProducer

TOPICS = ['PROFILE_CREATED', 'IMG_RATED']
BOOTSTRAP_SERVERS = ['kafka:9092']

consumer = KafkaConsumer(TOPICS, bootstrap_servers=BOOTSTRAP_SERVERS)

我收到NoBrokersAvailable錯誤。 由此,我可以理解 Flask 應用程序找不到 Kafka 服務器。

Traceback (most recent call last):
  File "./app.py", line 11, in <module>
    consumer = KafkaConsumer("PROFILE_CREATED", bootstrap_servers=BOOTSTRAP_SERVERS)
  File "/usr/local/lib/python3.6/site-packages/kafka/consumer/group.py", line 340, in __init__
    self._client = KafkaClient(metrics=self._metrics, **self.config)
  File "/usr/local/lib/python3.6/site-packages/kafka/client_async.py", line 219, in __init__
    self.config['api_version'] = self.check_version(timeout=check_timeout)
  File "/usr/local/lib/python3.6/site-packages/kafka/client_async.py", line 819, in check_version
    raise Errors.NoBrokersAvailable()
kafka.errors.NoBrokersAvailable: NoBrokersAvailable

其他觀察:

  1. 我能夠從 Flask 容器運行ping kafka並從 Kafka 容器獲取數據包。
  2. 當我在本地運行 Flask 應用程序時,嘗試通過設置BOOTSTRAP_SERVERS = ['localhost:9092']連接到 Kafka 容器,它工作正常。

更新

正如 cricket_007 所提到的,鑒於您正在使用下面提供的 docker-compose,您應該使用kafka:29092從另一個容器連接到 Kafka。 所以你的代碼看起來像這樣:

from kafka import KafkaConsumer, KafkaProducer

TOPICS = ['PROFILE_CREATED', 'IMG_RATED']
BOOTSTRAP_SERVERS = ['kafka:29092']

consumer = KafkaConsumer(TOPICS, bootstrap_servers=BOOTSTRAP_SERVERS)

結束更新

我建議您使用Confluent Inc的 Kafka 圖像,它們有各種使用 docker-compose 的示例設置,可以隨時使用,並且它們總是在更新它們。

試試這個:

---
version: '2'
services:
zookeeper:
    image: confluentinc/cp-zookeeper:latest
    environment:
    ZOOKEEPER_CLIENT_PORT: 2181
    ZOOKEEPER_TICK_TIME: 2000

kafka:
    image: confluentinc/cp-kafka:latest
    depends_on:
    - zookeeper
    ports:
    - 9092:9092
    environment:
    KAFKA_BROKER_ID: 1
    KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
    KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
    KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
    KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
    KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

flaskapp:
    build: ./flask-app
    container_name: flask_dev
    ports:
    - '9000:5000'
    volumes:
    - ./flask-app:/app

我使用了這個docker-compose.yml並在頂部添加了您的服務請注意:

這里使用的配置暴露端口9092,用於向代理即那些來自搬運工網絡之外的外部連接。 這可能來自運行 docker 的主機,或者如果您有更復雜的設置,則可能來自更遠的地方。 如果后者為真,則需要將 KAFKA_ADVERTISED_LISTENERS 中的值“localhost”更改為可從這些遠程客戶端解析到 docker 主機的值

確保您查看其他示例,這可能對您有用,尤其是在遷移到生產環境時: https : //github.com/confluentinc/cp-docker-images/tree/5.0.1-post/examples

也值得檢查:

看來您需要指定 api_version 以避免此錯誤。 有關更多詳細信息,請查看此處

該庫的 1.3.5 版本(pypy 上的最新版本)僅列出了 0.8.0 到 0.10.1 的某些 API 版本。 因此,除非您明確指定 api_version 為 (0, 10, 1),否則客戶端庫嘗試發現版本將導致 NoBrokersAvailable 錯誤。

producer = KafkaProducer(
    bootstrap_servers=URL,
    client_id=CLIENT_ID,
    value_serializer=JsonSerializer.serialize,
    api_version=(0, 10, 1)
)

這應該可以工作,有趣的是,設置 api_version 是根據以下內容意外修復問題:

當您設置 api_version 時,客戶端將不會嘗試探查代理以獲取版本信息。 因此,失敗的是探測操作。 版本探測連接和一般連接之間的一大區別是,前者僅嘗試在每個連接(每個代理)上的單個接口上進行連接,而后者——一般操作——將不斷循環所有接口,直到連接成功。 #1411 通過切換版本探測邏輯來嘗試在所有找到的接口上建立連接來解決此問題。

實際問題描述為here

我設法在所有服務之間使用名為stream_net網絡啟動並運行。

# for local development
version: "3.7"
services:

  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    networks:
      - stream_net

  kafka:
    image: confluentinc/cp-kafka:latest
    depends_on:
      - zookeeper
    ports:
      - 9092:9092
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
    networks:
      - stream_net

  flaskapp:
    build: ./flask-app
    container_name: flask_dev
    ports:
      - "9000:5000"
    volumes:
      - ./flask-app:/app
    networks:
      - stream_net
    depends_on:
      - kafka

networks:
  stream_net:
  • 來自localhost:9092上容器外部的連接
  • kafka:29092上的網絡連接kafka:29092

當然,把已經在一個網絡中運行的所有容器放在一個網絡中是很奇怪的。 但是通過這種方式,容器可以通過它們的實際名稱來命名。 也許有人可以確切地解釋這是如何工作的,或者它可以幫助其他人理解問題的核心並正確解決它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM