Kafka是一个分布式流处理平台,最近几年获得了长足的发展和进步。这篇文章主要针对在AWS 上部署 Kafka Docker镜像的注意事项。其中最容易出问题的部分就是Kafka的listeners配置,如果不能正确配置Listener,会造成Kafka 客户端找不到Broker,无法使用。这个问题在Docker背景下更加复杂一些,因为这里涉及到三个网络:Docker内部网络,运行的Docker的机器本地网络(这里可以是AWS的VPC网络)以及外网(即Internet)。
目前AWS部署Docker镜像应用主要有两种方式,一种是传统的EC2部署,另一种是托管的ECS部署。理论上,只要你的docker-compose文件在一种方式通过测试,那么在另一种应该也会通过测试,因为ECS和EC2的docker环境几乎是一样的。
Kafka Broker和Zookeeper
我们需要弄清楚Zookeeper和Kafka Broker之间的关系。Zookeeper是Apache开发用来集中管理、协调分布式系统,比如Kafka集群,的工具。他就相当于是一个指挥官,负责协调kafka集群中的各个Broker,Zookeeper会把集群信息分散到Broker,这样每一个Broker会知道是否有新的Broker加入?是否有Broker离线?是否有新的Topic加入?等等。(我想这也是Zookeeper名字的来源吧,就是动物园管理员的身份。。。。)
没有Zookeeper不能运行Kafka集群。
所以,在配置Kafka时,需要首先部署Zookeeper,然后将Zoopeeker的端口信息暴露给每一个Kafka的Broker,并且确保网络通讯,即尽量把他们放在同一个网络中。如果不行,就需要保证不同的Zookeeper之间能够通讯协调,而Broker之间是不会互相对话的。
Kafka的Listener
Kafka是一个分布式系统,客户端(Consumer、Producer或者其他Connector)第一次连接集群时,会向集群请求Meta Data,他可以向集群中的任何一个Broker读取或者写入数据。但是,容易出问题的地方就是如何找到集群中的一个Broker呢?通常我们使用IP地址或者DNS名称寻找broker。但是当我们使用Docker部署在AWS时候,涉及到三个网络:Docker内部网络,运行的Docker的机器本地网络(这里可以是AWS的VPC网络)以及外网(即Internet),我们必须为不同的网络配置对应的Listener,才能保证Broker可以被不用网络的用户正确的发现。否则,会出现找不到Broker的问题。
问题的关键是,当你启动一个Kafka客户端,你传入的Broker会成为客户端发现整个集群的入口。此后,客户端读取、写入数据所使用的Host&IP会基于这个Broker传回的信息。比如,下面的情况,这个Producer就会连接server1.xxxx.com:9092
import json
from kafka import KafkaProducer
producer = KafkaProducer(
bootstrap_servers=['server1.xxxx.com:9092'],
value_serializer=lambda v: json.dumps(v).encode('utf-8'),
)
重点来了:一个Kafka Broker可以有多个Listener,这就是在多个网络环境下配置Kafka的关键。每一个Listener包含如下属性:
- Host/IP
- Port
- Protocol
我们来看一个例子:
KAFKA_LISTENERS: INTERNAL://0.0.0.0:29092, EXTERNAL://0.0.0.0:9092, AWS://0.0.0.0:29094
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:29092,EXTERNAL://xxxx.xxxx.com:9092, AWS://172.31.x.x:29094
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,AWS:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
上面配置涉及三个不同Listener,分别是Internal、External和AWS,黄色代表了AWS本地网络,或VPC网络,蓝色代表了Docker内部网络,绿色代表非AWS网络,或者一般的公网或者其他VPC网络。可以看到,Producer1 处于Docker内部,需要连接Internal Listener:29092才能正确连接集群。而Producer2 处于AWS本地网络,或者VPC网络,他需要连接对应的AWS listener:29094。而对于Producer3和Consumer3,他们处于VPC之外,需要访问External listerner: 9092。
这里值得注意的是,理论上Producer2也可以访问External。可是通常,处于安全考虑,防火墙会限制外网访问端口和IP,所以最好的方案是为外网和内网配置不同的Listener,以便防火墙进行安全管理。
Kafka部署的Compose文件配置
根据上面的配置文件,我们形成了如下Compose文件:
---
version: '2'
services:
zookeeper:
image: confluentinc/cp-zookeeper:latest
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
kafka:
image: confluentinc/cp-kafka:latest
depends_on:
- zookeeper
ports:
- 9092:9092
- 29094:29094
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_LISTENERS: INTERNAL://0.0.0.0:29092, EXTERNAL://0.0.0.0:9092, AWS://0.0.0.0:29094
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:29092,EXTERNAL://xxxx.xxxx.com:9092, AWS://172.31.x.x:29094
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,AWS:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
参考:
- https://medium.com/hacking-talent/kafka-all-you-need-to-know-8c7251b49ad0
- https://github.com/wurstmeister/kafka-docker/wiki/Connectivity
- https://rmoff.net/2018/08/02/kafka-listeners-explained/
- https://github.com/confluentinc/cp-docker-images/tree/5.3.1-post/examples
- 利用docker-compose部署ECS
- http://cloudurable.com/blog/kafka-architecture/index.html