确定 Kubernetes 中 Kafka 集群的适当大小

笔记。 翻译。:在本文中,Banzai Cloud 分享了一个示例,说明如何使用其自定义工具使 Kafka 在 Kubernetes 中更易于使用。 以下说明说明了如何确定基础设施的最佳规模并配置 Kafka 本身以实现所需的吞吐量。

确定 Kubernetes 中 Kafka 集群的适当大小

Apache Kafka 是一个分布式流平台,用于创建可靠、可扩展和高性能的实时流系统。 其令人印象深刻的功能可以使用 Kubernetes 进行扩展。 为此我们开发了 开源 Kafka 算子 和一个名为 超级管。 它们允许您在 Kubernetes 上运行 Kafka 并使用其各种功能,例如微调代理配置、基于指标的重新平衡扩展、机架感知、“软” (优美) 推出更新等

在您的集群中尝试 Supertubes:

curl https://getsupertubes.sh | sh и supertubes install -a --no-democluster --kubeconfig <path-to-eks-cluster-kubeconfig-file>

或联系 文件资料。 您还可以了解 Kafka 的一些功能,它的工作是使用 Supertubes 和 Kafka 运算符实现自动化的。 我们已经在博客上写过它们:

当您决定在 Kubernetes 上部署 Kafka 集群时,您可能会面临确定底层基础设施的最佳大小以及需要微调 Kafka 配置以满足吞吐量要求的挑战。 每个代理的最大性能由底层基础设施组件的性能决定,例如内存、处理器、磁盘速度、网络带宽等。

理想情况下,代理配置应使所有基础设施元素都能发挥其最大功能。 然而,在现实生活中,这种设置相当复杂。 用户更有可能配置代理以最大限度地利用一两个组件(磁盘、内存或处理器)。 一般来说,当代理的配置允许最慢的组件得到最大程度的使用时,代理就会显示出最大的性能。 这样我们就可以粗略地了解一个代理可以处理的负载。

理论上,我们还可以估计处理给定负载所需的代理数量。 然而,实际上,不同级别的配置选项如此之多,以至于评估特定配置的潜在性能非常困难(如果不是不可能)。 换句话说,根据某些给定的性能来规划配置是非常困难的。

对于Supertubes用户,我们通常采取以下方法:我们从一些配置(基础设施+设置)开始,然后测量其性能,调整经纪人设置并再次重复该过程。 这种情况会一直发生,直到基础设施中最慢的组件被充分利用为止。

这样,我们就更清楚地知道一个集群需要多少个broker来处理一定的负载(broker的数量还取决于其他因素,比如保证弹性的最小消息副本数、分区数量等)领导等)。 此外,我们还深入了解哪些基础设施组件需要垂直扩展。

本文将讨论我们为充分利用初始配置中最慢的组件并测量 Kafka 集群的吞吐量而采取的步骤。 高弹性配置需要至少三个正在运行的代理(min.insync.replicas=3),分布在三个不同的可访问区域。 为了配置、扩展和监控 Kubernetes 基础设施,我们使用自己的混合云容器管理平台 - 管道。 它支持本地部署(裸机、VMware)和五种类型的云(阿里巴巴、AWS、Azure、Google、Oracle)以及它们的任意组合。

关于Kafka集群基础设施和配置的思考

对于下面的示例,我们选择 AWS 作为云提供商,选择 EKS 作为 Kubernetes 发行版。 可以使用类似的配置来实现 P.K.E. - Banzai Cloud 的 Kubernetes 发行版,经 CNCF 认证。

磁盘

亚马逊提供各种 EBS 卷类型. 在核心 gp2 и io1 不过,有 SSD 驱动器可确保高吞吐量 gp2 消耗累积积分 (I/O 积分),所以我们更喜欢这种类型 io1,提供一致的高吞吐量。

实例类型

Kafka 的性能高度依赖于操作系统的页面缓存,因此我们需要为代理(JVM)和页面缓存提供足够内存的实例。 实例 c5.2xlarge - 一个好的开始,因为它有 16 GB 内存 优化以与 EBS 配合使用。 它的缺点是每30小时只能提供不超过24分钟的最大性能。 如果您的工作负载需要在较长时间内达到峰值性能,您可能需要考虑其他实例类型。 这正是我们所做的,停在 c5.4xlarge。 它提供最大吞吐量 593,75兆比特/秒。 EBS卷的最大吞吐量 io1 高于实例 c5.4xlarge,因此基础设施中最慢的元素可能是该实例类型的 I/O 吞吐量(我们的负载测试也应该确认这一点)。

Сеть

与虚拟机实例和磁盘的性能相比,网络吞吐量必须足够大,否则网络将成为瓶颈。 在我们的例子中,网络接口 c5.4xlarge 支持高达 10 Gb/s 的速度,明显高于 VM 实例的 I/O 吞吐量。

代理部署

Brokers 应部署(在 Kubernetes 中调度)到专用节点,以避免与其他进程竞争 CPU、内存、网络和磁盘资源。

爪哇版

合理的选择是 Java 11,因为它与 Docker 兼容,因为 JVM 可以正确确定运行代理的容器可用的处理器和内存。 知道 CPU 限制很重要,JVM 在内部透明地设置 GC 线程和 JIT 线程的数量。 我们使用了 Kafka 图像 banzaicloud/kafka:2.13-2.4.0,其中包括 Java 2.4.0 上的 Kafka 版本 2.13 (Scala 11)。

如果您想了解有关 Kubernetes 上的 Java/JVM 的更多信息,请查看我们的以下文章:

经纪商内存设置

配置代理内存有两个关键方面:JVM 和 Kubernetes Pod 的设置。 为 pod 设置的内存限制必须大于最大堆大小,以便 JVM 为驻留在其自身内存中的 Java 元空间以及 Kafka 主动使用的操作系统页面缓存提供空间。 在我们的测试中,我们启动了带参数的 Kafka 代理 -Xmx4G -Xms2G,并且 Pod 的内存限制为 10 Gi。 请注意,JVM 的内存设置可以使用以下命令自动获取 -XX:MaxRAMPercentage и -X:MinRAMPercentage,基于 Pod 的内存限制。

经纪商处理器设置

一般来说,可以通过增加Kafka使用的线程数来提高并行度来提高性能。 可用于 Kafka 的处理器越多越好。 在我们的测试中,我们一开始限制为 6 个处理器,并逐渐(通过迭代)将其数量增加到 15 个。此外,我们设置 num.network.threads=12 在代理设置中增加从网络接收数据并发送数据的线程数量。 他们立即发现追随者经纪人无法足够快地接收副本,他们提出了 num.replica.fetchers 到 4 以提高追随者经纪人从领导者那里复制消息的速度。

负载生成工具

您应该确保在 Kafka 集群(正在进行基准测试)达到其最大负载之前,所选负载生成器不会耗尽容量。 也就是说,需要对负载生成工具的能力进行初步评估,并为其选择具有足够数量的处理器和内存的实例类型。 在这种情况下,我们的工具将产生超过 Kafka 集群可以处理的负载。 经过多次实验,我们决定三本 c5.4xlarge,每个都有一台发电机在运行。

基准测试

绩效衡量是一个迭代过程,包括以下阶段:

  • 设置基础设施(EKS集群、Kafka集群、负载生成工具以及Prometheus和Grafana);
  • 生成一定周期的负载,过滤收集到的性能指标中的随机偏差;
  • 根据观察到的绩效指标调整经纪商的基础设施和配置;
  • 重复该过程,直到达到 Kafka 集群吞吐量所需的水平。 同时,它必须具有一致的可重复性,并表现出最小的吞吐量变化。

下一节介绍测试集群基准测试过程中执行的步骤。

工具

以下工具用于快速部署基线配置、生成负载和测量性能:

  • 万岁云管道 用于从 Amazon c 组织 EKS 集群 普罗米修斯 (收集 Kafka 和基础设施指标)和 格拉法纳 (可视化这些指标)。 我们利用了 融合的 в 管道 提供联合监控、集中日志收集、漏洞扫描、灾难恢复、企业级安全等的服务。
  • 桑格勒内尔 — 用于对 Kafka 集群进行负载测试的工具。
  • 用于可视化 Kafka 指标和基础设施的 Grafana 仪表板: 库伯内特卡夫卡, 节点导出器.
  • Supertubes CLI 是在 Kubernetes 上设置 Kafka 集群的最简单方法。 Zookeeper、Kafka Operator、Envoy 和许多其他组件均已安装并正确配置,以便在 Kubernetes 上运行生产就绪的 Kafka 集群。
    • 用于安装 超级管 CLI 使用提供的说明 这里.

确定 Kubernetes 中 Kafka 集群的适当大小

EKS集群

准备具有专用工作节点的 EKS 集群 c5.4xlarge 具有 Kafka 代理的 Pod 位于不同的可用区域,以及负载生成器和监控基础设施的专用节点。

banzai cluster create -f https://raw.githubusercontent.com/banzaicloud/kafka-operator/master/docs/benchmarks/infrastructure/cluster_eks_202001.json

EKS 集群启动并运行后,启用其集成 监控服务 — 她将把 Prometheus 和 Grafana 部署到一个集群中。

卡夫卡系统组件

使用 supertubes CLI 在 EKS 中安装 Kafka 系统组件(Zookeeper、kafka-operator):

supertubes install -a --no-democluster --kubeconfig <path-to-eks-cluster-kubeconfig-file>

卡夫卡集群

默认情况下,EKS 使用 EBS 卷类型 gp2,所以需要根据卷创建单独的存储类 io1 对于卡夫卡集群:

kubectl create -f - <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  iopsPerGB: "50"
  fsType: ext4
volumeBindingMode: WaitForFirstConsumer
EOF

设置经纪人的参数 min.insync.replicas=3 并在三个不同可用区的节点上部署 Broker Pod:

supertubes cluster create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f https://raw.githubusercontent.com/banzaicloud/kafka-operator/master/docs/benchmarks/infrastructure/kafka_202001_3brokers.yaml --wait --timeout 600

话题

我们并行运行三个负载生成器实例。 他们每个人都写自己的主题,也就是说,我们总共需要三个主题:

supertubes cluster topic create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f -<<EOF
apiVersion: kafka.banzaicloud.io/v1alpha1
kind: KafkaTopic
metadata:
  name: perftest1
spec:
  name: perftest1
  partitions: 12
  replicationFactor: 3
  retention.ms: '28800000'
  cleanup.policy: delete
EOF

supertubes cluster topic create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f -<<EOF
apiVersion: kafka.banzaicloud.io/v1alpha1
kind: KafkaTopic
metadata:
    name: perftest2
spec:
  name: perftest2
  partitions: 12
  replicationFactor: 3
  retention.ms: '28800000'
  cleanup.policy: delete
EOF

supertubes cluster topic create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f -<<EOF
apiVersion: kafka.banzaicloud.io/v1alpha1
kind: KafkaTopic
metadata:
  name: perftest3
spec:
  name: perftest3
  partitions: 12
  replicationFactor: 3
  retention.ms: '28800000'
  cleanup.policy: delete
EOF

对于每个主题,复制因子为 3 — 高可用生产系统的最小建议值。

负载生成工具

我们启动了负载生成器的三个副本(每个副本都在单独的主题中编写)。 对于负载生成器 Pod,您需要设置节点关联性,以便仅在为其分配的节点上调度它们:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: loadtest
  name: perf-load1
  namespace: kafka
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: loadtest
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: loadtest
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: nodepool.banzaicloud.io/name
                operator: In
                values:
                - loadgen
      containers:
      - args:
        - -brokers=kafka-0:29092,kafka-1:29092,kafka-2:29092,kafka-3:29092
        - -topic=perftest1
        - -required-acks=all
        - -message-size=512
        - -workers=20
        image: banzaicloud/perfload:0.1.0-blog
        imagePullPolicy: Always
        name: sangrenel
        resources:
          limits:
            cpu: 2
            memory: 1Gi
          requests:
            cpu: 2
            memory: 1Gi
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30

有几点需要注意:

  • 负载生成器生成长度为 512 字节的消息,并以 500 条消息为批量发布到 Kafka。
  • 使用参数 -required-acks=all 当 Kafka 代理收到并确认消息的所有同步副本时,发布被视为成功。 这意味着在基准测试中,我们不仅测量了领导者接收消息的速度,还测量了他们的追随者复制消息的速度。 该测试的目的不是评估消费者的阅读速度 (消费者) 最近收到的仍保留在操作系统页面缓存中的消息,及其与存储在磁盘上的消息的读取速度的比较。
  • 负载生成器并行运行 20 个工作线程(-workers=20)。 每个worker包含5个生产者,它们共享worker与Kafka集群的连接。 因此,每个生成器有 100 个生产者,它们都向 Kafka 集群发送消息。

监控集群的健康状况

在 Kafka 集群的负载测试过程中,我们还监控其运行状况,以确保没有 pod 重新启动、没有不同步的副本以及最大吞吐量和最小波动:

  • 负载生成器写入有关已发布消息数量和错误率的标准统计数据。 错误率应该保持不变 0,00%.
  • 巡航控制由 kafka-operator 部署,提供了一个仪表板,我们还可以在其中监视集群的状态。 要查看此面板,请执行以下操作:
    supertubes cluster cruisecontrol show -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file>
  • 情监侦级别 (“同步”副本的数量) 收缩和膨胀等于 0。

测量结果

3 个代理,消息大小 - 512 字节

通过将分区均匀分布在三个代理上,我们能够实现性能 ~500 Mb/s(每秒大约 990 万条消息):

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

JVM虚拟机的内存消耗不超过2GB:

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

在运行代理的所有三个实例上,磁盘吞吐量均达到最大 I/O 节点吞吐量:

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

从节点内存使用数据来看,系统缓冲和缓存占用了约 10-15 GB:

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

3 个代理,消息大小 - 100 字节

随着消息大小的减小,吞吐量会下降大约 15-20%:处理每条消息所花费的时间会对其产生影响。 此外,处理器负载几乎增加了一倍。

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

由于代理节点仍然有未使用的核心,因此可以通过更改 Kafka 配置来提高性能。 这不是一件容易的任务,因此为了提高吞吐量,最好使用更大的消息。

4 个代理,消息大小 - 512 字节

只需添加新的代理并保持分区平衡(这可确保负载在代理之间均匀分配),您就可以轻松提高 Kafka 集群的性能。 在我们的例子中,添加代理后,集群吞吐量增加到 约 580 Mb/s(约每秒 1,1 万条消息)。 增长结果低于预期:这主要是由于分区不平衡造成的(并非所有经纪商都在其能力的巅峰状态下工作)。

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

JVM 机器的内存消耗保持在 2 GB 以下:

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

具有驱动器的代理的工作受到分区不平衡的影响:

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

确定 Kubernetes 中 Kafka 集群的适当大小

发现

上面提出的迭代方法可以扩展到涵盖涉及数百个消费者、重新分区、滚动更新、pod 重新启动等的更复杂的场景。 所有这些使我们能够评估 Kafka 集群在各种条件下的能力限制,识别其运行中的瓶颈并找到解决它们的方法。

我们设计 Supertubes 是为了快速轻松地部署集群、配置集群、添加/删除代理和主题、响应警报并确保 Kafka 在 Kubernetes 上正常运行。 我们的目标是帮助您专注于主要任务(“生成”和“消费”Kafka 消息),并将所有艰苦的工作留给 Supertubes 和 Kafka 操作员。

如果您对 Banzai Cloud 技术和开源项目感兴趣,请订阅该公司: GitHub上, LinkedIn или Twitter.

译者PS

另请阅读我们的博客:

来源: habr.com

添加评论