使用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
公开外部服务?
答案 0 :(得分:22)
我们已在1.7中通过将无头服务更改为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与特定的外部网络标识绑定,但并不能保证您的存储卷与该网络标识相关联。如果您在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
下,用于将卷数据绑定到特定节点。 / p>
https://github.com/kubernetes/features/issues/121 https://kubernetes.io/docs/concepts/storage/volumes/#local
答案 1 :(得分:18)
到目前为止,解决方案对我自己来说还不够令人满意,所以我将发布自己的答案。我的目标:
从Yolean/kubernetes-kafka开始,唯一缺少的是外部公开服务以及这样做的两个挑战。
每个广告连播标签和外部服务:
要为每个广告单元生成标签,this issue确实非常有用。使用它作为指南,我们将以下行添加到10broker-config.yml 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
我发现this issue非常有用,可以帮助您了解如何配置Kafka。
这又要求使用以下内容更新10broker-config.yml中的init.sh
和server.properties
属性:
将以下内容添加到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##*-}))
然后为listeners
和advertised.listeners
(也在EXTERNAL_LISTENER
属性中)配置INTERNAL_LISTENER
和init.sh
IP:
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
显然,这不是一个完整的生产解决方案(例如解决外部暴露经纪人的安全问题),而且我仍然在理解如何让内部生产者/消费者也与经纪人沟通。
然而,到目前为止,这是我理解Kubernetes和Kafka的最佳方法。
答案 2 :(得分:11)
我想说的是,在尝试围绕无头服务是什么/它们的目的是什么之前,我已经阅读了3遍问答。 (而且我从来没有完全理解无头服务,或者这是什么问答环节。)
经过四遍阅读(在进一步学习后重新审视),它终于被点击/我终于明白了。
因此,此答案的目的是重申Nadir的问题/问题/答案,就像向一年级学生解释该问题一样。这样一来,那些偶然发现它的人就会在第一次阅读时获得Nadir出色解决方案的意义。
有用的背景知识:
存在类型为:外部名称的服务。
ExternalName
服务只是指向DNS地址。
有2种口味
外部名称服务:
有状态集的身份包含3个部分:
关于Kube-Proxy,要记住3件事:
关于无头服务,需要记住以下四件事:
现在,我们对问题有了更好的了解,让我们回到一个问题:无头服务(指向有状态集合的单个成员)如何在外部公开?
解决方案第1部分:
集群中的任何Pod都可以与状态集的成员进行对话。
因为有状态生成无头服务,且其内部群集DNS地址的形式可预测:
statefulsetname-#。associatedheadlessservice.namespace.svc.cluster.local:port
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,也可以用来指代有空的人。
解决方案第2部分:
通过引入可以接受外部流量的第二项服务,然后将流量从该服务重定向到无头服务,您可以允许外部流量与有状态集合的成员进行通信只能接受互联网流量。
为有状态集中的每个Pod创建一个类型为ExternalName的服务,该服务具有由Kube-Proxy管理的虚拟静态ClusterIP地址。这些外部名称服务中的每一个都将流量指向/重定向到解决方案1中标识的可预测的静态内部群集DNS地址,并且由于此外部名称服务具有通过Kube-Proxy管理的虚拟静态ClusterIP,因此可能存在从NodePorts到其的映射。
答案 3 :(得分:2)
将服务从无头ClusterIP更改为NodePort,NodePort将请求转发到设置端口(在我的示例中为30092)上的任何节点到Kafkas上的端口9042。你可以随机点击其中一个pod,但我想这很好。
20dns.yml变成(像这样):
# A no longer headless service to create DNS records
---
apiVersion: v1
kind: Service
metadata:
name: broker
namespace: kafka
spec:
type: NodePort
ports:
- port: 9092
- nodePort: 30092
# [podname].broker.kafka.svc.cluster.local
selector:
app: kafka
免责声明:您可能需要两项服务。一个是内部dns名称的无头,另一个是外部访问的NodePort。我没有试过这个我自己。
答案 4 :(得分:1)
来自kubernetes kafka documentation:
通过主机端口进行外部访问
一种替代方法是使用主机端口进行外部访问。什么时候 使用这个,每个主机上只能运行一个kafka代理,这很好 反正这个想法。
要切换到主机端口,kafka广告地址必须为 切换到运行该节点的节点的ExternalIP或ExternalDNS名称 经纪人。在kafka / 10broker-config.yml中切换到
OUTSIDE_HOST=$(kubectl get node "$NODE_NAME" -o jsonpath='{.status.addresses[?(@.type=="ExternalIP")].address}') OUTSIDE_PORT=${OutsidePort}
,然后在kafka / 50kafka.yml中添加主机端口:
- name: outside containerPort: 9094 hostPort: 9094
答案 5 :(得分:0)
我通过为每个代理创建单独的statefulset和为每个代理创建单独的NodePort类型的服务来解决此问题。内部通信可以在每个单独的服务名称上进行。外部通信可以在NodePort地址上进行。