连接到在 Docker 中运行的 Kafka

新手上路,请多包涵

我在本地机器上设置了一个单节点 Kafka Docker 容器,就像 Confluent 文档 中描述的那样(步骤 2-3)。

此外,我还暴露了 Zookeeper 的 2181 端口和 Kafka 的 9092 端口,这样我就可以从本地机器上运行的客户端连接到它们:

 $ docker run -d \
    -p 2181:2181 \
    --net=confluent \
    --name=zookeeper \
    -e ZOOKEEPER_CLIENT_PORT=2181 \
    confluentinc/cp-zookeeper:4.1.0

$ docker run -d \
    --net=confluent \
    --name=kafka \
    -p 9092:9092 \
    -e KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 \
    -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092 \
    -e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 \
    confluentinc/cp-kafka:4.1.0

问题: 当我尝试从主机连接到 Kafka 时,连接失败,因为它 can't resolve address: kafka:9092

这是我的 Java 代码:

 Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("client.id", "KafkaExampleProducer");
props.put("key.serializer", LongSerializer.class.getName());
props.put("value.serializer", StringSerializer.class.getName());
KafkaProducer<Long, String> producer = new KafkaProducer<>(props);
ProducerRecord<Long, String> record = new ProducerRecord<>("foo", 1L, "Test 1");
producer.send(record).get();
producer.flush();

例外:

 java.io.IOException: Can't resolve address: kafka:9092
    at org.apache.kafka.common.network.Selector.doConnect(Selector.java:235) ~[kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.common.network.Selector.connect(Selector.java:214) ~[kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.clients.NetworkClient.initiateConnect(NetworkClient.java:864) [kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.clients.NetworkClient.ready(NetworkClient.java:265) [kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.clients.producer.internals.Sender.sendProducerData(Sender.java:266) [kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.clients.producer.internals.Sender.run(Sender.java:238) [kafka-clients-2.0.0.jar:na]
    at org.apache.kafka.clients.producer.internals.Sender.run(Sender.java:176) [kafka-clients-2.0.0.jar:na]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_144]
Caused by: java.nio.channels.UnresolvedAddressException: null
    at sun.nio.ch.Net.checkAddress(Net.java:101) ~[na:1.8.0_144]
    at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:622) ~[na:1.8.0_144]
    at org.apache.kafka.common.network.Selector.doConnect(Selector.java:233) ~[kafka-clients-2.0.0.jar:na]
    ... 7 common frames omitted

问题: 如何连接到运行在Docker中的Kafka?我的代码是从主机而不是 Docker 运行的。

注意:我知道理论上我可以尝试使用 DNS 设置和 /etc/hosts 但这是一种解决方法 - 它不应该那样。

这里 也有类似的问题,但它是基于 ches/kafka 图像。我使用 confluentinc 基于图像,这是不一样的。

原文由 Sasha Shpota 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 813
2 个回答

免责声明

tl;dr - 从容器到主机的简单端口转发将不起作用,并且不应修改任何主机文件。您要连接到哪个确切的 IP/主机名 + 端口?确保该值在代理上设置为 advertised.listeners 。确保作为 bootstrap.servers 的一部分列出的地址和服务器实际上是可解析的( ping IP/主机名,使用 netcat 端口…)-

要验证主机上的端口是否正确映射,请确保 docker ps 显示 kafka 容器是从 0.0.0.0:<host_port> -> <advertised_listener_port>/tcp 映射的。如果尝试从 Docker 网络外部运行客户端,则端口必须匹配。

如果您对使用简单的端口映射感兴趣,请尝试寻找支持 Kafka“KRaft”模式的容器(例如 bitnami/kafka 图像)


以下答案使用 confluentinc images 来解决所提出的问题,而 不是 wurstmeister/kafka 。更具体地说,尽管后者是最受欢迎的 Kafka docker 镜像之一,但维护得不好。

以下部分尝试汇总使用另一个图像所需的所有详细信息。对于其他常用的 Kafka 图像, 它们都是在容器中运行的相同 Apache Kafka

你只是依赖于 _它是如何配置的_。 哪些变量 使它如此。

wurstmeister/kafka

请参阅他们关于 侦听器配置 的 README 部分,还 请阅读他们的 Connectivity wiki

bitnami/kafka

如果你想要一个小容器,试试这些。这些图像比 Confluent 的图像小得多,并且比 wurstmeister 维护得更好。 请参阅他们的自述文件 以了解侦听器配置。

debezium/kafka

此处提到了有关它的 文档。

注意:已弃用公布的主机和端口设置。广告 听众 涵盖两者。与 Confluent 容器类似,Debezium 可以使用 KAFKA_ 前缀代理设置来更新其属性。

其他

spotify/kafka 已弃用且已过时。

fast-data-devlensesio/box 非常适合一体化解决方案,但如果你 想要 Kafka,它们就会变得臃肿

你自己的 Dockerfile - 为什么?这些其他人有什么不完整的吗?从拉取请求开始,而不是从头开始。

有关补充阅读、 功能齐全的 docker-compose 和网络图,请参阅 @rmoff 的博客

回答

Confluent 快速入门 (Docker) 文档 假设所有生产和消费请求都在 Docker 网络中。

您可以通过在其自己的容器中运行 Kafka 客户端代码来解决连接到 kafka:9092 的问题,因为它使用 Docker 网桥,但否则您将需要添加更多环境变量以在外部公开容器,同时仍然让它在 Docker 网络中工作。

首先添加 PLAINTEXT_HOST:PLAINTEXT 的协议映射,将监听器协议映射到Kafka协议

密钥: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP

值: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT

然后在不同的端口上设置两个通告的监听器。 ( kafka 这里指的是docker容器名称;它也可能被命名为 broker ,所以仔细检查你的服务+主机名)。

密钥: KAFKA_ADVERTISED_LISTENERS

值: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092

请注意,此处的协议与上面协议映射设置的左侧值匹配

运行容器时,为主机端口映射添加 -p 29092:29092 ,并发布 PLAINTEXT_HOST 监听器。


tl;博士

使用上述设置

如果 仍然 无法正常工作, KAFKA_LISTENERS 可以设置为包括 <PROTOCOL>://0.0.0.0:<PORT> 其中两个选项都匹配广告设置和 Docker 转发端口

客户端在同一台机器上,而不是在容器中

正如您所期望的那样,广告 localhost 和关联的端口将使您可以连接到容器外部。

换句话说,当在 Docker 网络 之外 运行任何 Kafka 客户端(包括您可能已在本地安装的 CLI 工具)时,使用 localhost:29092 作为引导服务器,使用 --- localhost:2181 作为 Zookeeper(需要 Docker 端口转发) )

另一台机器上的客户端

如果尝试从外部服务器连接,您需要公布主机的外部主机名/IP 以及/代替 localhost

简单地通过端口转发来通告本地主机是行不通的,因为 Kafka 协议仍会继续通告您配置的侦听器。

如果不在同一个本地网络中,此设置需要 Docker 端口转发 路由器端口转发(以及防火墙/安全组更改),例如,您的容器在云中运行并且您希望从本地计算机与其交互。

同一主机上容器中的客户端(或其他代理)

这是最不容易出错的配置;您可以直接使用 DNS 服务名称。

在 Docker 网络中 运行应用程序时,使用 kafka:9092 (参见上面的广告 PLAINTEXT 侦听器配置)用于引导服务器和 zookeeper:2181 for 76a,就像其他 Docker 一样服务通信(不需要任何端口转发)

如果您使用单独的 docker run 命令或 Compose 文件,您需要手动定义一个共享的 network

请参阅示例 Compose 文件以获取完整的 Confluent 堆栈更小 的单个代理。

如果使用多个代理,那么他们需要使用唯一的主机名 + 广告侦听器。 见例子

相关问题

从 Docker (ksqlDB) 连接到主机上的 Kafka

附录

对于任何对 Kubernetes 部署感兴趣的人:

  • 运营商(推荐): https ://operatorhub.io/?keyword=Kafka
  • Helm 工件中心: https ://artifacthub.io/packages/search?ts_query_web=kafka&sort=stars&page=1

原文由 OneCricketeer 发布,翻译遵循 CC BY-SA 4.0 许可协议

当你第一次连接到一个 kafka 节点时,它会返回所有的 kafka 节点和连接的 url。然后您的应用程序将尝试直接连接到每个 kafka。

问题始终是 kafka 会给你什么作为 url?这就是为什么有 KAFKA_ADVERTISED_LISTENERS kafka 将使用它来告诉世界如何访问它。

现在对于您的用例,需要考虑多个小问题:

假设您设置 plaintext://kafka:9092

  • 如果您的 docker compose 中有一个使用 kafka 的应用程序,这没问题。此应用程序将从 kafka 获取带有 kafka 的 URL,该 URL 可通过 docker 网络解析。
  • 如果您尝试从您的主系统或不在同一个 docker 网络中的另一个容器连接,这将失败,因为 kafka 名称无法解析。

==> 要解决这个问题,你需要有一个特定的 DNS 服务器,比如服务发现服务器,但这对小东西来说是个大麻烦。或者您手动将 kafka 名称设置为每个容器 ip /etc/hosts

如果你设置 plaintext://localhost:9092

  • 如果您有端口映射(启动 kafka 时为 -p 9092:9092),这在您的系统上是可以的
  • 如果您从容器上的应用程序进行测试(是否是同一个 docker 网络),这将失败(localhost 是容器本身而不是 kafka 容器)

==> 如果你有这个并且希望在另一个容器中使用 kafka 客户端,解决这个问题的一种方法是为两个容器共享网络(相同的 ip)

最后一个选项:在名称中设置一个 IP: plaintext://x.y.z.a:9092 (kafka 广告 url 不能是 0.0.0.0,如文档 https://kafka.apache.org/documentation/#brokerconfigs_advertised.listeners 中所述)

这对每个人来说都可以…但是您如何获得 xyza 名称?

唯一的方法是在启动容器时硬编码此 ip: docker run .... --net confluent --ip 10.x.y.z ... 。请注意,您需要将 ip 适配为 confluent 子网中的一个有效 ip。

原文由 wargre 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题