连接到在 Docker 中运行的 Kafka
我在本地计算机上设置了一个单节点 Kafka Docker 容器,如 Confluence 文档(步骤 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: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
图像。我使用基于 confluenceinc 的图像,但它不一样。
I setup a single node Kafka Docker container on my local machine like it is described in the Confluent 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 a 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? My code is running from host machine, not Docker.
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 confluentinc
based image which is not the same.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
tl;dr - 从容器到主机的简单端口转发将不起作用...主机文件(例如
/etc/hosts
*NIX 系统)不应修改以解决 Kafka 网络问题,因为此解决方案不可移植。1) 您想要连接到哪个确切的 IP/主机名 + 端口?确保该值在经纪人。如果您看到诸如
Connection to node -1 (localhost/127.0.0.1:9092)
之类的错误,则意味着您的应用容器尝试连接到自身。您的应用程序容器是否也在运行 Kafka 代理进程?可能不会。2) 确保作为
bootstrap.servers
一部分列出的服务器实际上是可解析的。例如ping
IP/主机名,使用netcat
检查端口...如果您的客户端位于容器中,则需要从容器中执行此操作,不仅仅是您的主机。如果容器没有立即崩溃以访问其 shell,请使用 docker exec 。3) 如果从主机而不是另一个容器运行进程,要验证主机上的端口是否正确映射,请确保 docker ps 表明 kafka 容器是从 0.0.0.0 映射的:<主机端口> ->/tcp。如果尝试从 Docker 网络外部运行客户端,则端口必须匹配。两个容器之间不需要端口转发;使用链接/docker网络
以下部分尝试汇总使用另一个图像所需的所有详细信息。对于其他常用的 Kafka 镜像,都是在容器中运行的 Apache Kafka。
您只依赖于它的配置方式。以及哪些变量导致了这种情况。
如需补充阅读、功能齐全
docker-compose
和网络图,请参阅@rmoff 的此博客回答
Confluence 快速入门 (Docker) 文档 假设所有生产和消费请求都将位于 Docker 网络内。
您可以通过在自己的容器中运行 Kafka 客户端代码(使用 Docker 网桥)来解决连接到 kafka:9092 的问题,但否则您需要添加更多环境变量来公开容器在外部,同时仍然在 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
侦听器。所以...(使用上述设置)
客户端位于同一台计算机上,而不是在容器中
广告本地主机和关联的端口将允许您在容器外部进行连接,正如您所期望的那样。
换句话说,当在 Docker 网络外部运行任何 Kafka 客户端(包括您可能在本地安装的 CLI 工具)时,请使用 localhost:29092 作为引导服务器(需要 Docker 端口转发) )
另一台计算机上的客户端
如果尝试从外部服务器连接,您需要公布主机的外部主机名/IP(例如
192.168.xy
)以及/在本地主机的位置。简单地通过端口转发来通告 localhost 是行不通的,因为 Kafka 协议仍将继续通告您已配置的侦听器。
如果不在同一本地网络中,此设置需要 Docker 端口转发和路由器端口转发(以及防火墙/安全组更改),例如,您的容器在云中运行并且您想要与其交互从您的本地计算机。
容器中的客户端(或另一个代理)位于同一主机上
这是最不容易出错的配置;您可以直接使用 DNS 服务名称。
在 Docker 网络中运行应用程序时,请使用 Docker 服务名称,例如
kafka:9092
(请参阅上面广告的PLAINTEXT
侦听器配置)作为引导服务器,就像任何其他 Docker 服务通信一样(不需要任何端口转发)如果您使用单独的 docker run 命令或 Compose 文件,则需要定义共享网络手动使用撰写
networks
部分或docker network --create
查看使用 Kraft 的完整 Confluence 堆栈的示例 Compose 文件 或 更最小的一个(使用 Zookeeper ) 对于单个经纪人。
对于 apache/kafka 映像,Kafka Github 存储库。
如果使用多个代理,那么他们需要使用唯一的主机名+广告侦听器。 查看示例
相关问题
从 Docker 连接到主机上的 Kafka (ksqlDB)
附录
对于任何对 Kubernetes 部署感兴趣的人:
tl;dr - A simple port forward from the container to the host will not work... Hosts files (e.g.
/etc/hosts
on *NIX systems) should not be modified to work around Kafka networking, as this solution is not portable.1) What exact IP/hostname + port do you want to connect to? Make sure that value is set as
advertised.listeners
(notadvertised.host.name
andadvertised.port
, as these are deprecated) on the broker. If you see an error such asConnection to node -1 (localhost/127.0.0.1:9092)
, then that means your app container tries to connect to itself. Is your app container also running a Kafka broker process? Probably not.2) Make sure that the server(s) listed as part of
bootstrap.servers
are actually resolvable. E.gping
an IP/hostname, usenetcat
to check ports... If your clients are in a container, you need to do this from the container, not (only) your host. Usedocker exec
if the container isn't immediately crashing to get to its shell.3) If running a process from the host, rather than another container, to verify the ports are mapped correctly on the host, ensure that
docker ps
shows the kafka container is mapped from0.0.0.0:<host_port> -> <advertised_listener_port>/tcp
. The ports must match if trying to run a client from outside the Docker network. You do not need port forwarding between two containers; use links / docker networksThe following sections try to aggregate all the details needed to use another image. For other, commonly used Kafka images, it's all the same Apache Kafka running in a container.
You're just dependent on how it is configured. And which variables make it so.
For supplemental reading, a fully-functional
docker-compose
, and network diagrams, see this blog by @rmoffAnswer
The Confluent quickstart (Docker) document assumes all produce and consume requests will be within the Docker network.
You could fix the problem of connecting to
kafka:9092
by running your Kafka client code within its own container as that uses the Docker network bridge, 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 protocolKey:
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP
Value:
PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
Then setup two advertised listeners on different ports. (
kafka
here refers to the docker container name; it might also be namedbroker
, so double check your service + hostnames).Key:
KAFKA_ADVERTISED_LISTENERS
Value:
PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092
Notice the protocols here match the left-side values of the protocol mapping setting above
When running the container, add
-p 29092:29092
for the host port mapping, and advertisedPLAINTEXT_HOST
listener.So... (with the above settings)
Client on same machine, not in a container
Advertising localhost and the associated port will let you connect outside of the container, as you'd expect.
In other words, when running any Kafka Client outside the Docker network (including CLI tools you might have installed locally), use
localhost:29092
for bootstrap servers (requires Docker port forwarding)Client on another machine
If trying to connect from an external server, you'll need to advertise the external hostname/ip (e.g.
192.168.x.y
) of the host as well as/in place of localhost.Simply advertising localhost with a port forward will not work because Kafka protocol will still continue to advertise the listeners you've configured.
This setup requires Docker port forwarding and router port forwarding (and firewall / security group changes) if not in the same local network, for example, your container is running in the cloud and you want to interact with it from your local machine.
Client (or another broker) in a container, on the same host
This is the least error-prone configuration; you can use DNS service names directly.
When running an app in the Docker network, use Docker service names such as
kafka:9092
(see advertisedPLAINTEXT
listener config above) for bootstrap servers, just like any other Docker service communication (doesn't require any port forwarding)If you use separate
docker run
commands, or Compose files, you need to define a sharednetwork
manually using composenetworks
section ordocker network --create
See the example Compose file for the full Confluent stack using Kraft or more minimal one (with Zookeeper) for a single broker.
For
apache/kafka
image, there's example files in the Kafka Github repo.If using multiple brokers, then they need to use unique hostnames + advertised listeners. See example
Related question
Connect to Kafka on host from Docker (ksqlDB)
Appendix
For anyone interested in Kubernetes deployments:
当你第一次连接到一个kafka节点时,它会返回所有的kafka节点和连接的url。然后你的应用程序将尝试直接连接到每个kafka。
问题始终是 kafka 会给你什么 url ?这就是为什么有
KAFKA_ADVERTISED_LISTENERS
,kafka 将使用它来告诉世界如何访问它。现在,对于您的用例,有多个小问题需要考虑:
假设您设置了 plaintext://kafka:9092 ,
kafka
URL。==>为了解决这个问题,你需要有一个特定的 DNS 服务器,比如服务发现服务器,但这对于小东西来说是个大麻烦。或者您手动将
kafka
名称设置为每个/etc/hosts
中的容器 ip如果您设置
plaintext://localhost:9092
==>如果您有此问题并希望在另一个容器中使用 kafka 客户端,解决此问题的一种方法是共享两个容器的网络(相同的 IP)
最后一个选项:在名称中设置 IP:
plaintext://xyza :9092
(kafka 广告的 url 不能为 0.0.0.0,如文档 https://kafka.apache.org/documentation/#brokerconfigs_advertished.listeners )这将是每个人都可以...但是你怎么能得到 xyza 名字呢?
唯一的方法是在启动容器时硬编码此 IP:
docker run .... --net confluence --ip 10.xyz ...
。请注意,您需要将 ip 调整为confluence
子网中的一个有效 ip。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
( kafka advertised url cannot be 0.0.0.0 as stated in the doc https://kafka.apache.org/documentation/#brokerconfigs_advertised.listeners )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.在 Zookeeper
在 kafka
KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://ip_address_of_your_computer_but_not_localhost!!!:9092 -e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 kafka 消费者和生产者配置中的
我按照这些规定运行我的项目。祝你好运,伙计。
before zookeeper
after kafka
in kafka consumer and producer config
I run my project with these regulations. Good luck dude.
这允许我在 M1 Mac 上的 Kafka 应用程序中访问 localhost:9092
以及端口转发:
最后,再次,对于我的设置,我必须以这种方式设置侦听器密钥
This allows me to access
localhost:9092
in Kafka applications on my M1 Macplus port forwarding :
Finally, again, for my set up, I have to set listeners key this way
解决此问题的最简单方法是使用 -h 选项向代理添加自定义主机名
,然后编辑 /etc/hosts
并使用:
the simplest way to solve this is adding a custom hostname to your broker using -h option
and edit your /etc/hosts
and use:
此配置工作正常,
请确保从 docker 内部
从外部容器
localhost:9092
连接kafka:29092
完整工作 docker compose 配置
This configuration works fine
make sure from inside docker you are connecting
kafka:29092
from outside container
localhost:9092
full working docker compose config