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}"
其中 NAMESPACE
是 default
,除非您更改它,如果您将服务命名为 zookeeper,SERVICE_NAME
将是 zookeeper。
你会得到构成服务的容器的描述,它们的 ip 在 "ip" 字段中。
你可以这样做:
curl -s $URL | grep '\"ip\"' | awk '{print }' | awk -F\" '{print }'
获取服务中的 IP 列表。
这样,使用上面定义的 ID
填充节点上的 zoo.cfg
您可能需要 USERNAME 和 PASSWORD 才能到达 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 命令。
所以我的 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}"
其中 NAMESPACE
是 default
,除非您更改它,如果您将服务命名为 zookeeper,SERVICE_NAME
将是 zookeeper。
你会得到构成服务的容器的描述,它们的 ip 在 "ip" 字段中。 你可以这样做:
curl -s $URL | grep '\"ip\"' | awk '{print }' | awk -F\" '{print }'
获取服务中的 IP 列表。 这样,使用上面定义的 ID
填充节点上的 zoo.cfg您可能需要 USERNAME 和 PASSWORD 才能到达 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 命令。