Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Nota. traducir: En este artículo, Banzai Cloud comparte un ejemplo del uso de sus herramientas personalizadas para facilitar la ejecución de Kafka dentro de Kubernetes. Las instrucciones a continuación ilustran cómo puede determinar el tamaño óptimo de la infraestructura y ajustar Kafka para lograr el rendimiento requerido.

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Apache Kafka es una plataforma de transmisión distribuida para crear sistemas de transmisión en tiempo real confiables, escalables y de alto rendimiento. Sus impresionantes capacidades se pueden ampliar con Kubernetes. Para ello hemos desarrollado Operador Kafka de código abierto y una herramienta llamada supertubos. Le permiten ejecutar Kafka en Kubernetes y utilizar sus diversas funciones, como el ajuste fino de la configuración del intermediario, el escalado basado en métricas con reequilibrio, el conocimiento del bastidor (conocimiento de los recursos de hardware), "suave" (agraciado) implementar actualizaciones, etc.

Prueba Supertubes en tu clúster:

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

o referirse a documentación. También puede leer sobre algunas de las características de Kafka, el trabajo con el que se automatiza utilizando Supertubes y el operador Kafka. Ya hemos escrito en un blog sobre ellos:

Cuando decida implementar un clúster de Kafka en Kubernetes, es probable que enfrente el desafío de determinar el tamaño óptimo de la infraestructura subyacente y la necesidad de ajustar su configuración de Kafka para cumplir con los requisitos de rendimiento. El rendimiento máximo de cada agente está determinado por el rendimiento de los componentes de la infraestructura en su núcleo, como la memoria, el procesador, la velocidad del disco, el ancho de banda de la red, etc.

Idealmente, la configuración del intermediario debe ser tal que todos los elementos de la infraestructura se utilicen al máximo de sus capacidades. Sin embargo, en la vida real, tal configuración es bastante complicada. Es más probable que los usuarios configuren los intermediarios de manera que maximicen el uso de uno o dos componentes (disco, memoria o procesador). En términos generales, un bróker muestra su máximo rendimiento cuando su configuración permite utilizar el componente más lento "al máximo". Entonces podemos tener una idea aproximada de la carga que puede manejar un corredor.

Teóricamente, también podemos estimar la cantidad de intermediarios necesarios para trabajar con una carga determinada. Sin embargo, en la práctica, hay tantas opciones de ajuste en varios niveles que es muy difícil (si no imposible) evaluar el rendimiento potencial de una determinada configuración. En otras palabras, es muy difícil planificar una configuración basada en un rendimiento dado.

Para los usuarios de Supertubes, generalmente tomamos el siguiente enfoque: comenzamos con alguna configuración (infraestructura + configuración), luego medimos su rendimiento, ajustamos la configuración del intermediario y repetimos el proceso nuevamente. Esto sucede hasta que se explota por completo el potencial del componente más lento de la infraestructura.

De esta forma, tenemos una idea más clara de cuántos intermediarios necesita un clúster para manejar una determinada carga (la cantidad de intermediarios también depende de otros factores, como la cantidad mínima de réplicas de mensajes para garantizar la resiliencia, la cantidad de partición líderes, etc). Además, nos hacemos una idea de qué componente de infraestructura es deseable para el escalado vertical.

Este artículo se centrará en los pasos que tomamos para "exprimir todo" de los componentes más lentos en las configuraciones iniciales y medir el rendimiento de un clúster de Kafka. Una configuración altamente resistente requiere al menos tres intermediarios en funcionamiento (min.insync.replicas=3) distribuidos en tres zonas de disponibilidad diferentes. Para configurar, escalar y monitorear la infraestructura de Kubernetes, usamos nuestra propia plataforma de administración de contenedores para nubes híbridas: Tubería. Admite on-premise (bare metal, VMware) y cinco tipos de nubes (Alibaba, AWS, Azure, Google, Oracle), así como cualquier combinación de ellas.

Reflexiones sobre la infraestructura y la configuración de un clúster de Kafka

Para los ejemplos a continuación, hemos elegido AWS como proveedor de la nube y EKS como la distribución de Kubernetes. Se puede implementar una configuración similar usando P.K.E. es una distribución de Kubernetes certificada por CNCF de Banzai Cloud.

disco

Amazon ofrece varios Tipos de volumen de EBS... En el corazon de gp2 и io1 Sin embargo, mienten las unidades SSD para garantizar un alto rendimiento gp2 consume créditos acumulados (créditos de E/S), por lo que preferimos el tipo io1, que ofrece un alto rendimiento estable.

Tipos de instancia

El rendimiento de Kafka depende en gran medida de la memoria caché de la página del sistema operativo, por lo que necesitamos instancias con suficiente memoria para los intermediarios (JVM) y la memoria caché de la página. Instancia c5.2xgrande - un buen comienzo, porque tiene 16 GB de memoria y optimizado para EBS. Su desventaja es que es capaz de proporcionar el máximo rendimiento durante no más de 30 minutos cada 24 horas. Si su carga de trabajo requiere el máximo rendimiento durante un período de tiempo más prolongado, debe considerar otros tipos de instancias. Esto es exactamente lo que hicimos, centrándonos en c5.4xgrande. Proporciona el máximo rendimiento en 593,75 Mb / s. Ancho de banda máximo de volumen de EBS io1 superior a la instancia c5.4xgrande, por lo que la pieza de infraestructura más lenta parece ser el rendimiento de E/S de este tipo de instancia (que nuestras pruebas de carga también deberían confirmar).

Red

El rendimiento de la red debe ser lo suficientemente grande en comparación con el rendimiento de la instancia de VM y el disco; de lo contrario, la red se convierte en un cuello de botella. En nuestro caso, la interfaz de red. c5.4xgrande admite velocidades de hasta 10 Gb/s, que es significativamente más alta que el rendimiento de E/S de una instancia de VM.

Implementación de intermediarios

Los intermediarios deben implementarse (programarse en Kubernetes) en nodos dedicados para evitar conflictos con otros procesos por recursos de CPU, memoria, red y disco.

Versión Java

Java 11 es la opción lógica, ya que es compatible con Docker en el sentido de que la JVM detecta correctamente los procesadores y la memoria disponibles para el contenedor en el que se ejecuta el intermediario. Sabiendo que los límites de la CPU son importantes, la JVM establece interna y transparentemente la cantidad de subprocesos GC y subprocesos del compilador JIT. Usamos la imagen de Kafka banzaicloud/kafka:2.13-2.4.0, que incluye Kafka 2.4.0 (Scala 2.13) en Java 11.

Si desea obtener más información sobre Java/JVM en Kubernetes, consulte nuestras publicaciones a continuación:

Configuración de la memoria del intermediario

Hay dos aspectos clave para configurar la memoria de un intermediario: la configuración de la JVM y la configuración del pod de Kubernetes. El límite de memoria establecido para un pod debe ser mayor que el tamaño máximo de almacenamiento dinámico para permitir que la JVM tenga espacio para el metaespacio de Java que reside en su propia memoria y para la memoria caché de la página del sistema operativo que Kafka usa activamente. En nuestras pruebas, lanzamos corredores Kafka con parámetros -Xmx4G -Xms2G, y el límite de memoria para el pod era 10 Gi. Tenga en cuenta que la configuración de memoria para la JVM se puede obtener automáticamente usando -XX:MaxRAMPercentage и -X:MinRAMPercentage, según el límite de memoria del pod.

Configuración del procesador del intermediario

En términos generales, puede mejorar el rendimiento aumentando la simultaneidad aumentando la cantidad de subprocesos utilizados por Kafka. Cuantos más procesadores haya disponibles para Kafka, mejor. En nuestra prueba, comenzamos con un límite de 6 procesadores y gradualmente (en iteraciones) aumentamos su número a 15. Además, establecimos num.network.threads=12 en la configuración del intermediario para aumentar la cantidad de flujos que reciben datos de la red y los envían. Habiendo descubierto de inmediato que los corredores seguidores no podían recibir réplicas lo suficientemente rápido, planteó num.replica.fetchers a 4 para aumentar la velocidad a la que los intermediarios seguidores replicaron los mensajes de los líderes.

Herramienta de generación de carga

Debe asegurarse de que el potencial del generador de carga seleccionado no se agote antes de que el clúster de Kafka (que se está evaluando) alcance su carga máxima. En otras palabras, es necesario realizar una evaluación preliminar de las capacidades de la herramienta de generación de carga y también seleccionar tipos de instancias para ella con una cantidad suficiente de procesadores y memoria. En este caso, nuestra herramienta producirá más carga de la que puede manejar el clúster de Kafka. Después de muchos experimentos, nos decidimos por tres copias. c5.4xgrande, en cada uno de los cuales se puso en marcha el generador.

Benchmarking

La medición del desempeño es un proceso iterativo que incluye los siguientes pasos:

  • configuración de infraestructura (clúster EKS, clúster Kafka, herramienta de generación de carga, así como Prometheus y Grafana);
  • generar carga durante un período para filtrar fluctuaciones aleatorias en las métricas de rendimiento recopiladas;
  • ajustar la infraestructura y la configuración del corredor en función de los indicadores de rendimiento observados;
  • repitiendo el proceso hasta alcanzar el nivel de rendimiento requerido del clúster de Kafka. Al mismo tiempo, debe ser reproducible de forma estable y mostrar variaciones mínimas de rendimiento.

La siguiente sección describe los pasos que se tomaron durante el proceso de evaluación comparativa del clúster de prueba.

Instrumentos

Las siguientes herramientas se utilizaron para implementar rápidamente una configuración básica, generación de carga y medición del rendimiento:

  • Canalización en la nube de Banzai para organizar un clúster EKS de Amazon c Prometeo (para recopilar Kafka y métricas de infraestructura) y Grafana (para visualizar estas métricas). aprovechamos integrado в Tubería servicios que brindan monitoreo federado, registro centralizado, escaneo de vulnerabilidades, recuperación ante desastres, seguridad de nivel empresarial y más.
  • Sangrenel es una herramienta para realizar pruebas de carga en un clúster de Kafka.
  • Tableros de Grafana para visualizar las métricas y la infraestructura de Kafka: Kafka de Kubernetes, Exportador de nodos.
  • Supertubes CLI para la forma más fácil de configurar un clúster de Kafka en Kubernetes. Zookeeper, el operador de Kafka, Envoy y muchos otros componentes están instalados y configurados correctamente para ejecutar un clúster de Kafka listo para producción en Kubernetes.
    • Para la instalacion supertubos CLI utilice las instrucciones proporcionadas aquí.

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Clúster EKS

Aprovisione un clúster de EKS con nodos de trabajo dedicados c5.4xgrande en diferentes zonas de disponibilidad para pods con brokers Kafka, así como nodos dedicados para el generador de carga y la infraestructura de monitoreo.

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

Una vez que el clúster de EKS esté en funcionamiento, encienda su integrado servicio de vigilancia - desplegará Prometheus y Grafana en un clúster.

Componentes del sistema Kafka

Instale los componentes del sistema Kafka (Zookeeper, kafka-operator) en EKS usando la CLI de supertubos:

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

Clúster de Kafka

De forma predeterminada, EKS utiliza volúmenes de EBS de tipo gp2, por lo que debe crear una clase de almacenamiento separada basada en volúmenes io1 para el clúster de Kafka:

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

Establecer el parámetro de intermediarios min.insync.replicas=3 e implemente pods de agentes en nodos en tres zonas de disponibilidad diferentes:

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

Temas

Ejecutamos tres instancias del generador de carga en paralelo. Cada uno de ellos escribe en su propio tema, es decir, necesitamos tres temas en total:

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

Para cada tema, el factor de replicación es 3, el valor mínimo recomendado para sistemas de producción de alta disponibilidad.

Herramienta de generación de carga

Ejecutamos tres instancias del generador de carga (cada una escrita en un tema separado). Para los pods de generadores de carga, debe especificar la afinidad de los nodos para que se programen solo en los nodos asignados para ellos:

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

Algunos puntos a los que prestar atención:

  • El generador de carga genera mensajes de 512 bytes y los publica en Kafka en lotes de 500 mensajes.
  • con un argumento -required-acks=all Una publicación se considera exitosa cuando los agentes de Kafka han recibido y confirmado todas las réplicas de mensajes sincronizados. Esto significa que en el benchmark medimos no solo la velocidad de los líderes que reciben mensajes, sino también la de sus seguidores que replican mensajes. El propósito de esta prueba no es evaluar la velocidad de lectura de los consumidores. (consumidores) mensajes recibidos recientemente que todavía están en el caché de la página del sistema operativo y su comparación con la velocidad de lectura de los mensajes almacenados en el disco.
  • El generador de carga hace funcionar a 20 trabajadores en paralelo (-workers=20). Cada trabajador contiene 5 productores que comparten la conexión del trabajador con el clúster de Kafka. Como resultado, cada generador tiene 100 productores y todos envían mensajes al clúster de Kafka.

Supervisar el estado del clúster

Durante la prueba de carga del clúster de Kafka, también supervisamos su estado para asegurarnos de que no hubiera reinicios de pod, réplicas no sincronizadas y rendimiento máximo con fluctuaciones mínimas:

  • El generador de carga escribe estadísticas estándar sobre la cantidad de mensajes publicados y la tasa de error. El porcentaje de errores debe permanecer en el valor. 0,00%.
  • Cruise Control, implementado por kafka-operator, proporciona un tablero donde también podemos observar el estado del clúster. Para ver este panel, ejecute:
    supertubes cluster cruisecontrol show -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file>
  • nivel de ISR (número de réplicas "sincronizadas") la contracción y la expansión es 0.

Resultados de la medición

3 intermediarios, tamaño del mensaje - 512 bytes

Con particiones distribuidas uniformemente entre los tres corredores, pudimos lograr un rendimiento ~500 Mb/s (aproximadamente 990 mil mensajes por segundo):

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

El consumo de memoria de la máquina virtual JVM no superó los 2 GB:

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

El rendimiento del disco alcanzó el rendimiento máximo de E/S del nodo en las tres instancias que estaban ejecutando los agentes:

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

A partir de los datos sobre el uso de la memoria por parte de los nodos, se deduce que el almacenamiento en búfer y en caché del sistema tomó ~10-15 GB:

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

3 intermediarios, tamaño del mensaje - 100 bytes

A medida que disminuye el tamaño del mensaje, el rendimiento cae entre un 15 y un 20 % debido al tiempo que se dedica a procesar cada mensaje. Además, la carga en el procesador casi se ha duplicado.

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Dado que los nodos del intermediario todavía tienen núcleos sin usar, el rendimiento se puede mejorar cambiando la configuración de Kafka. Esta no es una tarea fácil, por lo que para aumentar el rendimiento es mejor trabajar con mensajes más grandes.

4 intermediarios, tamaño del mensaje - 512 bytes

Puede aumentar fácilmente el rendimiento de un clúster de Kafka simplemente agregando nuevos intermediarios y manteniendo las particiones equilibradas (esto garantiza que la carga se distribuya uniformemente entre los intermediarios). En nuestro caso, después de agregar un intermediario, el rendimiento del clúster aumentó a ~580 Mbps (~1,1 millones de mensajes por segundo). El crecimiento resultó ser menor de lo esperado: esto se debe principalmente al desequilibrio de las particiones (no todos los corredores funcionan al máximo de sus capacidades).

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

El consumo de memoria de la máquina JVM se mantuvo por debajo de 2 GB:

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

El trabajo de los intermediarios con unidades se vio afectado por el desequilibrio de las particiones:

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Determine el tamaño adecuado para un clúster de Kafka en Kubernetes

Hallazgos

El enfoque iterativo presentado anteriormente se puede ampliar para cubrir escenarios más complejos que involucren a cientos de consumidores, particiones, actualizaciones continuas, reinicios de módulos, etc. Todo esto nos permite evaluar los límites del clúster de Kafka en diversas condiciones, identificar cuellos de botella en su funcionamiento y encontrar formas de abordarlos.

Diseñamos Supertubes para implementar un clúster de forma rápida y sencilla, configurarlo, agregar/eliminar intermediarios y temas, responder a alertas y garantizar que Kafka en Kubernetes funcione correctamente en general. Nuestro objetivo es ayudar a centrarse en la tarea principal ("generar" y "consumir" mensajes de Kafka) y dejar todo el trabajo duro a Supertubes y Kafka operator'u.

Si te interesan las tecnologías y proyectos Open Source de Banzai Cloud, suscríbete a la empresa en GitHub, Etiqueta LinkedIn o Twitter.

PD del traductor

Lea también en nuestro blog:

Fuente: habr.com

Añadir un comentario