当前位置: 首页 > 知识库问答 >
问题:

如何在Kubernetes中向外公开StatefulSet的无头服务

萧明贤
2023-03-14

用库伯涅茨-Kafka作为迷你库伯的起点。

这使用StatefulSet和headless服务在集群内进行服务发现。

我们的目标是对外展示各个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

限制是这个外部服务能够专门针对经纪人。

做这件事的正确(或一种可能)方法是什么?是否可以通过< code > Kafka-x . broker . Kafka . SVC . cluster . local:9092 公开外部服务?

共有3个答案

万博涛
2023-03-14

注:我在第一次发帖一年后完全重写了这篇帖子:
1。鉴于Kubernetes的更新,我写的一些内容不再相关,我认为应该删除它,以免混淆人们
2.我现在对库伯内特斯和Kafka都有了更多的了解,应该能够做一个更好的解释。

Kubernetes 上对 Kafka 的背景上下文理解:
假设使用集群 IP 和有状态集类型的服务在 Kubernetes 集群上部署 5 个 pod 的 Kafka 集群,因为有状态集用于创建 Pod,它们各自自动获取以下 5 个内部集群 dns 名称,然后 clusterIP 类型的 kafka 服务会给出另一个内部集群 dns 名称。

M$*  kafka-0.my-kafka-headless-service.my-namespace.svc.cluster.local 
M$   kafka-1.my-kafka-headless-service.my-namespace.svc.cluster.local 
M *  kafka-2.my-kafka-headless-service.my-namespace.svc.cluster.local 
M *  kafka-3.my-kafka-headless-service.my-namespace.svc.cluster.local 
M$   kafka-4.my-kafka-headless-service.my-namespace.svc.cluster.local
     kafka-service.my-namespace.svc.cluster.local

^:假设您有2个Kafka主题:$和*
每个Kafka主题在5 pod Kafka集群中复制3次< br >(上面的ASCII图显示了哪些pod包含$和*主题的副本,m代表元数据)

4有用的背景知识:< br> 1。. svc.cluster.local是内部集群DNS FQDN,但是pods会自动填充自动完成该功能的知识,因此在通过内部集群DNS进行对话时可以省略它。< br> 2。Kafka-x . my-Kafka-headless-service . my-namespace内部群集DNS名称解析为单个pod。< br> 3。kafka-service.my-namespace集群IP类型的kubernetes服务的作用类似于内部集群第4层负载平衡器,并将在5个kafka pods之间循环调度流量。< br> 4。要实现的一个关键Kafka特定概念是,当Kafka客户端与Kafka集群对话时,它分两个阶段进行。假设一个Kafka客户端想要从Kafka集群中读取$ topic。< br >阶段1:客户端读取kafka集群元数据,这在所有5个kafka pod之间同步,因此客户端与哪一个进行对话并不重要,因此使用kafka-service.my-namespace(这是LB的名称空间,仅转发给随机的健康kafka pod)进行初始通信会很有用< br >阶段2:元数据告诉kafka客户端哪些Kafka代理/节点/服务器pod具有感兴趣的主题,在本例中,$ exists on 0、1和4。因此,在第二阶段,客户将只与拥有所需数据的Kafka经纪人直接对话。

如何对外公开无头服务/Statefulset和Kafka特定Nuance的Pods:
假设我有一个3 pod HashiCorp Consul Cluster在库伯内特斯集群上启动,我对其进行配置,使网页启用,我想从局域网查看网页/对外公开它。Pod是无头的这一事实没有什么特别之处。您可以使用NodePort或LoadBalancer类型的服务来公开它们,就像通常公开任何Pod一样,NP或LB将循环LB传入3个Consul Pod之间的流量。

由于Kafka通信分两个阶段进行,这就带来了一些细微差别,当您拥有一个超过1个Kafka pod的Kafka集群时,使用单个LB或NP类型的服务对外公开statefulset的headless服务的常规方法可能无法工作
1.Kafka客户希望在第2阶段通信期间直接与Kafka经纪人通话。因此,您可能需要6个NodePort/LB类型的服务,而不是NodePort类型的1个服务。1将在第1阶段循环LB流量,第5阶段将以1:1映射到各个吊舱以进行第2阶段通信
(如果您运行kubectl get pods--针对5个Kafka pods显示标签,您将看到有状态集的每个pod都有一个唯一的标签,statefulset.kubernetes.io/pod name=Kafka-0,这允许您手动创建映射到有状态集1个pod的1个NP/LB服务。)(请注意,这还不够)
2。在Kubernetes上安装Kafka集群时,其默认配置通常只支持Kubernete集群内的Kafka客户端。请记住,来自Kafka客户机与Kafka群集对话的第1阶段的元数据,Kafka群集可能已配置为它的“广告侦听器”由内部群集DNS名称组成。因此,当LAN客户端通过NP/LB与外部暴露的Kafka群集进行对话时,它在第1阶段成功,但在第2阶段失败,因为第1阶段返回的元数据提供了内部群集DNS名称,作为第2阶段通信期间直接与pods通信的手段,这是群集外部客户端无法解析的,因此仅适用于群集内部的Kafga客户端。因此,配置kafka集群很重要,这样第1阶段元数据返回的“advertized.listeners”就可以由集群外部和集群内部的客户端解析

明确Kafka Nuance导致的问题所在:< br >对于Kafka客户之间的第2阶段沟通-

Kafka细微差别导致问题的解决方案:
Bitnami Kafka Helm Chart有一些自定义逻辑,允许statefulset中的每个pod具有唯一的"advertised.listerners"配置。Bitnami提供硬化容器,根据Quay.io2.5.0只有一个High CVE,作为非root运行,具有合理的留档,并且可以外部暴露*,https://quay.io/repository/bitnami/kafka?tab=tags

我参与的最后一个项目是Bitnami,因为安全是第一位的,我们只有kubernetes集群内部的kafka客户端,我最终不得不弄清楚如何在dev env中对外公开它,以便有人可以运行某种测试,我记得能够让它工作,我还记得它不是超级简单, 也就是说,如果我要在Kubernetes上做另一个Kafka项目,我会建议查看Strimzi Kafka Operator,因为它在外部公开Kafka的选项方面更灵活,并且它有一个很棒的5部分深度分析,提供了使用Strimzi(通过NP、LB或Ingress)外部公开Kubernetes上运行的Kafka集群的不同选项(虽然我不确定Strimzi的安全性如何,所以我建议使用类似AnchorCLI的东西对Strimzi图像进行左移CVE扫描

潘衡
2023-03-14

到目前为止,我对自己的解决方案还不太满意,所以我将发布我自己的答案。我的目标:

  1. Pod 仍应尽可能通过有状态集进行动态管理。
  2. 为每个 Pod 创建一个外部服务(即 Kafka Broker),用于生产者/消费者客户端,并避免负载平衡。
  3. 创建内部无外设服务,以便每个 Broker 可以相互通信。

从Yolean/kubernetes-kafka开始,唯一缺少的是在外部公开服务和这样做的两个挑战。

  1. 为每个Broker pod生成唯一的标签,以便我们可以为每个Brokerpod创建外部服务
  2. 告诉经纪人使用内部服务相互通信,同时配置Kafka以告诉生产者/消费者通过外部服务通信

每个pod标签和外部服务:

为了生成每个pod的标签,这个问题非常有用。以此为指导,我们将以下代码行添加到10 broker-config . yml < code > init . sh 属性中:

kubectl label pods ${HOSTNAME} kafka-set-component=${HOSTNAME}

我们保留了现有的无外设服务,但我们也使用标签为每个 Pod 生成一个外部服务(我将它们添加到 20dns.yml):

apiVersion: v1
kind: Service
metadata:
  name: broker-0
   namespace: kafka
spec:
  type: NodePort
  ports:
  - port: 9093
    nodePort: 30093
selector:
  kafka-set-component: kafka-0

使用内部/外部侦听器配置Kafka

我发现这个问题非常有助于理解如何配置Kafka。

这同样需要使用以下内容更新10broker-config.yml中的< code>init.sh和< code>server.properties属性:

将以下内容添加到服务器。属性更新安全协议(当前使用PLAINTEXT):

listener.security.protocol.map=INTERNAL_PLAINTEXT:PLAINTEXT,EXTERNAL_PLAINTEXT:PLAINTEXT
inter.broker.listener.name=INTERNAL_PLAINTEXT

init.sh中动态确定每个Pod的外部IP和外部端口:

EXTERNAL_LISTENER_IP=<your external addressable cluster ip>
EXTERNAL_LISTENER_PORT=$((30093 + ${HOSTNAME##*-}))

然后为EXTERNAL_LISTENERINTERNAL_LISTENER配置侦听器advertised.listenersIP(也在init.sh属性中):

sed -i "s/#listeners=PLAINTEXT:\/\/:9092/listeners=INTERNAL_PLAINTEXT:\/\/0.0.0.0:9092,EXTERNAL_PLAINTEXT:\/\/0.0.0.0:9093/" /etc/kafka/server.properties
sed -i "s/#advertised.listeners=PLAINTEXT:\/\/your.host.name:9092/advertised.listeners=INTERNAL_PLAINTEXT:\/\/$HOSTNAME.broker.kafka.svc.cluster.local:9092,EXTERNAL_PLAINTEXT:\/\/$EXTERNAL_LISTENER_IP:$EXTERNAL_LISTENER_PORT/" /etc/kafka/server.properties

显然,这不是生产的完整解决方案(例如解决外部暴露的代理的安全性),我仍在完善我对如何让内部生产者/消费者也与代理沟通的理解。

然而,到目前为止,这是我理解库伯内特斯和Kafka的最好方法。

龚盛
2023-03-14

我们在1.7中通过将headless服务更改为Type=NodePort并设置externalTrafficPolicy=Local来解决这个问题。这将绕过服务的内部负载平衡,而发往该节点端口上特定节点的流量仅在该节点上有Kafka pod时才有效。

apiVersion: v1
kind: Service
metadata:
  name: broker
spec:
  externalTrafficPolicy: Local
  ports:
  - nodePort: 30000
    port: 30000
    protocol: TCP
    targetPort: 9092
  selector:
    app: broker
  type: NodePort

例如,我们有两个节点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中也作为测试版注释提供,更多信息请参见特性可用性:https://kubernetes . io/docs/tasks/access-application-cluster/create-external-load-balancer/# preserving-the-client-source-IP

另请注意,虽然这会将 kafka 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

 类似资料:
  • 我正在公有云(Azure/AWS/Google cloud)中运行一个Kubernetes集群,我有一些非HTTP服务要为用户公开。 对于HTTP服务,Id通常使用Ingress资源通过可寻址的DNS条目公开地公开该服务。 对于非HTTP、基于TCP的服务(例如,诸如PostgreSQL之类的数据库),我应该如何公开这些服务以供公共使用? 我考虑过使用服务,但这要求节点本身可以公开访问(依赖路由到

  • $kubectl版本 客户端版本:Version.info{Major:“1”,Minor:“13”,GitVersion:“V1.13.3”,GitCommit:“721BFA751924DA8D1680787490C54B9179B1FED0”,GitTreEstate:“Clean”,BuildDate:“2019-02-01T20:08:12Z”,GoVersion:“Go1.11.5”,

  • 名称:示例-服务 命名空间:默认 标签:run=load-balancer-example 注释: 选择器:run=load-balancer-example 类型:nodeport IP:10.108.214.162 端口:9090/tcp 目标端口:9090/tcp 节点端口:31105/tcp endpoint:192.168.1.23:9090,192.168.1.24:9090 会话关联

  • 我是Kubernetes的新手,想了解如何向外部世界公开在Kubernetes中运行的服务。我已经使用集群上的节点端口公开了它。例如:一个服务在主机上公开端口31234,我可以通过https://kubeserverip:31234从另一个服务器访问该服务。

  • 它显示无头服务是在豆荚顶部创建的。我没有办法强制连接到第一个吊舱或吊舱0或第二个吊舱,即吊舱1。

  • 我在Google Container Engine上部署了一个容器,它运行良好。现在,我想公开它。 这个应用程序是一个侦听2个端口的服务。使用kubectl公开部署,我创建了2个负载均衡器,每个端口一个。 我制作了两个负载平衡器,因为kubectl expose命令似乎不允许使用多个端口。虽然我在kubectl上将其定义为type=LoadBalancer,但一旦在GKE上创建了它们,它们就被定义