简体   繁体   English

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

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

I am writing end-to-end tests for an application.我正在为应用程序编写端到端测试。 I start an instance of an application, a Kafka instance, and a Zookeeper (all Dockerized) and then I interact with the application API to test its functionality.我启动了一个应用程序实例、一个 Kafka 实例和一个 Zookeeper(全部 Docker 化),然后与应用程序 API 交互以测试其功能。 I need to test an event consumer's functionality in this application.我需要在此应用程序中测试事件使用者的功能。 I publish events from my tests and the application is expected to handle them.我从我的测试中发布事件,预计应用程序会处理它们。

Problem: If I run the application locally (not in Docker) and run tests that would produce events, the consumer in the application code handles events correctly.问题:如果我在本地(而不是在 Docker 中)运行应用程序并运行会产生事件的测试,应用程序代码中的使用者会正确处理事件。 In this case, the consumer and the test have bootstrapServers set to localhost:9092 .在这种情况下,消费者和测试将bootstrapServers设置为localhost:9092 But if the application is run as a Dockerized instance it doesn't see the events.但是,如果应用程序作为 Dockerized 实例运行,则它看不到事件。 In this case bootstrapServers are set to kafka:9092 in the application and localhost:9092 in the test where kafka is a Docker container name.在这种情况下, bootstrapServers在应用bootstrapServers中设置为kafka:9092 ,在测试中设置为localhost:9092 ,其中kafka是 Docker 容器名称。 The kafka container exposes its 9092 port to the host so that the same instance of Kafka can be accessed from inside a Docker container and from the host (running my tests). kafka容器将其9092端口暴露给主机,以便可以从 Docker 容器内部和主机(运行我的测试)访问相同的 Kafka 实例。

The only difference in the code is localhost vs kafka set as bootstrap servers.代码中的唯一区别是localhostkafka设置为引导服务器。 In both scenarios consumers and producers start successfully;在这两种情况下,消费者和生产者都成功启动; events are published without errors.事件发布没有错误。 It is just that in one case the consumer doesn't receive events.只是在一种情况下,消费者没有收到事件。

Question: How to make Dockerized consumers see events posted from the host machine?问题:如何让 Dockerized 消费者看到从主机发布的事件?

Note: I have a properly configured Docker network which includes the application instance, Zookeeper, and Kafka.注意:我有一个正确配置的 Docker 网络,其中包括应用程序实例、Zookeeper 和 Kafka。 They all "see" each other.他们都“看到”了对方。 The corresponding ports of kafka and zookeeper are exposed to the host. kafkazookeeper的对应端口暴露给宿主机。 Kafka ports: 0.0.0.0:9092->9092/tcp .卡夫卡端口: 0.0.0.0:9092->9092/tcp Zookeeper ports: 22/tcp, 2888/tcp, 3888/tcp, 0.0.0.0:2181->2181/tcp . Zookeeper 端口: 22/tcp, 2888/tcp, 3888/tcp, 0.0.0.0:2181->2181/tcp

I am using wurstmeister/kafka and wurstmeister/zookeeper Docker images (I cannot replace them).我正在使用wurstmeister/kafkawurstmeister/zookeeper Docker 映像(我无法替换它们)。

Any ideas/thoughts are appreciated.任何想法/想法表示赞赏。 How would you debug it?你会如何调试它?

UPDATE: The issue was with KAFKA_ADVERTISED_LISTENERS and KAFKA_LISTENERS env variables that were set to different ports for INSIDE and OUTSIDE communications.更新:问题在于KAFKA_ADVERTISED_LISTENERSKAFKA_LISTENERS变量设置为不同的端口以进行内部和外部通信。 The solution was to use a correct port in the application code when it is run inside a Docker container.解决方案是在应用程序代码中在 Docker 容器内运行时使用正确的端口。

Thes kind of issues are usually related to the way Kafka handles the broker's address.这类问题通常与 Kafka 处理代理地址的方式有关。

When you start a Kafka broker it binds itself on 0.0.0.0:9092 and register itself on Zookeeper with the address <hostname>:9092 .当您启动 Kafka 代理时,它会将自己绑定在0.0.0.0:9092并使用地址<hostname>:9092在 Zookeeper 上注册自己。 When you connect with a client, Zookeeper will be contacted to fetch the address of the specific broker.当您与客户端连接时,将联系 Zookeeper 以获取特定代理的地址。

This means that when you start a Kafka container you have a situation like the following:这意味着当您启动 Kafka 容器时,您会遇到如下情况:

  • container name: kafka容器名称:kafka
  • network name: kafkanet网络名称:kafkanet
  • hostname: kafka主机名:kafka
  • registration on zookeeper: kafka:9092在zookeeper上注册:kafka:9092

Now if you connect a client to your Kafka from a container inside the kafkanet network, the address you get back from Zookeeper is kafka:9092 which is resolvable through the kafkanet network.现在,如果您将客户端从 kafkanet 网络内的容器连接到您的 Kafka,您从 Zookeeper 返回的地址是kafka:9092 ,该地址可通过kafkanet网络解析。

However if you connect to Kafka from outside docker (ie using the localhost:9092 endpoint mapped by docker), you still get back the kafka:9092 address which is not resolvable.但是,如果您从 docker 外部连接到 Kafka(即使用由localhost:9092映射的localhost:9092端点),您仍然会返回无法解析的kafka:9092地址。

In order to address this issue you can specify the advertised.host.name and advertised.port in the broker configuration in such a way that the address is resolvable by all the client ( see documentation ).为了解决这个问题,你可以指定advertised.host.nameadvertised.port在这样的代理配置,该地址是由所有的客户端解析(见文档)。

What is usually done is to set advertised.host.name as <container-name>.<network> (in your case something like kafka.kafkanet) so that any container connected to the network is able to correctly resolve the IP of the Kafka broker.什么是通常做的是一套advertised.host.name<container-name>.<network> (在你的情况类似kafka.kafkanet),以便连接到网络的任何容器能够正确解析卡夫卡的IP经纪人。

In your case however you have a mixed network configuration, as some components live inside docker (hence able to resolve the kafkanet network) while others live outside it.但是,在您的情况下,您有一个混合网络配置,因为某些组件位于 docker 内部(因此能够解析 kafkanet 网络),而其他组件位于 docker 外部。 If it were a production system my suggestion would be to set the advertised.host.name to the DNS/IP of the host machine and always rely on docker port mapping to reach the Kafka broker.如果它是一个生产系统,我的建议是将设置advertised.host.name到主机的DNS / IP,总是依靠搬运工端口映射,以达到卡夫卡经纪人。

From my understanding however you only need this setup to test things out, so the easiest thing would be to "trick" the system living outside docker.然而,根据我的理解,你只需要这个设置来测试事情,所以最简单的事情是“欺骗”生活在 docker 之外的系统。 Using the naming specified above, this means simply to add to your /etc/hosts (or windows equivalent) the line 127.0.0.1 kafka.kafkanet .使用上面指定的命名,这意味着只需将127.0.0.1 kafka.kafkanet行添加到/etc/hosts (或 Windows 等效项)。

This way when your client living outside docker connects to Kafka the following should happen:这样,当您居住在 docker 之外的客户端连接到 Kafka 时,应该会发生以下情况:

  1. client -> Kafka via localhost:9092客户端 -> Kafka 通过 localhost:9092
  2. kafka queries Zookeeper and return the host kafka.kafkanet kafka 查询 Zookeeper 并返回主机kafka.kafkanet
  3. client resolves kafka.kafkanet to 127.0.0.1客户端将kafka.kafkanet解析为 127.0.0.1
  4. client -> Kafka via 127.0.0.1:9092客户端 -> Kafka 通过 127.0.0.1:9092

EDIT编辑

As pointed out in a comment, newer Kafka version now use the concept of listeners and advertised.listeners which are used in place of host.name and advertised.host.name (which are deprecated and only used in case the the above ones are not specified).正如在评论中指出,新版本的卡夫卡现在使用的概念, listenersadvertised.listeners这是用来代替host.nameadvertised.host.name被弃用,只有在情况下使用(在上面的人不指定的)。 The general idea is the same however:但是总体思路是一样的:

  • host.name : specifies the host to which the Kafka broker should bind itself to (works in conjunction with port host.name :指定 Kafka 代理应该绑定到的主机(与port一起工作
  • listeners : specifies all the endpoints to which the Kafka broker should bind (for instance PLAINTEXT://0.0.0.0:9092,SSL://0.0.0.0:9091 ) listeners :指定 Kafka 代理应绑定的所有端点(例如PLAINTEXT://0.0.0.0:9092,SSL://0.0.0.0:9091
  • advertised.host.name : specifies how the broker is advertised to client (ie which address client should use to connect to it) advertised.host.name :指定代理如何通告给客户端(即客户端应该使用哪个地址来连接到它)
  • avertised.listeners : specifies all the advertised endpoints (for instance PLAINTEXT://kafka.example.com:9092,SSL://kafka.example.com:9091 ) avertised.listeners :指定所有广告端点(例如PLAINTEXT://kafka.example.com:9092,SSL://kafka.example.com:9091

In both cases for client to be able to successfully communicate with Kafka they need to be able to resolve and connect to the advertised hostname and port.在这两种情况下,为了让客户端能够成功地与 Kafka 通信,他们需要能够解析并连接到advertised主机名和端口。

In both cases if not specified they are automatically derived by the broker using the hostname of the machine the broker is running on.在这两种情况下,如果未指定,它们将由代理使用运行代理的机器的主机名自动派生。

You kept referencing 8092 .你一直在引用8092 Was that intentional?那是故意的吗? Kafka runs on 9092 . Kafka 在9092运行。 Easiest test is to download the same version of Kafka and manually run its kafka-console-consumer and kafka-console-producer scripts to see if you can pub-sub from your host machine.最简单的测试是下载相同版本的 Kafka 并手动运行其kafka-console-consumerkafka-console-producer脚本,看看是否可以从主机上发布订阅。

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

You could create a docker network for your containers and then containers will be able to resolve each other hostnames and communicate.您可以为您的容器创建一个docker 网络,然后容器将能够解析彼此的主机名并进行通信。

Note: this is usable with docker-compose as well with standalone containers注意:这可用于 docker-compose 以及独立容器

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

相关问题 从在 Docker 中运行的 Java 生产者访问安装在主机上的 Kafka - Accessing Kafka installed on host from Java producer running in Docker Kafka 和 zookeeper docker 容器在主机上与我的 api 对话 - Kafka and zookeeper docker containers talking to my api on the host machine Kafka 容器无法启动,因为 docker 警告:使用主机网络模式时会丢弃已发布的端口 - Kafka container don't start because the docker Warning : Published ports are discarded when using host network mode 无法从 Java 连接到在 Docker 中运行的 Kafka - Unable to Connect from Java to Kafka running in Docker 如何在虚拟机中运行的主机中访问Web应用程序 - How to access web application in host running in virtual machine 从消费消息中获取 Kafka 主题的名称 - Get the name of the Kafka topic from a consumed message 在 Docker-compose 中使用 Kafka 运行 Spring Boot 应用程序 - Running Spring Boot application with Kafka in Docker-compose 连接到运行在 Docker 的 Kafka - Connect to Kafka running in Docker 如何从 Linux 命令行或 docker 容器中检查 spring 启动 java 应用程序消耗了哪些值 - How to check what values are consumed by a spring boot java application from the Linux command line or in the docker container 无法从Docker中运行的服务向kafka生成消息 - Cannot produce message to kafka from service running in docker
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM