我在本地机器上使用docker设置单节点基本Kafka部署,就像在the Confluent Kafka documentation中描述的那样(步骤2-3)。
另外,我还暴露了zookeeper的端口2181和kafka的端口9092,这样我就能从本地机器上运行的java客户端连接到它们:
$ 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时,连接失败,因为它无法解析地址: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?
注意:我知道我理论上可以使用DNS设置和/etc/hosts
,但它是一种解决方法 - 它应该不是那样的。
也有类似的问题here,但它是基于ches/kafka
图像。我使用基于confluent
的图像,这是不一样的。
免责声明:以下使用
confluentinc
码头图像,而不是wurstmeister/kafka
,虽然there is a similar configuration,我还没有尝试过。如果使用该图像,read their Connectivity wiki。
debezium/kafka
文件are mentioned here。我尝试了
bitnami
Kafka图像,并让它与these settings一起工作在一天结束时,它是在容器中运行的所有相同的Apache Kafka。您只需依赖它的配置方式。
有关补充阅读和网络图,请参阅this blog by @rmoff
Confluent快速入门文档假定所有生产和使用请求都在Docker网络中。
您可以通过在自己的容器中运行Kafka客户端代码来解决问题,但除此之外,您还需要添加一些环境变量以便在外部公开容器,同时仍然可以在Docker网络中运行它。
首先添加PLAINTEXT_HOST:PLAINTEXT
的协议映射,该映射将侦听器协议映射到Kafka协议
-e KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
然后在不同的端口上设置两个播发的侦听器。 (kafka:9092
在这里指的是docker容器名称)。请注意,协议与上面映射的右侧值匹配
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092
运行容器时,为主机端口映射添加-p 29092:29092
tl; dr(具有上述设置)
在Docker网络之外运行任何Kafka客户端时(包括本地安装的CLI工具),使用localhost:29092
作为引导服务器,使用localhost:2181
作为Zookeeper
在Docker网络中运行应用程序时,使用kafka:9092
作为bootstrap服务器,使用zookeeper:2181
作为Zookeeper
当您第一次连接到kafka节点时,它将返回所有kafka节点和url连接位置。然后您的应用程序将尝试直接连接到每个kafka。
问题总是kafka会给你什么作为网址?这就是为什么kafka将使用KAFKA_ADVERTISED_LISTENERS
告诉全世界如何访问它。
现在,对于您的用例,需要考虑以下几个小问题:
假设你设置plaintext://kafka:9092
kafka
的URL。kafka
名称。==>要解决此问题,您需要拥有一个特定的DNS服务器,如服务发现服务器,但对于小东西来说这是一个很大的麻烦。或者你手动将kafka
名称设置为每个/etc/hosts
中的容器ip
如果你设置plaintext://localhost:9092
==>如果您有此并希望在另一个容器中使用kafka客户端,解决此问题的一种方法是共享两个容器的网络(相同的IP)
最后一个选项:在名称中设置IP:plaintext://x.y.z.a:9092
这对每个人都没问题......但是你怎么能得到x.y.z.a这个名字?
唯一的方法是在启动容器时对此ip进行硬编码:docker run .... --net confluent --ip 10.x.y.z ...
。请注意,您需要将ip调整为confluent
子网中的一个有效ip。