本文是我在使用Docker部署kafka遇到一些问题之后,在网上看到的一篇比较优秀的英文资料Link。借此翻译一下这篇文章,也帮助自己搞懂在使用Docker时遇到的一些网络问题,尤其是Host怎样配置。
作者的Kafka使用环境是Kafka Producer 和 Broker 均在 Docker 网络中, Kafka Consumer 在宿主机环境中。结构如下图这样子:
首先,我从Docker hub 中找到了一个Kafka Docker image。 我使用的是Wurstmeister Kafka and ZooKeeper images, 然后Docker-compose的文件是按照下面的格式定义的:
version: '2'
services:
zookeeper:
image: wurstmeister/zookeeper:3.4.6
expose:
- "2181"
kafka:
image: wurstmeister/kafka:2.11-2.0.0
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
启动Docker,然后按照kafka QuickStart 的步骤来使用kafka-console-producer.sh
和 kafka-console-consumer.sh
在宿主机中运行 Producer 的结果:
andrew@host$ bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
>Hi there!
>It is a test message.
在宿主机中运行 Consumer的结果:
andrew@host$ bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning
Hi there!
It's a test message.
可以看到,不管是producer还是consumer都可以在宿主机网络中正常工作。需要注意的是KAFKA_ADVERTISED_LISTENERS
这个环境变量在Compose文件中的设置.Kafka会在client第一次连接的时候把这个变量值发送给client。在接收到这个变量值之后,client就可以使用它来从kafka 的 broker 中消费或者生产数据了。
由于我们定义的变量值为 PLAINTEXT://localhost:9092
, producer和consumer在初始化连接时都会使用它并且之后所有的通信都会通过 9092 这个端口。
这里的关键要点是客户端使用指定的Kafka地址(--bootstrap-server
and --broker-list
的值)。Kafka之后冲顶下他们的值为KAFKA_ADVERTISED_LISTENERS
下面让我们在运行Kafka容器的同一Docker网络内的任意Docker容器内运行Producer:
oot@869f83f2f265:/kafka# bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
>Hi there!
[2018-10-10 14:37:40,397] WARN [Producer clientId=console-producer] Connection to node -1 could not be established. Broker may not be available. (org.apache.kafka.clients.NetworkClient)
这时候就会报错了。这时候发生的事情是client收到了KAFKA_ADVERTISED_LISTENERS
这个环境变量的值(PLAINTEXT://localhost:9092
),之后尝试去连接它然后发现失败了,因为在自己docker网络中并没有这个地址。显然,从client的角度来说可以通过kafka:9092
这个地址来连接kafka。因此,为了使 client 能够和 broker 通信,KAFKA_ADVERTISED_LISTENERS
这个变量值就必须设置为PLAINTEXT://kafka:9092
,那么,下面就来重新构建一下我们的Compose文件:
kafka:
image: wurstmeister/kafka:2.11-2.0.0
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
然后测试producer:
oot@7dfb9eaa81dc:/kafka# bin/kafka-console-producer.sh --broker-list kafka:9092 --topic test
>Hi there!
成功!现在我们可以在Docker容器内使用Producer发送消息了。
下面我们来尝试通过宿主机上的Consumer消费Kafka的数据:
andrew@host$ bin/kafka-console-consumer.sh --bootstrap-server kafka:9092 --topic test --from-beginning
[2018-10-10 23:57:06,827] WARN Removing server kafka:9092 from bootstrap.servers as DNS resolution failed for kafka (org.apache.kafka.clients.ClientUtils)
正如预料的,此时consumer并不能连接到broker因为宿主机并不能识别kafka:9092
这个地址。我们需要再重新设置一下上面的Compose文件:
kafka:
image: wurstmeister/kafka:2.11-2.0.0
depends_on:
- zookeeper
ports:
- "9092:9092"
expose:
- "9093"
environment:
KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:9093,OUTSIDE://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
KAFKA_LISTENERS: INSIDE://0.0.0.0:9093,OUTSIDE://0.0.0.0:9092
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE
下面让我们来解释一下上面的几个环境变量:
-
KAFKA_ADVERTISED_LISTENERS
kafka的broker将监听地址表(
0.0.0.0:9093
,0.0.0.0:9092
)和 listener (INSIDE
,OUTSIDE
) -
KAFKA_ADVERTISED_LISTENERS
指向Broker的可用地址列表,kafka 将会在初始连接时将地址发送给client
-
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP
将上面定义的 listener 名称(INSIDE,OUTSIDE)映射到PLAINTEXT Kafka协议。
-
KAFKA_INTER_BROKER_LISTENER_NAME
指向跨Broker间通信时的命名地址
这里我们定义了两个listeners(INSIDE://0.0.0.0:9093
, OUTSIDE://0.0.0.0:9092
)来分别表示Docker网络内部的流量和Docker主机外部的流量。我们为跨Broker间的通信定义了 INSIDE listener。通过KAFKA_ADVERTISED_LISTENERS
和KAFKA_LISTENER_SECURITY_PROTOCOL_MAP
我们将PLAINTEXT://kafka:9093
发送给那些使用kafka:9093
连接的客户端和
PLAINTEXT://localhost:9092
发送给那些使用localhost:9092
连接的客户端。
总之,我们定义了两种类型的客户端-内部和外部-并且配置kafka返回不同的地址给对应的客户端。
现在我们再来尝试下在Docker网络中使用Producer:
root@7dfb9eaa81dc:/kafka# bin/kafka-console-producer.sh --broker-list kafka:9093 --topic test
>Hi there!
在宿主机中使用Consumer:
andrew@host$ bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning
Hi there!
现在全部都是成功的了!
producer和kafka broker都是在docker网络的内部。
Consumer在外部, borker在Docker网络内部
现在,当服务和kafka 部署在不同的网络环境中的时候,我们也知道该如何去配置Docker了。