从本地计算机连接到在Docker中运行的Kafka

时间:2018-08-01 09:50:52

标签: java docker apache-kafka confluent

我在本地计算机上使用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的图片,这是不一样的。

3 个答案:

答案 0 :(得分:7)

  

免责声明:以下示例使用confluentinc个docker映像,而不是wurstmeister/kafa,尽管there is a similar configuration,但我没有尝试过。如果使用该图像,请read their Connectivity wiki。归根结底,Apache Kafka仍在容器中运行。

     

我尝试了bitnami个Kafka图像,并将其与these settings

一起使用      

有关补充阅读图和网络图,请参见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

在外部Docker网络外部运行任何Kafka客户端时,将localhost:29092用于引导服务器,将localhost:2181用于Zookeeper

在Docker网络中运行应用时,将kafka:9092用于引导服务器,将zookeeper:2181用于Zookeeper

See the example Compose file for the full Confluent stack

答案 1 :(得分:2)

首次连接到kafka节点时,它将返回所有kafka节点和要连接的URL。然后,您的应用程序将尝试直接连接到每个kafka。

问题永远是kafka将为您提供的url吗?这就是为什么卡夫卡会使用KAFKA_ADVERTISED_LISTENERS来告诉世界如何访问它的原因。

现在就您的用例而言,有很多小问题需要考虑:

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

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

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

中的容器ip

如果您设置了plaintext://localhost:9092

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

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

最后一个选项:在名称plaintext://x.y.z.a:9092

中设置IP

每个人都可以...但是如何获得x.y.z.a名称?

唯一的方法是在启动容器docker run .... --net confluent --ip 10.x.y.z ...时对该IP进行硬编码。请注意,您需要使IP地址适应confluent子网中的一个有效IP地址。

答案 2 :(得分:1)

在动物园管理员之前

  1. docker container run --name zookeeper -p 2181:2181 zookeeper

卡夫卡之后

  1. docker容器运行--name kafka -p 9092:9092 -e KAFKA_ZOOKEEPER_CONNECT = 192.168.8.128:2181 -e KAFKA_ADVERTISED_LISTENERS = PLAINTEXT:// ip_address_of_your_computer_but_not_localhost !!!:9092 -e KAFKA_OFFSETS_TOPIC_REPLICATION_FCP = 1 li>

在kafka消费者和生产者配置中

@Bean
public ProducerFactory<String, String> producerFactory() {
    Map<String, Object> configProps = new HashMap<>();
    configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.8.128:9092");
    configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    return new DefaultKafkaProducerFactory<>(configProps);
}

@Bean
public ConsumerFactory<String, String> consumerFactory() {
    Map<String, Object> props = new HashMap<>();
    props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.8.128:9092");
    props.put(ConsumerConfig.GROUP_ID_CONFIG, "group_id");
    props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
    props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
    return new DefaultKafkaConsumerFactory<>(props);
}

我按照这些规定运行我的项目。祝你好运伙计。