使用kubernetes-kafka作为minikube的起点 .
这使用StatefulSet和headless service进行集群内的服务发现 .
目标是在外部公开个人Kafka经纪人,这些经纪人在内部被称为:
kafka-0.broker.kafka.svc.cluster.local:9092
kafka-1.broker.kafka.svc.cluster.local:9092
kafka-2.broker.kafka.svc.cluster.local:9092
限制是该外部服务能够专门解决代理 .
什么是正确的(或一种可能的)方式?是否可以按 kafka-x.broker.kafka.svc.cluster.local:9092
公开外部服务?
5 回答
我们已经在1.7中通过将无头服务更改为
Type=NodePort
并设置externalTrafficPolicy=Local
来解决了这个问题 . 这会绕过服务的内部负载 balancer ,并且只有当Kafka pod位于该节点上时,才会将指向该节点端口上的特定节点的流量工作 .例如,我们有两个节点nodeA和nodeB,nodeB正在运行一个kafka pod . nodeA:30000将无法连接,但nodeB:30000将连接到nodeB上运行的kafka pod .
https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typenodeport
请注意,此版本在1.5和1.6中也可用作beta注释,更多信息可在此处找到:https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip
另请注意,虽然这会将kafka pod与特定的外部网络标识绑定,但并不能保证您的存储卷与该网络标识相关联 . 如果您在StatefulSet中使用VolumeClaimTemplates,则您的卷将绑定到pod,而kafka期望该卷与网络标识绑定 .
例如,如果kafka-0 pod重新启动并且kafka-0在nodeC而不是nodeA上出现,则kafka-0的pvc(如果使用VolumeClaimTemplates)具有针对nodeA的数据,并且在kafka-0上运行的代理开始拒绝请求它是nodeA而不是nodeC .
为了解决这个问题,我们期待本地持久性卷,但是现在我们的kafka StatefulSet只有一个PVC,数据存储在该PVC上的
$NODENAME
之下,用于将卷数据绑定到特定节点 .https://github.com/kubernetes/features/issues/121 https://kubernetes.io/docs/concepts/storage/volumes/#local
到目前为止,解决方案对我自己来说还不够令人满意,所以我将发布一个自己的答案 . 我的目标:
Pod仍应尽可能通过StatefulSet动态管理 .
为 生产环境 者/消费者客户端为每个Pod(即Kafka Broker)创建外部服务,并避免负载 balancer .
创建内部无头服务,以便每个代理可以相互通信 .
从Yolean/kubernetes-kafka开始,唯一缺少的是外部公开服务,这样做有两个挑战 .
为每个Broker pod生成唯一标签,以便我们可以为每个Broker pod创建外部服务 .
告诉经纪人使用内部服务相互通信,同时配置Kafka告诉 生产环境 者/消费者通过外部服务进行通信 .
Per pod labels and external services:
要为每个pod生成标签,this issue确实非常有用 . 使用它作为指南,我们将以下行添加到10broker-config.yml
init.sh
属性中:我们保留现有的无头服务,但我们也使用标签为每个pod生成一个外部服务(我将它们添加到20dns.yml):
Configure Kafka with internal/external listeners
我发现this issue非常有用,试图了解如何配置Kafka .
这又需要使用以下内容更新10broker-config.yml中的
init.sh
和server.properties
属性:将以下内容添加到
server.properties
以更新安全协议(当前使用PLAINTEXT
):动态确定
init.sh
中每个Pod的外部IP和外部端口:然后为
EXTERNAL_LISTENER
和INTERNAL_LISTENER
(也在init.sh
属性中)配置listeners
和advertised.listeners
IP:显然,这不是一个完整的 生产环境 解决方案(例如解决外部暴露经纪人的安全问题),而且我仍然在理解如何让内部 生产环境 者/消费者也与经纪人沟通 .
然而,到目前为止,这是我理解Kubernetes和Kafka的最佳方法 .
我试着围绕无头服务是什么/他们的意思是什么之前阅读了这个问题和答案3次 . (我从来没有完全理解无头服务,或者这个Q&A的内容 . )
在第4次阅读(在进一步教育自己之后重新访问它)它终于点击了/我终于理解了 .
So the purpose of this answer is to restate Nadir's question/problem/and answer as if explaining it to a grade schooler. So that others who stumble upon this will get the significance of Nadir's awesome solution on the first read.
Useful Background Knowledge:
存在类型为:ExternalName的服务 .
ExternalName服务只是指向DNS地址 .
有2种ExternalName服务风味:
没有群集IP:
一个好的用例是允许测试集群和 生产环境 集群共享尽可能多的代码 . (并且在某些情况下为了简单方便)测试和 生产环境 中的Pod将指向相同的服务内部群集DNS地址名称,这将是可预测的可重用代码 . 不同之处在于测试环境将具有指向存在于其中的SQL服务的服务簇 . 生产环境 群集将使用ExternalName服务,该服务将重定向/指向 Cloud 提供程序托管SQL解决方案的DNS地址 .
使用群集IP:
这是解决方案关键的ExternalName服务的版本 .
有状态集有三个部分:
一个序数
持久存储
持久且可预测的内部群集DNS名称(它必须与无头服务一起提供此要求)
关于Kube-Proxy,有三件重要的事情需要记住:
确保所有内容都具有唯一的IP .
It 's responsible for implementing the Virtual Static Cluster IP' s(虚拟静态群集IP 's are considered virtual because they only exist in every nodes iptables in the iptables implementation of Kube-Proxy, or in a Kernel Hash Table in the ip-vs next-gen version of Kube-Proxy) and it'也负责具有群集IP的普通Kubernetes服务发生的逻辑负载 balancer 效果 .
KubeProxy负责将NodePorts上的流量映射到具有静态群集IP的相应Kubernetes服务 . < - 这对于有状态服务应该在外部暴露的要求非常重要,NodePorts总是应该涉及外部暴露服务 .
关于无头服务有四个重要的事项需要记住:
它创建一个可预测的DNS地址 .
它不充当内部群集Load Balancer . 您可以直接与可预测的DNS地址标识的pod进行通信 . (这对于有状态工作负载非常有用)
它没有静态群集IP .
作为质量2和3的副作用,它位于Kube-Proxy领域之外(它负责将节点端口上的流量引导到服务 . ) I'll paraphrase this a few times so the problem sinks in: NodePorts can't usually forward traffic to Headless Services. External traffic entering the cluster can't usually be forwarded to Headless Services. It's not intuitive how to externally expose a Headless Service.
Now that we understand the problem better, lets go back to the question: How can a Headless Service (which points to an individual member of a stateful set) be externally exposed?
Solution Part 1: Any pod in the cluster can talk to the members of the statefulset.
因为有状态生成无头服务,具有可预测的内部群集DNS地址的形式:
. statefulsetname - #associatedheadlessservice.namespace.svc.cluster.local:端口
Kafka 0.broker.kafka.svc.cluster.local:9092
Kafka 1.broker.kafka.svc.cluster.local:9092
Kafka 2.broker.kafka.svc.cluster.local:9092
broker.kafka.svc.cluster.local:9092,也可用于指代哪一个可用 .
Solution Part 2: You allow external traffic to talk to members of the stateful set, by introducing a 2nd service that can accept external traffic, and then redirecting traffic from that service to the headless service that can only accept internet traffic.
对于有状态集中的每个窗格,将创建一个类型为ExternalName的服务,其中包含由Kube-Proxy管理的虚拟静态ClusterIP地址 . 这些ExternalName服务中的每一个都指向/重定向流量到解决方案1中标识的可预测的静态内部群集DNS地址,并且因为此ExternalName服务具有通过Kube-Proxy管理的虚拟静态ClusterIP,所以可以存在从NodePorts到其的映射 .
将服务从无头ClusterIP更改为NodePort,NodePort将请求转发到设置端口上的任何节点(在我的示例中为30092)到Kafkas上的端口9042 . 你可以随机点击其中一个pod,但我想这很好 .
20dns.yml变成(像这样):
免责声明:您可能需要两项服务 . 一个用于内部dns名称的无头,一个用于外部访问的NodePort . 我没有试过这个我自己 .
来自kubernetes kafka documentation: