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

问题描述

我正在为应用程序编写端到端测试。我启动了一个应用程序实例,一个Kafka实例和一个Zookeeper(均已Docker化),然后与应用程序API交互以测试其功能。我需要在此应用程序中测试事件使用者的功能。我发布了测试中的事件,应用程序有望处理这些事件。

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

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

问题::如何使Docker化的使用者看到从主机发布的事件?

注意:我有一个配置正确的Docker网络,其中包括应用程序实例,Zookeeper和Kafka。他们都互相“见”。 kafkazookeeper的相应端口暴露给主机。 Kafka端口: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 env变量被设置为用于INSIDE和OUTSIDE通信的不同端口。解决方案是在Docker容器中运行应用程序代码时使用正确的端口。

解决方法

这类问题通常与卡夫卡处理经纪人地址的方式有关。

启动Kafka经纪人时,它会在0.0.0.0:9092上进行绑定,并使用地址<hostname>:9092在Zookeeper中进行注册。与客户建立联系后,将与Zookeeper联系以获取特定经纪人的地址。

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

  • 容器名称:kafka
  • 网络名称:kafkanet
  • 主机名:kafka
  • 动物园管理员注册:kafka:9092

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

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

为了解决此问题,您可以在代理配置中指定advertised.host.nameadvertised.port,以便所有客户端(see documentation)都可以解析该地址。 / p>

通常要做的是将advertised.host.name设置为<container-name>.<network>(在您的情况下,例如kafka.kafkanet),以便连接到网络的任何容器都能够正确解析Kafka代理的IP。

但是,在您的情况下,您具有混合的网络配置,因为某些组件位于docker内部(因此能够解析kafkanet网络),而其他组件位于docker内部。如果是生产系统,我的建议是将advertised.host.name设置为主机的DNS / IP,并始终依靠docker端口映射来到达Kafka代理。

但是,根据我的理解,您仅需要此设置即可进行测试,因此最简单的方法是“欺骗”生活在Docker外部的系统。使用上面指定的命名,这意味着仅将/etc/hosts行添加到您的127.0.0.1 kafka.kafkanet(或等效于Windows的行)中。

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

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

编辑

正如评论中指出的那样,较新的Kafka版本现在使用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 network,然后容器将能够相互解析主机名并进行通信。

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