新钛云服已为您服务1491天
在本文中,我们将了解 Kafka 的架构以及它如何通过复制分区支持高可用性。然后,我们也可以自己实现一个 Kafka 集群以使用标准 Kubernetes 资源实现高可用性,并了解它是如何做到高可用的,从而避免单点故障问题。
在最简单的基础环境中,Kafka 的架构由单个 Broker 服务器及其作为客户端的生产者和消费者组成。
· 生产者创建记录并将它们发布到 Kafka 代理。
· 消费者从broker节点消费记录。
虽然这个 Kafka 集群可以支持典型的 Kafka 用例,但对于大多数实际情况来说它还是过于简单了。
首先,让我们解释一下常见的相关专业术语。
· Broker:消息中间件处理节点;每个Kafka服务节点称之为一个Broker,一个Kafka集群由一个或多个Broker组成。
· Topic:一类特定数据集合的统称;可类比DB中Table的概念;逻辑概念。
· Producer:消息的生产者,向Broker发送消息的客户端。
· Consumer:消息的消费者,向Broker读取消息的客户端。
· Consumer Group:每一个Consumer隶属于一个特定的Consumer Group,一条消息可以被不同Group中的Consumer消费,但同一Group内的消息只能被一个Consumer消费。
· Partition:是对Topic中所包含数据集的物理分区;物理概念。
· Replication:副本集;是Kafka高可用的一种保障机制。
Kafka 通常作为一个由三个或更多brokers代理组成的集群运行,这些brokers可以跨越多个数据中心或云区域。
这种集群架构支持对可扩展性、一致性、可用性、分区容错性和性能的需求。
在本文中,我们的学习目标是探索 Kafka 在 Kubernetes 上的可用性。
在此,特别说明,我们将独立设计一个 高可用的基于kubernetes的Kafka 集群:
1、优先考虑可用性而不是一致性,这是大家可能希望为实时指标收集等用例做出的权衡,在这种情况下,如果发生故障,写入新数据的可用性比丢失一些历史数据点更重要。(https://en.wikipedia.org/wiki/CAP_theorem)
2、优先选择简单而不是其他非功能性需求(例如安全性、性能、效率等),从而更专注于 Kafka 和 Kubernetes。
3、本文也假设维护服务的计划外中断比基础设施故障更有可能发生。
考虑到这些因素,让我们首先讨论一个典型的高可用性 Kafka 集群——运行于主机上而非运行于Kubernetes集群中。
Kafka partitions 与 replication-factor
在 Kafka 中,消息被分类为topics,在集群中,每个topic都有一个唯一的名称。
例如,如果您构建一个聊天应用程序,您可能会为每个聊天室设置一个topic(例如“dave-tom-chat”)。
但是当消息的数量超过broker限制的大小时会发生什么?
Topic被分解成多个分区,每个分区都可以存在于 Kafka 集群中的一个单独节点上。
换句话说,来自单个topic的所有消息可以存储在不同的broker中,但来自单个分区的所有消息只能在同一个节点上找到。
· 如果一个topic包含所有消息,当设备上没有空间时它是如何工作的?
· Kafka 使用分区将记录分发给多个broker。
· 每个topic可以有不同数量的分区。来自单个分区的所有记录始终一起存储在节点上。
这种设计选择支持topic的并行化、可扩展性和高消息吞吐量。
下面还有更多内容,让我们继续往下看。
Topic配置了一个replication factor,它决定了每个分区的副本数。
如果一个集群只有一个topic和一个分区,则replication factor为 3 意味着存在三个分区:每个分区一个副本。
分区的所有副本都存在于不同的broker上,因此您不能配置比集群中的节点多的分区副本。
在前面的示例中,replication factor为 3,您应该期望 Kafka 集群中至少有三个节点。
但是 Kafka 如何让这些副本保持同步呢?
分区被区分成leader和follower角色,其中分区leader处理所有写入和读取,follower纯粹用于故障转移。
follower可以与leader同步(包含所有分区leader的消息,除了缓冲区窗口中的消息)或不同步。
所有同步副本的集合称为 ISR(in-sync replicas)。
这些是 Kafka 和replications的基础;让我们看看故障时会发生什么。
了解brokers故障
假设 Kafka 集群有 3 个broker,replication factor为 1。
集群中只有一个topic和一个分区。
当 broker 不可用时,分区也不可用,集群无法为消费者或生产者提供服务。
让我们通过将replication factor设置为 3 来改变它。
在这种情况下,每个broker都有一个分区的副本。
当broker不可用时会发生什么?
如果分区有额外的同步副本,其中一个将成为临时分区leader。
集群可以照常运行,消费者或生产者没有停机时间。
一个所有分区同步的 Kafka 集群丢失了一个broker。
两个分区中的一个将被提升为leader,集群将继续照常运行。
当有分区副本但它们不同步时怎么办?
在这种情况下,有两种选择:
选择等待分区leader重新上线——牺牲可用性。
允许不同步的副本成为临时分区leader——牺牲一致性。
分区不同步的 Kafka 集群失去了一个broker。
集群可以将其中一个不同步的副本提升为leader。但是,您可能会丢失一些消息。
或者,您可以等待broker恢复,但是,这样影响您服务的可用性。
上面我们已经讨论了一些失败场景,下面让我们看看如何处理它们。
如何解决或者减轻常见故障
您可能注意到一个分区应该有一个额外的同步副本 (ISR) 可用以在分区leader丢失后幸存下来。
因此,一个简单的集群大小至少得有两个最小同步副本大小为 2 的broker。
然而,这还不够。
如果你只有两个副本,然后失去了一个broker,同步副本大小会减少到 1,生产者和消费者都无法工作(即最小同步副本为 2)。
因此,broker的数量应该大于最小同步副本大小(即至少 3 个)。
您可以设置一个只有两个broker且最小同步副本大小为 2 的 Kafka 集群。
但是,当broker丢失时,集群将变得不可用,因为单个副本处于同步状态。
您应该配置一个 Kafka 集群,该集群的broker数量大于同步副本的大小。
在这种情况下,如果一个 broker 丢失,Kafka 集群仍然可以继续运行。
那你又该如何规划broker的位置了?
考虑到大都使用云服务托管的 Kafka 集群,因此最好在故障域(例如区域、区域、节点等)之间分布broker节点。
因此,如果您希望设计一个可以容忍一次计划内和一次计划外故障的 Kafka 集群,您至少应该考虑以下要求:
· 最少 2 个同步副本。
· topic的replication factor为 3。
· 至少 3 个 Kafka broker,每个broker运行在不同的节点上。
· 节点分布在三个可用区。
在本文的剩余部分,您将在 Kubernetes 上构建和故障测试 Kafka 集群以验证这些假设。
在 Kubernetes 上部署 3 节点 Kafka 集群
让我们创建一个跨三个可用区的三节点集群:
$ k3d cluster create kube-cluster \
--agents 3 \
--k3s-node-label topology.kubernetes.io/zone=zone-a@agent:0 \
--k3s-node-label topology.kubernetes.io/zone=zone-b@agent:1 \
--k3s-node-label topology.kubernetes.io/zone=zone-c@agent:2
INFO[0000] Created network 'k3d-kube-cluster'
INFO[0000] Created image volume k3d-kube-cluster-imagesINFO[0000] Starting new tools node...INFO[0001] Creating node 'k3d-kube-cluster-server-0'
INFO[0003] Starting Node 'k3d-kube-cluster-tools'
INFO[0012] Creating node 'k3d-kube-cluster-agent-0'
INFO[0012] Creating node 'k3d-kube-cluster-agent-1'
INFO[0012] Creating node 'k3d-kube-cluster-agent-2'
INFO[0012] Creating LoadBalancer 'k3d-kube-cluster-serverlb'
INFO[0017] Starting new tools node...INFO[0017] Starting Node 'k3d-kube-cluster-tools'
INFO[0018] Starting cluster 'kube-cluster'
INFO[0018] Starting servers...INFO[0018] Starting Node 'k3d-kube-cluster-server-0'
INFO[0022] Starting agents...INFO[0022] Starting Node 'k3d-kube-cluster-agent-1'
INFO[0022] Starting Node 'k3d-kube-cluster-agent-0'
INFO[0022] Starting Node 'k3d-kube-cluster-agent-2'
INFO[0032] Starting helpers...INFO[0032] Starting Node 'k3d-kube-cluster-serverlb'
INFO[0041] Cluster 'kube-cluster' created successfully!
您可以通过以下方式验证集群是否已准备就绪:
$ kubectl get nodes
NAME STATUS ROLES VERSION
k3d-kube-cluster-server-0 Ready control-plane,master v1.22.7+k3s1
k3d-kube-cluster-agent-1 Ready <none> v1.22.7+k3s1
k3d-kube-cluster-agent-0 Ready <none> v1.22.7+k3s1
k3d-kube-cluster-agent-2 Ready <none> v1.22.7+k3s1
接下来,让我们将 Kafka 集群部署为 Kubernetes StatefulSet。
这是一个 YAML 清单,kafka.yaml
定义了创建简单 Kafka 集群所需的资源:
apiVersion: v1
kind: Service
metadata:
name: kafka-svc
labels:
app: kafka-app
spec:
clusterIP: None
ports:
- name: '9092'
port: 9092
protocol: TCP
targetPort: 9092
selector:
app: kafka-app
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kafka
labels:
app: kafka-app
spec:
serviceName: kafka-svc
replicas: 3
selector:
matchLabels:
app: kafka-app
template:
metadata:
labels:
app: kafka-app
spec:
containers:
- name: kafka-container
image: doughgle/kafka-kraft
ports:
- containerPort: 9092
- containerPort: 9093
env:
- name: REPLICAS
value: '3'
- name: SERVICE