Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Прим. перев.: У цій статті компанія Banzai Cloud ділиться прикладом використання її спеціальних утиліт для полегшення експлуатації Kafka в рамках Kubernetes. Інструкції, що наводяться, ілюструють, як можна визначити оптимальний розмір інфраструктури і налаштувати саму Kafka для досягнення необхідної пропускної спроможності.

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Apache Kafka - розподілена стрімінгова платформа для створення надійних, масштабованих та високопродуктивних потокових систем реального часу. Її вражаючі здібності можна розширити за допомогою Kubernetes. Для цього ми розробили Open Source-оператор Kafka та інструмент під назвою Супертрубки. Вони дозволяють запускати Kafka в Kubernetes та використовувати її різні функції, такі як тонка настройка конфігурації брокера, масштабування на основі метрик з ребалансуванням, rack awareness (поінформованість про апаратні ресурси), «м'яке» (graceful) викочування оновлень і т.д.

Спробуйте Supertubes у своєму кластері:

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

Або зверніться до документації. Також можна почитати про деякі можливості Kafka, робота з якими автоматизована за допомогою Supertubes та Kafka operator. Про них ми вже писали у блозі:

Вирішивши розгорнути кластер Kafka у Kubernetes, ви напевно зіткнетеся з проблемою визначення оптимального розміру базової інфраструктури та необхідністю тонкого підстроювання конфігурації Kafka для задоволення вимог до пропускної спроможності. Максимальна продуктивність кожного брокера визначається продуктивністю компонентів інфраструктури в основі, таких як пам'ять, процесор, швидкість диска, пропускна спроможність мережі і т.д.

В ідеалі конфігурація брокера має бути такою, щоб усі елементи інфраструктури використовувалися на максимум своїх можливостей. Однак у реальному житті таке налаштування дуже складне. Більш ймовірно, що користувачі налаштовуватимуть конфігурацію брокерів таким чином, щоб максимізувати використання одного або двох компонентів (диска, пам'яті або процесора). Взагалі кажучи, брокер показує максимальну продуктивність, коли його конфігурація дозволяє задіяти найповільніший компонент "за повною програмою". Так ми можемо отримати зразкове уявлення про навантаження, з яким здатний впоратися один брокер.

Теоретично ми також можемо прикинути кількість брокерів, необхідну для роботи із заданим навантаженням. Однак на практиці варіантів налаштування на різних рівнях настільки багато, що оцінити потенційну продуктивність певної конфігурації дуже складно (якщо неможливо). Інакше кажучи, дуже складно спланувати конфігурацію, відштовхуючись від певної заданої продуктивності.

Для користувачів Supertubes ми зазвичай застосовуємо наступний підхід: починаємо з деякої конфігурації (інфраструктура + налаштування), потім вимірюємо її продуктивність, коригуємо налаштування брокера та повторюємо процес ще раз. Це відбувається доти, доки потенціал найповільнішого компонента інфраструктури не буде повністю задіяний.

Таким чином ми отримуємо більш чітке уявлення про те, скільки брокерів необхідно кластеру, щоб упоратися з певним навантаженням (кількість брокерів також залежить від інших факторів, таких як мінімальна кількість реплік повідомлень для забезпечення стійкості, кількість лідерів лідерів тощо). Крім того, ми отримуємо уявлення про те, для якого інфраструктурного компонента бажано масштабування по вертикалі.

У цій статті йтиметься про кроки, які ми робимо для того, щоб «вичавити все» з найповільніших компонентів у початкових конфігураціях і виміряти пропускну здатність кластера Kafka. Високостійка конфігурація потребує принаймні трьох працюючих брокерів (min.insync.replicas=3), рознесених по трьох різних зон доступності. Для налаштування, масштабування та моніторингу інфраструктури Kubernetes ми використовуємо власну платформу управління контейнерами для гібридних хмар. Трубопровід. Вона підтримує on-premise (bare metal, VMware) та п'ять типів хмар (Alibaba, AWS, Azure, Google, Oracle), а також будь-які їх поєднання.

Думки щодо інфраструктури та конфігурації кластера Kafka

Для прикладів, наведених нижче, ми вибрали AWS як постачальника хмарних послуг і EKS як дистрибутив Kubernetes. Аналогічну конфігурацію можна реалізувати за допомогою PKE – дистрибутива Kubernetes від Banzai Cloud, сертифікованого CNCF.

Диск

Amazon пропонує різні типи томів EBS. В основі gp2 и io1 лежать SSD-диски, проте для забезпечення високої пропускної спроможності gp2 споживає накопичені кредити (I/O credits)тому ми віддали перевагу типу io1що пропонує стабільну високу пропускну здатність.

Типи інстансів

Продуктивність Kafka залежить від сторінкового кешу операційної системи, тому нам потрібні інстанси з достатньою кількістю пам'яті для брокерів (JVM) і сторінкового кешу. Інстанс c5.2xlarge - непоганий початок, оскільки має 16 Гб пам'яті та оптимізовано для роботи з EBS. Його недоліком є ​​те, що він здатний забезпечувати максимальну продуктивність не більше 30 хвилин кожні 24 години. Якщо робоче навантаження потребує максимальної продуктивності протягом тривалішого проміжку часу, слід придивитися до інших типів інстансів. Ми саме так і вчинили, зупинившись на c5.4xlarge. Він забезпечує максимальну пропускну здатність у 593,75 Мб/с. Максимальна пропускна спроможність тому EBS io1 вище, ніж у інстансу c5.4xlarge, Тому найповільніший елемент інфраструктури, мабуть, це пропускна спроможність I/O цього типу інстансу (що також повинні підтвердити результати наших навантажувальних тестів).

Мережа

Пропускна здатність мережі повинна виявитися досить великою в порівнянні з продуктивністю інстансу VM і диска, інакше мережа стає вузьким місцем. У нашому випадку мережевий інтерфейс c5.4xlarge підтримує швидкість до 10 Гб/с, що значно вище за пропускну здатність I/O інстансу VM.

Розгортання брокерів

Брокери повинні розвертатися (плануватися в Kubernetes) на виділені вузли, щоб уникнути конкуренції з іншими процесами за ресурси процесора, пам'яті, мережі та диска.

Версія Java

Логічним вибором є Java 11, оскільки вона сумісна з Docker у тому сенсі, що JVM правильно визначає процесори та пам'ять, доступні контейнеру, в якому працює брокер. Знаючи, що ліміти процесорів важливі, JVM внутрішньо і прозоро встановлює кількість потоків GC і потоків JIT-компілятора. Ми використали образ Kafka banzaicloud/kafka:2.13-2.4.0, що включає версію Kafka 2.4.0 (Scala 2.13) Java 11.

Якщо ви бажаєте докладніше дізнатися про Java/JVM на Kubernetes, зверніть увагу на такі наші публікації:

Налаштування пам'яті брокера

Існує два ключових аспекти в налаштуванні пам'яті брокера: налаштування для JVM і для Kubernetes. Межа пам'яті, встановлена ​​для pod'а, має бути більшою, ніж максимальний heap size, щоб у 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 (бенчмарк якого проводиться) досягне свого максимального навантаження. Іншими словами, необхідно провести попередню оцінку можливостей інструменту генерування навантаження, а також вибрати для нього типи instance'ів із достатньою кількістю процесорів та пам'яті. У цьому випадку наш інструмент буде продукувати більше навантаження, ніж здатний перетравити кластер Kafka. Після безлічі дослідів ми зупинилися на трьох примірниках. c5.4xlarge, у кожному з яких було запущено генератор.

Бенчмаркінг

Вимірювання продуктивності - ітеративний процес, що включає такі стадії:

  • налаштування інфраструктури (кластера EKS, кластера Kafka, інструменту генерування навантаження, а також Prometheus та Grafana);
  • генерування навантаження протягом певного періоду для фільтрації випадкових відхилень у показниках продуктивності, що збираються;
  • підстроювання інфраструктури та конфігурації брокера на основі спостережуваних показників продуктивності;
  • повторення процесу доти, доки досягнеться необхідний рівень пропускної спроможності кластера Kafka. При цьому він має бути стабільно відтвореним та демонструвати мінімальні варіації пропускної спроможності.

У наступному розділі описані кроки, що виконувались у процесі бенчмарку тестового кластера.

Інструменти

Для швидкого розгортання базової конфігурації, генерації навантаження та вимірювання продуктивності використовувалися наступні інструменти:

  • Banzai Cloud Pipeline для організації кластера EKS від Amazon c Прометей (для збору метрик Kafka та інфраструктури) та Grafana (Для візуалізації цих метрик). Ми скористалися інтегрованими в Трубопровід сервісами, які забезпечують федеративний моніторинг, централізований збір логів, сканування вразливостей, відновлення після збоїв, безпека корпоративного рівня та багато іншого.
  • Sangrenel - Інструмент для навантажувального тестування кластера Kafka.
  • Панелі Grafana для візуалізації метрик Kafka та інфраструктури: Kubernetes Kafka, Node Exporter.
  • Supertubes CLI для максимально простого налаштування кластера Kafka у Kubernetes. Zookeeper, Kafka operator, Envoy та багато інших компонентів встановлені та належним чином налаштовані для запуску готового до production кластера Kafka у Kubernetes.
    • Для установки supertubes CLI скористайтесь інструкціями, наведеними тут.

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Кластер EKS

Підготуйте кластер EKS із виділеними робочими вузлами c5.4xlarge у різних зонах доступності для pod'ів з брокерами Kafka, а також виділені вузли для генератора навантаження та моніторингової інфраструктури.

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

Коли кластер EKS запрацює, увімкніть його інтегровану службу моніторингу - Вона розгорне Prometheus і Grafana в кластер.

Системні компоненти Kafka

Встановіть системні компоненти Kafka (Zookeeper, kafka-operator) в EKS за допомогою supertubes CLI:

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

Кластер Kafka

За замовчуванням у EKS використовуються томи типу EBS gp2тому необхідно створити окремий клас сховищ на основі томів. io1 для кластера 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

Встановіть для брокерів параметр min.insync.replicas=3 та розгорніть 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 - мінімального рекомендованого значення для високодоступних production-систем.

Інструмент генерування навантаження

Ми запускали три екземпляри генератора навантаження (кожен писав в окремий топік). Для pod'ів генератора навантаження необхідно прописати node affinity, щоб вони планувалися тільки на виділені для них вузли:

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 байт та публікує їх у Kafka партіями по 500 повідомлень.
  • За допомогою аргументу -required-acks=all публікація визнається успішною, коли всі синхронізовані репліки повідомлення отримані та підтверджені брокерами Kafka. Це означає, що у бенчмарку ми вимірювали як швидкість роботи лідерів, одержують повідомлення, а й їх послідовників, реплицирующих повідомлення. До завдання цього тесту не входить оцінка швидкості читання споживачами (consumers) нещодавно прийнятих повідомлень, які поки що залишаються у сторінковому кеші ОС, та її порівняння зі швидкістю читання повідомлень, що зберігаються на диску.
  • Генератор навантаження паралельно запускає 20 worker'ів (-workers=20). Кожен worker містить 5 producer'ів, які спільно використовують підключення worker'а до кластера Kafka. У результаті кожен генератор налічує 100 producer'ів, і всі вони надсилають повідомлення кластеру Kafka.

Спостереження за станом кластера

Під час тестування навантаження кластера Kafka ми також стежили за його здоров'ям, щоб переконатися у відсутності перезапусків pod'ів, розсинхронізованих реплік і максимальної пропускної здатності з мінімальними флуктуаціями:

  • Генератор навантаження пише стандартну статистику про кількість опублікованих повідомлень та рівень помилок. Відсоток помилок має залишатися у значенні 0,00%.
  • Круїз контроль, розгорнутий kafka-operator'ом, надає панель моніторингу, де ми також можемо спостерігати за станом кластера. Щоб переглянути цю панель, виконайте:
    supertubes cluster cruisecontrol show -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file>
  • Рівень ISR (число реплік "in-sync") shrink та expansion дорівнює 0.

Результати вимірів

3 брокери, розмір повідомлень - 512 байт

З partition'ами, рівномірно розподіленими за трьома брокерами, нам вдалося досягти продуктивності ~500 Мб/с (приблизно 990 тис. повідомлень за секунду):

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Споживання пам'яті віртуальною машиною JVM не перевищило 2 Гб:

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Пропускна спроможність диска досягла максимальної пропускної спроможності I/O вузла на всіх трьох інстансах, на яких працювали брокери:

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

З даних використання пам'яті вузлами випливає, що системна буферизація і кешування зайняли ~10-15 Гб:

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

3 брокери, розмір повідомлень - 100 байт

З зменшенням розміру повідомлень пропускна спроможність падає приблизно на 15-20%: дається взнаки час, що витрачається на обробку кожного повідомлення. Крім того, навантаження на процесор зросло майже вдвічі.

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Оскільки на вузлах брокерів як і раніше є ядра, що не використовуються, продуктивність можна підвищити за рахунок зміни конфігурації Kafka. Це непросте завдання, тому збільшення пропускну здатність краще працювати з повідомленнями більшого розміру.

4 брокери, розмір повідомлень - 512 байт

Можна легко збільшити продуктивність кластера Kafka, просто додаючи нові брокери та зберігаючи баланс partition'ів (це забезпечує рівномірний розподіл навантаження між брокерами). У нашому випадку після додавання брокера пропускна здатність кластера зросла до ~580 Мб/с (~1,1 млн повідомлень за секунду). Зростання виявилося меншим, ніж очікувалося: переважно це пояснюється дисбалансом partition'ів (не всі брокери працюють на піку можливостей).

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Споживання пам'яті машиною JVM залишилося нижче 2 Гб:

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

На роботі брокерів з накопичувачами дався взнаки дисбаланс partition'ів:

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Визначаємо відповідний розмір для кластера Kafka у Kubernetes

Висновки

Представлений вище ітеративний підхід може бути розширений для охоплення більш складних сценаріїв, що включають сотні consumer'ів, repartitioning, оновлення, перезапуски pod'ів і т.д. Все це дозволяє нам оцінити межі можливостей кластера Kafka у різних умовах, виявити вузькі місця у його роботі та знайти способи боротьби з ними.

Ми розробили Supertubes для швидкого та легкого розгортання кластера, його конфігурування, додавання/видалення брокерів та топиків, реагування на оповіщення та забезпечення правильної роботи Kafka у Kubernetes загалом. Наша мета - допомогти сконцентруватися на основному завданні («генерувати» і «споживати» повідомлення Kafka), а всю важку роботу надати Supertubes і Kafka operator'у.

Якщо вам цікаві технології та Open Source-проекти Banzai Cloud, підписуйтесь на компанію GitHub, LinkedIn або Twitter.

PS від перекладача

Читайте також у нашому блозі:

Джерело: habr.com

Додати коментар або відгук