I setup Single Node Basic Kafka Deployment using docker on my local machine like it is described in the Confluent Kafka documentation (steps 2-3).
In addition, I also exposed zookeeper's port 2181 and kafka's port 9092 so that I'll be able to connect to them from java client running on local machine:
$ 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
Problem: When I try to connect to kafka from the host machine, the connection fails because it can't resolve address: kafka:9092.
Here is my Java code:
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();
The exception:
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
Question: How to connect to kafka running in Docker from host machine?
Note: I know that I could theoretically play around with DNS setup and /etc/hosts
but it is a workaround - it shouldn't be like that.
There is also similar question here, however it is based on ches/kafka
image. I use confluent
based image which is not the same.
When you first connect to a kafka node, it will give you back all the kafka node and the url where to connect. Then your application will try to connect to every kafka directly.
Issue is always what is the kafka will give you as url ? It's why there is the
KAFKA_ADVERTISED_LISTENERS
which will be used by kafka to tell the world how it can be accessed.Now for your use-case, there is multiple small stuff to think about:
Let say you set
plaintext://kafka:9092
kafka
that is resolvable through the docker network.kafka
name cannot be resolved.==> To fix this, you need to have a specific DNS server like a service discovery one, but it is big trouble for small stuff. Or you set manually the
kafka
name to the container ip in each/etc/hosts
If you set
plaintext://localhost:9092
==> If you have this and wish to use a kafka client in another container, one way to fix this is to share the network for both container (same ip)
Last option : set an IP in the name:
plaintext://x.y.z.a:9092
This will be ok for everybody... BUT how can you get the x.y.z.a name ?
The only way is to hardcode this ip when you launch the container:
docker run .... --net confluent --ip 10.x.y.z ...
. Note that you need to adapt the ip to one valid ip in theconfluent
subnet.That Confluent quickstart document assumes all produce and consume requests will be within the Docker network.
You could fix the problem by running your Kafka client code within its own container, but otherwise you'll need to add some more environment variables for exposing the container externally, while still having it work within the Docker network.
First add a protocol mapping of
PLAINTEXT_HOST:PLAINTEXT
that will map the listener protocol to a Kafka protocol-e KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
Then setup two advertised listeners on different ports. (
kafka:9092
here refers to the docker container name). Notice the protocols match the right side values of the mappings above-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092
When running the container, add
-p 29092:29092
for the host port mappingtl;dr (with the above settings)
When running any Kafka Client outside the Docker network (including CLI tools you might have installed locally), use
localhost:29092
for bootstrap servers andlocalhost:2181
for ZookeeperWhen running an app in the Docker network, use
kafka:9092
for bootstrap servers andzookeeper:2181
for ZookeeperSee the example Compose file for the full Confluent stack