Kafka on Kubernetes 多节点

Kafka on Kubernetes multi-node

所以我的 objective 是以分布式方式建立一个由多个 kafka-broker 组成的集群。但是我看不到让经纪人相互了解的方法。

据我了解,每个代理在其配置中都需要一个单独的 ID,如果我从 kubernetes 启动容器,我不能保证或配置它?

他们也需要有相同的 advertised_host?

是否有任何我遗漏的参数需要更改才能让节点相互发现?

在 Dockerfile 的末尾使用脚本进行这样的配置是否可行? And/or 共享卷?

我目前正尝试在 vanilla Kubernetes 上使用 spotify/kafka-image 进行此操作,它具有预配置的 zookeeper+kafka 组合。

我使用 docker-compose 完成了此操作(Kubernetes 的区别在于您将通过 service.yaml 传递 ID 并拥有 2 个服务):

kafka1:
  build: kafka-0.8.1/
  ports:
  - 9092
  links:
  - zookeeper
  environment:
  - ID=1
kafka2:
  build: kafka-0.8.1/
  ports:
  - 9092
  links:
  - zookeeper
  environment:
  - ID=2

配置:

broker.id=${ID}
port=9092
advertised.host.name=${HOST}
advertised.port=9092
num.network.threads=3
num.io.threads=8
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
log.dirs=/kafka/kafka-logs-${ID}
num.partitions=200
num.recovery.threads.per.data.dir=1
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000
log.cleaner.enable=false
zookeeper.connect=${DOCKER_ZOOKEEPER_1_PORT_2181_TCP_ADDR}:${DOCKER_ZOOKEEPER_1_PORT_2181_TCP_PORT}
zookeeper.connection.timeout.ms=6000

sh:

#!/bin/bash
echo "Running config"
export HOST=`grep $HOSTNAME /etc/hosts | awk '{print }'`
export ID=${ID:?}
perl -p -i -e 's/$\{([^}]+)\}/defined $ENV{} ? $ENV{} : $&/eg' < /broker.template > $KAFKA_HOME/config/server.properties
echo "Done"
echo "starting kafka with:"
echo "$KAFKA_HOME/config/server.properties"
echo ""
cat $KAFKA_HOME/config/server.properties
$KAFKA_HOME/bin/kafka-server-start.sh $KAFKA_HOME/config/server.properties

我的解决方案是使用 IP 作为 ID:trim 点,您将获得一个唯一的 ID,该 ID 也可以在容器外使用到其他容器。

通过服务,您可以访问多个容器的 IP(请在此处查看我的回答以了解如何执行此操作: what's the best way to let kubenetes pods communicate with each other?

因此,如果您使用 IP 作为唯一 ID,您也可以获得他们的 ID。 唯一的问题是ID不连续或从0开始,但zookeeper / kafka似乎不介意。

编辑 1:

后续关注Zookeeper的配置:

每个ZK节点都需要知道其他节点。 Kubernetes 发现服务知道 Service 中的节点,因此我们的想法是使用 ZK 节点启动 Service

需要在创建 Zookeeper 的 ReplicationController (RC) 之前启动此服务 pods。

ZK容器的启动脚本需要:

  • 等待发现服务用它的节点填充 ZK 服务(这需要几秒钟,现在我只是在启动脚本的开头添加一个 sleep 10 但更可靠的是你应该寻找服务来其中至少有 3 个节点。)
  • 在发现服务中查找构成服务的容器: 这是通过查询 API 完成的。 KUBERNETES_SERVICE_HOST 环境变量在每个容器中可用。 查找服务描述的端点是

URL="http(s)://$USERNAME:$PASSWORD@${KUBERNETES_SERVICE_HOST/api/v1/namespaces/${NAMESPACE}/endpoints/${SERVICE_NAME}"

其中 NAMESPACEdefault,除非您更改它,如果您将服务命名为 zookeeper,SERVICE_NAME 将是 zookeeper。

你会得到构成服务的容器的描述,它们的 ip 在 "ip" 字段中。 你可以这样做:

curl -s $URL | grep '\"ip\"' | awk '{print }' | awk -F\" '{print }' 

获取服务中的 IP 列表。 这样,使用上面定义的 ID

填充节点上的 zoo.cfg

您可能需要 USERNAMEPASSWORD 才能到达 google 容器引擎等服务的端点。这些需要放在 Secret 卷中(请参阅此处的文档:http://kubernetes.io/v1.0/docs/user-guide/secrets.html

您还需要在 Google Container Engine 上使用 curl -s --insecure,除非您不厌其烦地将 CA 证书添加到您的 pods

基本上将卷添加到容器中,并从文件中查找值。 (与文档所说的相反,在使用 base64 编码时,请勿将 \n 放在用户名或密码的末尾:阅读这些内容只会让您的生活变得更加复杂)

编辑 2:

您需要在 Kafka 节点上做的另一件事是获取 IP 和主机名,并将它们放入 /etc/hosts 文件中。 Kafka 似乎需要通过主机名了解节点,默认情况下这些节点未在服务节点中设置

编辑 3:

经过多次尝试和思考,使用 IP 作为 ID 可能不是很好:这取决于您如何配置存储。 对于任何类型的分布式服务,如 zookeeper、kafka、mongo、hdfs,您可能希望使用 emptyDir 类型的存储,因此它就在那个节点上(安装远程存储类型会破坏分布这些的目的服务!) emptyDir 将在同一节点上重新加载数据,因此使用 NODE ID(节点 IP)作为 ID 似乎更合乎逻辑,因为这样在同一节点上重新启动的 pod 将拥有数据。 这避免了潜在的数据损坏(如果一个新节点开始写入实际上不是空的相同目录,谁知道会发生什么)并且对于 Kafka,主题被分配 broker.id,如果 broker id更改,zookeeper 不更新主题 broker.id,主题看起来可用,但指向错误 broker.id,一团糟。

到目前为止我还没有找到如何获取节点 IP,但我认为可以通过查找服务 pods 名称然后查找节点来在 API 中进行查找部署于。

编辑 4

要获取节点IP,您可以从端点获取pod主机名==名称API /api/v1/namespaces/default/endpoints/ 如上所述。 然后你可以从 pod 名称中获取节点 IP /api/v1/namespaces/default/pods/

PS:这是受 Kubernetes 存储库中示例的启发(这里是 rethinkdb 的示例:https://github.com/kubernetes/kubernetes/tree/master/examples/rethinkdb

看看 https://github.com/CloudTrackInc/kubernetes-kafka 它允许在 kubernetes 中启动 Kafka 并支持扩展它和自动扩展。

这在我的搜索中很显眼,但包含相当过时的信息。要使用更现代的解决方案对此进行更新,您应该使用 StatefulSet 部署,这将生成 pods,它们的名称中有一个整数计数器而不是散列,例如。卡夫卡控制器-0。

这当然是主机名,因此使用 awk 从那里提取固定不变的代理 ID 是一件简单的事情:

hostname | awk -F'-' '{print }'

目前最流行的 Kafka 容器都有代理 ID 命令。