繁体   English   中英

Kafka:从宿主机发布的事件不会被运行在 Docker 中的应用程序消费

[英]Kafka: events published from the host machine are not consumed by the application running in Docker

我正在为应用程序编写端到端测试。 我启动了一个应用程序实例、一个 Kafka 实例和一个 Zookeeper(全部 Docker 化),然后与应用程序 API 交互以测试其功能。 我需要在此应用程序中测试事件使用者的功能。 我从我的测试中发布事件,预计应用程序会处理它们。

问题:如果我在本地(而不是在 Docker 中)运行应用程序并运行会产生事件的测试,应用程序代码中的使用者会正确处理事件。 在这种情况下,消费者和测试将bootstrapServers设置为localhost:9092 但是,如果应用程序作为 Dockerized 实例运行,则它看不到事件。 在这种情况下, bootstrapServers在应用bootstrapServers中设置为kafka:9092 ,在测试中设置为localhost:9092 ,其中kafka是 Docker 容器名称。 kafka容器将其9092端口暴露给主机,以便可以从 Docker 容器内部和主机(运行我的测试)访问相同的 Kafka 实例。

代码中的唯一区别是localhostkafka设置为引导服务器。 在这两种情况下,消费者和生产者都成功启动; 事件发布没有错误。 只是在一种情况下,消费者没有收到事件。

问题:如何让 Dockerized 消费者看到从主机发布的事件?

注意:我有一个正确配置的 Docker 网络,其中包括应用程序实例、Zookeeper 和 Kafka。 他们都“看到”了对方。 kafkazookeeper的对应端口暴露给宿主机。 卡夫卡端口: 0.0.0.0:9092->9092/tcp Zookeeper 端口: 22/tcp, 2888/tcp, 3888/tcp, 0.0.0.0:2181->2181/tcp

我正在使用wurstmeister/kafkawurstmeister/zookeeper Docker 映像(我无法替换它们)。

任何想法/想法表示赞赏。 你会如何调试它?

更新:问题在于KAFKA_ADVERTISED_LISTENERSKAFKA_LISTENERS变量设置为不同的端口以进行内部和外部通信。 解决方案是在应用程序代码中在 Docker 容器内运行时使用正确的端口。

这类问题通常与 Kafka 处理代理地址的方式有关。

当您启动 Kafka 代理时,它会将自己绑定在0.0.0.0:9092并使用地址<hostname>:9092在 Zookeeper 上注册自己。 当您与客户端连接时,将联系 Zookeeper 以获取特定代理的地址。

这意味着当您启动 Kafka 容器时,您会遇到如下情况:

  • 容器名称:kafka
  • 网络名称:kafkanet
  • 主机名:kafka
  • 在zookeeper上注册:kafka:9092

现在,如果您将客户端从 kafkanet 网络内的容器连接到您的 Kafka,您从 Zookeeper 返回的地址是kafka:9092 ,该地址可通过kafkanet网络解析。

但是,如果您从 docker 外部连接到 Kafka(即使用由localhost:9092映射的localhost:9092端点),您仍然会返回无法解析的kafka:9092地址。

为了解决这个问题,你可以指定advertised.host.nameadvertised.port在这样的代理配置,该地址是由所有的客户端解析(见文档)。

什么是通常做的是一套advertised.host.name<container-name>.<network> (在你的情况类似kafka.kafkanet),以便连接到网络的任何容器能够正确解析卡夫卡的IP经纪人。

但是,在您的情况下,您有一个混合网络配置,因为某些组件位于 docker 内部(因此能够解析 kafkanet 网络),而其他组件位于 docker 外部。 如果它是一个生产系统,我的建议是将设置advertised.host.name到主机的DNS / IP,总是依靠搬运工端口映射,以达到卡夫卡经纪人。

然而,根据我的理解,你只需要这个设置来测试事情,所以最简单的事情是“欺骗”生活在 docker 之外的系统。 使用上面指定的命名,这意味着只需将127.0.0.1 kafka.kafkanet行添加到/etc/hosts (或 Windows 等效项)。

这样,当您居住在 docker 之外的客户端连接到 Kafka 时,应该会发生以下情况:

  1. 客户端 -> Kafka 通过 localhost:9092
  2. kafka 查询 Zookeeper 并返回主机kafka.kafkanet
  3. 客户端将kafka.kafkanet解析为 127.0.0.1
  4. 客户端 -> Kafka 通过 127.0.0.1:9092

编辑

正如在评论中指出,新版本的卡夫卡现在使用的概念, listenersadvertised.listeners这是用来代替host.nameadvertised.host.name被弃用,只有在情况下使用(在上面的人不指定的)。 但是总体思路是一样的:

  • host.name :指定 Kafka 代理应该绑定到的主机(与port一起工作
  • listeners :指定 Kafka 代理应绑定的所有端点(例如PLAINTEXT://0.0.0.0:9092,SSL://0.0.0.0:9091
  • advertised.host.name :指定代理如何通告给客户端(即客户端应该使用哪个地址来连接到它)
  • avertised.listeners :指定所有广告端点(例如PLAINTEXT://kafka.example.com:9092,SSL://kafka.example.com:9091

在这两种情况下,为了让客户端能够成功地与 Kafka 通信,他们需要能够解析并连接到advertised主机名和端口。

在这两种情况下,如果未指定,它们将由代理使用运行代理的机器的主机名自动派生。

你一直在引用8092 那是故意的吗? Kafka 在9092运行。 最简单的测试是下载相同版本的 Kafka 并手动运行其kafka-console-consumerkafka-console-producer脚本,看看是否可以从主机上发布订阅。

您是否在 dockerized 应用程序中尝试过“host.docker.internal”?

您可以为您的容器创建一个docker 网络,然后容器将能够解析彼此的主机名并进行通信。

注意:这可用于 docker-compose 以及独立容器

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM