Цель проекта — проверить отказоустойчивость Strimzi Kafka в Kubernetes с помощью chaos-экспериментов (Chaos Mesh). Для этого разворачивается полный стенд: кластер Kafka (KRaft, 3 контроллера + 3 брокера), мониторинг (VictoriaMetrics K8s Stack, Grafana), сбор логов (VictoriaLogs), верификация сквозной доставки сообщений через Redis и Go-приложения producer/consumer. Затем последовательно применяются chaos-сценарии (pod-kill, network partition, CPU/memory stress, IO/DNS/JVM/HTTP chaos и др.) и проверяется, что кластер корректно восстанавливается без потери данных.

Схема взаимодействия
Схема взаимодействия

Пор��док развёртывания

  1. VictoriaMetrics K8s Stack + Grafana

  2. Strimzi Operator и Cruise Control (Strimzi)

  3. Strimzi Kafka (namespace, Kafka CR, топик, пользователь, PDB, Cruise Control с CronJob для ребаланса, метрики, Kafka Exporter)

  4. Сбор метрик Kafka через JMX, Kafka Exporter и kube-state-metrics для Strimzi CRD

  5. Schema Registry (Karapace) для Avro

  6. Kafka UI

  7. Redis в Kubernetes (верификация доставки, хеши сообщений Producer → Consumer)

  8. Golang producer/consumer (Helm)

  9. VictoriaLogs и victoria-logs-collector

  10. Chaos Mesh — установка (Helm, VMServiceScrape, RBAC/Dashboard)

  11. Импорт дашбордов Grafana

  12. Chaos Mesh для проведения хаос-экспериментов (pod-kill, network-delay, CPU/memory stress, I/O chaos и др.)

Установка стека мониторинга (VictoriaMetrics K8s Stack)

VictoriaMetrics K8s Stack: Готовый стек VictoriaMetrics + Grafana для метрик и дашбордов; совместим с Prometheus (PromQL), экономичен по ресурсам и удобно разворачивается через Helm.

  1. Репозиторий Helm для VictoriaMetrics:

helm repo add vm https://victoriametrics.github.io/helm-charts/
helm repo update
  1. Установить VictoriaMetrics K8s Stack с values из victoriametrics-values.yaml (Ingress для Grafana на grafana.apatsev.org.ru). Имя релиза и namespace vmks выбраны короткими, чтобы не упираться в лимит 63 символа для имён ресурсов Kubernetes.

helm upgrade --install vmks \
  oci://ghcr.io/victoriametrics/helm-charts/victoria-metrics-k8s-stack \
  --namespace vmks \
  --create-namespace \
  --wait \
  --version 0.70.0 \
  --timeout 15m \
  -f victoriametrics-values.yaml

Ссылка на исходный код: victoriametrics-values.yaml

  1. Получить пароль администратора Grafana:

kubectl get secret vmks-grafana -n vmks -o jsonpath='{.data.admin-password}' | base64 --decode; echo
  1. Grafana будет доступна по адресу http://grafana.apatsev.org.ru (логин по умолчанию: admin).

Strimzi

Strimzi — оператор для развёртывания и управления Apache Kafka в Kubernetes; выбран как де-факто стандарт для Kafka в K8s (CNCF-проект, декларативные CRD, активная разработка). Мониторинг вынесен в отдельные компоненты (Kafka Exporter, kube-state-metrics, PodMonitors для брокеров и операторов).

Установка Strimzi

Namespace kafka-cluster должен существовать заранее (как в оригинале strimzi-kafka-chaos-testing):

# Идемпотентно: создаёт namespace только если его ещё нет
kubectl get ns kafka-cluster 2>/dev/null || kubectl create namespace kafka-cluster
helm upgrade --install strimzi-cluster-operator \
  oci://quay.io/strimzi-helm/strimzi-kafka-operator \
  --namespace strimzi \
  --create-namespace \
  --set 'watchNamespaces={kafka-cluster}' \
  --wait \
  --version 0.50.0

Использовались манифесты из examples Strimzi с адаптацией для VictoriaMetrics K8s Stack.

Установка Kafka из examples

Kafka разворачивается с внутренним listener на порту 9092 с аутентификацией SASL SCRAM-SHA-512 и авторизацией simple (для ACL в KafkaUser). Клиенты (Producer, Consumer, Schema Registry) подключаются с учётными данными KafkaUser. В kafka-metrics.yaml уже заданы authorization.type: simple; без этого KafkaUser с ACL не перейдёт в Ready и Secret myuser не будет создан.

# Kafka-кластер (KRaft, persistent, listener sasl:9092 с SCRAM-SHA-512, JMX и Kafka Exporter)
kubectl apply -n kafka-cluster -f strimzi/kafka-metrics.yaml

# Топик
kubectl apply -n kafka-cluster -f strimzi/kafka-topic.yaml

# Пользователь Kafka (SCRAM-SHA-512; оператор создаёт Secret myuser с паролем)
kubectl apply -n kafka-cluster -f strimzi/kafka-user.yaml

Ссылка на исходный код: strimzi/kafka-metrics.yaml · strimzi/kafka-topic.yaml · strimzi/kafka-user.yaml

# Дождаться готовности Kafka (при первом развёртывании может занять 10–15 минут)
kubectl wait kafka/kafka-cluster -n kafka-cluster --for=condition=Ready --timeout=900s

PodDisruptionBudget для Kafka

PodDisruptionBudget гарантирует, что минимум 2 брокера всегда доступны во время плановых прерываний (drain ноды, rolling updates).

kubectl apply -n kafka-cluster -f strimzi/kafka-pdb.yaml
kubectl get pdb -n kafka-cluster

Ссылка на исходный код: strimzi/kafka-pdb.yaml

Cruise Control

Cruise Control - компонент для ребаланса партиций Kafka (распределение реплик по брокера��, цели по загрузке CPU/сети/диска). В kafka-metrics.yaml включён Cruise Control и autoRebalance при масштабировании (add-brokers / remove-brokers): при добавлении или удалении брокеров оператор сам запускает ребаланс по шаблонам.

Автоматический полный ребаланс в фоне (все брокеры, все топики) реализован через CronJob strimzi/cruise-control/kafka-rebalance-cronjob.yaml: раз в час создаётся KafkaRebalance и одобряется. Расписание можно изменить в .spec.schedule (например "0 */6 * * *" - раз в 6 часов). В Strimzi нет встроенного «постоянного» полного ребаланса, поэтому используется периодический запуск. Strimzi специально сделан так, потому что ребаланс — очень дорогая операция.

Порядок применения:

# 1. Шаблоны для autoRebalance (нужны до/вместе с Kafka CR)
kubectl apply -n kafka-cluster -f strimzi/cruise-control/kafka-rebalance-templates.yaml

# 2. Kafka с Cruise Control и autoRebalance (уже в kafka-metrics.yaml)
kubectl apply -n kafka-cluster -f strimzi/kafka-metrics.yaml

# 3. CronJob для периодического полного ребаланса
kubectl apply -n kafka-cluster -f strimzi/cruise-control/kafka-rebalance-cronjob.yaml

Ссылка на исходный код: strimzi/cruise-control/kafka-rebalance-templates.yaml · strimzi/kafka-metrics.yaml · strimzi/cruise-control/kafka-rebalance-cronjob.yaml

Ручной полный ребаланс: strimzi/cruise-control/kafka-rebalance.yaml (тот же ресурс, что использует CronJob).

kubectl apply -n kafka-cluster -f strimzi/cruise-control/kafka-rebalance.yaml
kubectl annotate kafkarebalance kafka-cluster-rebalance -n kafka-cluster strimzi.io/rebalance=approve
kubectl get kafkarebalance -n kafka-cluster

Ссылка на исходный код: strimzi/cruise-control/kafka-rebalance.yaml

Metrics (examples/metrics)

Кластер Kafka задаётся манифестом kafka-metrics.yaml (ресурс Kafka CR Strimzi) - JMX-метрики (metricsConfig) и Kafka Exporter уже включены в манифест. Остаётся применить VMPodScrape для сбора метрик в VMAgent.

# Сбор метрик Strimzi Cluster Operator (состояние оператора, реконсиляция)
kubectl apply -n vmks -f strimzi/cluster-operator-metrics.yaml

# Сбор метрик Entity Operator - Topic Operator и User Operator
kubectl apply -n vmks -f strimzi/entity-operator-metrics.yaml

# Сбор JMX-метрик с подов брокеров Kafka
kubectl apply -n vmks -f strimzi/kafka-resources-metrics.yaml

Ссылка на исходный код: strimzi/cluster-operator-metrics.yaml · strimzi/entity-operator-metrics.yaml · strimzi/kafka-resources-metrics.yaml

Kube-state-metrics для Strimzi CRD - отдельный экземпляр kube-state-metrics в режиме --custom-resource-state-only: он следит за кастомными ресурсами Strimzi (Kafka, KafkaTopic, KafkaUser, KafkaConnect, KafkaConnector и др.) и отдаёт их состояние в формате Prometheus (ready, replicas, topicId, kafka_version и т.д.). Это нужно для дашбордов и алертов по состоянию CR (например, «топик не Ready», «Kafka не на целевой версии»). Обычный kube-state-metrics из VictoriaMetrics K8s Stack таких метрик по Strimzi не даёт.

  • Шаг 1 (ConfigMap): описание, какие CRD и какие поля из них экспортировать как метрики (префиксы strimzi_kafka_topic_*strimzi_kafka_user_*strimzi_kafka_* и т.д.).

  • Шаг 2 (Deployment + RBAC + VMServiceScrape): сам под kube-state-metrics с этим конфигом, права на list/watch Strimzi CR в кластере и VMServiceScrape, чтобы VMAgent начал скрейпить метрики.

# 1. ConfigMap с конфигом метрик по CRD Strimzi
kubectl apply -n kafka-cluster -f strimzi/kube-state-metrics-configmap.yaml

# 2. Deployment, Service, RBAC и VMServiceScrape
kubectl apply -n kafka-cluster -f strimzi/kube-state-metrics-ksm.yaml

Ссылка на исходный код: strimzi/kube-state-metrics-configmap.yaml · strimzi/kube-state-metrics-ksm.yaml

Kafka Exporter

Kafka Exporter подключается к брокерам по Kafka API и отдаёт метрики в формате Prometheus.

kafka-metrics.yaml уже включает блок spec.kafkaExporter в ресурсе Kafka (CR Strimzi). Этот блок включает Kafka Exporter: без него оператор не создаёт соответствующие ресурсы, а при его наличии - автоматически разворачивает Deployment и Pod в namespace кластера.

Сбор метрик Kafka Exporter: В Strimzi 0.50 оператор создаёт Deployment и Pod (без отдельного Service). Метрики Kafka Exporter (kafka_topic_*kafka_consumergroup_*) собираются через kafka-resources-metrics (VMPodScrape) - поды Kafka Exporter имеют label strimzi.io/kind=Kafka и уже включены в этот scrape. Дополнительно примените VMServiceScrape для совместимости со старыми/будущими версиями Strimzi, где оператор создаёт Service:

kubectl apply -f strimzi/kafka-exporter-servicemonitor.yaml

Ссылка на исходный код: strimzi/kafka-exporter-servicemonitor.yaml

При указании kafkaExporter в CR Strimzi Cluster Operator поднимает отдельный Deployment (например, kafka-cluster-kafka-exporter) - это не «просто параметр» в поде Kafka, а отдельное приложение, которым управляет оператор.

Kafka Exporter встроен в Strimzi как опциональный компонент: образ и конфигурация задаются оператором, он создаёт и обновляет Deployment при изменении CR.

Schema Registry (Karapace) для Avro

Go-приложение из этого репозитория использует Avro и Schema Registry API. Для удобства здесь добавлены готовые мани��есты для Karapace - open-source реализации API Confluent Schema Registry (drop-in replacement).

Karapace поднимается как обычный HTTP-сервис и хранит схемы в Kafka-топике _schemas (как и Confluent SR).

  • strimzi/kafka-topic-schemas.yaml - KafkaTopic для _schemas (важно при min.insync.replicas: 2)

  • strimzi/kafka-user-schema-registry.yaml - отдельный KafkaUser для Karapace с минимальными правами (топик _schemas, consumer groups)

  • schema-registry.yaml - Service/Deployment для Karapace (ghcr.io/aiven-open/karapace:5.0.3). Подключение к Kafka по SASL SCRAM-SHA-512 (логин/пароль из KafkaUser schema-registry). Развёрнуто 2 реплики для отказоустойчивости (PDB, rolling update без простоя). У всех реплик KARAPACE_MASTER_ELIGIBILITY=true (выбор master через Kafka consumer group).

Файлы в директории strimzi в репозитории используют namespace: kafka-cluster и strimzi.io/cluster: kafka-cluster. В schema-registry.yaml задан KARAPACE_BOOTSTRAP_URIkafka-cluster-kafka-bootstrap.kafka-cluster.svc.cluster.local:9092. Подставьте свой namespace/кластер, если иные.

Секрет для Schema Registry: Karapace читает пароль из Secret schema-registry в namespace schema-registry. Strimzi создаёт этот Secret в kafka-cluster после применения kafka-user-schema-registry.yaml. Скопируйте Secret в namespace schema-registry перед развёртыванием Karapace:

kubectl create namespace schema-registry --dry-run=client -o yaml | kubectl apply -f -

# Создать KafkaUser для Schema Registry
kubectl apply -n kafka-cluster -f strimzi/kafka-user-schema-registry.yaml
kubectl wait kafkauser/schema-registry -n kafka-cluster --for=condition=Ready --timeout=60s || true
# Если таймаут: проверьте kubectl get kafkauser schema-registry -n kafka-cluster; при Ready продолжайте.

# Скопировать Secret schema-registry в namespace schema-registry.
# Важно: убрать ownerReferences, иначе в новом namespace Secret будет невалидным (используйте jq).
kubectl get secret schema-registry -n kafka-cluster -o json | \
  jq 'del(.metadata.resourceVersion, .metadata.uid, .metadata.creationTimestamp, .metadata.ownerReferences) | .metadata.namespace = "schema-registry"' | \
  kubectl apply -f -

# Создать топик для схем
kubectl apply -n kafka-cluster -f strimzi/kafka-topic-schemas.yaml
kubectl wait kafkatopic/schemas-topic -n kafka-cluster --for=condition=Ready --timeout=120s || true
# Если таймаут: проверьте kubectl get kafkatopic schemas-topic -n kafka-cluster; при Ready продолжайте.

# Развернуть Schema Registry
kubectl apply -f schema-registry.yaml
kubectl rollout status deploy/schema-registry -n schema-registry --timeout=5m || true
# При таймауте (загрузка образа, выбор master): проверьте kubectl get pods -n schema-registry; дождитесь Ready, затем sleep 120.
sleep 120
kubectl get svc -n schema-registry schema-registry

Ожидание: sleep 120 или дольше нужен после первого запуска Karapace, чтобы успел выбраться master; иначе приложение Producer при регистрации схем может получить ошибку 503. Команды kubectl wait для KafkaUser/KafkaTopic и kubectl rollout status в некоторых окружениях могут завершаться по таймауту при уже готовых ресурсах - тогда проверьте статус вручную и продолжайте.

Важно: ACL для Karapace. KafkaUser schema-registry содержит ACL для топика _schemas, consumer group schema-registry и групп с префиксом karapace (karapace-autogenerated-*). Без этих прав Schema Registry «зависнет» на Replay progress: -1/N.

Redis в Kubernetes

Redis используется для верификации доставки сообщений: Producer записывает хеши тел сообщений в Redis, Consumer читает и сверяет хеши, при совпадении удаляет ключ. Подробнее верификация доставки описана ниже.

kubectl apply -f redis/redis.yaml
kubectl rollout status deploy/redis -n redis --timeout=120s

Ссылка на исходный код: redis/redis.yaml

Метрики Redis для дашборда redis-delivery-verification (in-cluster Redis):

kubectl apply -f redis/redis-exporter-in-cluster.yaml

Ссылка на исходный код: redis/redis-exporter-in-cluster.yaml

Проверить: kubectl get pods -n vmks -l app.kubernetes.io/name=redis-exporter-in-cluster

Импорт дашборда: Grafana → Dashboards → Import → dashboards/redis-delivery-verification.json. Источник метрик - VictoriaMetrics.

Producer App и Consumer App

Producer App и Consumer App - Go приложение для работы с Apache Kafka через Strimzi. Приложение может работать в режиме producer (отправка сообщений) или consumer (получение сообщений) в зависимости от переменной окружения MODE. Сообщения сериализуются в Avro с использованием Schema Registry (Karapace) - совместимого с Confluent API. Kafka использует аутентификацию SASL SCRAM-SHA-512; учётные данные передаются только через Secret (kind: Secret, например myuser от Strimzi). Перед запуском Producer/Consumer необходимо развернуть Schema Registry (см. раздел «Schema Registry (Karapace) для Avro») и Redis (см. раздел «Redis в Kubernetes») и передать schemaRegistry.url и учётные данные Kafka в Helm.

Используемые библиотеки

Структура исходного кода

  • main.go - основной код Go-приложения (producer/consumer)

  • metrics.go - определение Prometheus-метрик

  • go.modgo.sum - файлы зависимостей Go модуля

  • Dockerfile - многоэтапная сборка Docker образа

Сборка и публикация Docker образа

Go-код в main.go можно изменять под свои нужды. После внесения изменений соберите и опубликуйте Docker образ:

# Сборка образа (используйте podman или docker)
podman build -t docker.io/antonpatsev/strimzi-kafka-chaos-testing:0.2.18 .

# Публикация в Docker Hub
podman push docker.io/antonpatsev/strimzi-kafka-chaos-testing:0.2.18

Переменные окружения

Переменная

Описание

Значение по умолчанию

MODE

Режим работы: producer или consumer

producer

KAFKA_BROKERS

Список брокеров Kafka (через запятую)

localhost:9092

KAFKA_TOPIC

Название топика

test-topic (как в Strimzi examples)

KAFKA_USERNAME

Имя пользователя Kafka (SASL SCRAM-SHA-512), обязательно

-

KAFKA_PASSWORD

Пароль пользователя Kafka (из Secret myuser в Strimzi), обязательно

-

SCHEMA_REGISTRY_URL

URL Schema Registry

http://localhost:8081

KAFKA_GROUP_ID

Consumer Group ID (только для consumer)

test-group (как в Strimzi kafka-user)

HEALTH_PORT

Порт для health-проверок (liveness/readiness)

8080

REDIS_ADDR

Адрес Redis для верификации доставки (хеш тела сообщения)

localhost:6379

REDIS_PASSWORD

Пароль Redis (если нужен)

-

REDIS_KEY_PREFIX

Префикс ключей сообщений в Redis

kafka-msg:

REDIS_SLO_SECONDS

Порог в секундах: сообщения в Redis старше этого считаются нарушением SLO

120

KAFKA_PRODUCER_MAX_ATTEMPTS

Кол-во попыток отправки при ошибке (Producer)

5

KAFKA_CONSUMER_MIN_BYTES

Минимум байт для fetch - ждать накопления перед ответом (Consumer)

5000 (5KB)

KAFKA_CONSUMER_MAX_BYTES

Максимум байт за один fetch (Consumer)

104857600 (100MB)

KAFKA_CONSUMER_MAX_WAIT_MS

Макс ожидание при отсутствии данных, ms (Consumer)

500

Запуск Producer/Consumer в кластере используя Helm

Для запуска приложений в кластере используйте Helm charts из директории helm. Kafka использует SASL SCRAM-SHA-512; учётные данные KafkaUser передаются только через Secret (kind: Secret) - указывается kafka.existingSecret="myuser" (Secret создаётся Strimzi при применении kafka-user.yaml). Имена приведены к примерам Strimzitest-topictest-group, пользователь myuser.

Секрет в namespace Producer/Consumer: Учётные данные Kafka (Secret myuser от Strimzi) должны быть в namespace kafka-producer и kafka-consumer. Скопируйте Secret из kafka-cluster один раз после применения kafka-user.yaml и готовности Kafka:

# 1. Убедиться, что Secret myuser есть в kafka-cluster (создаётся Strimzi User Operator после kafka-user.yaml)
kubectl get secret myuser -n kafka-cluster

# 2. Создать namespace для Producer и Consumer (если ещё нет)
kubectl create namespace kafka-producer --dry-run=client -o yaml | kubectl apply -f -
kubectl create namespace kafka-consumer --dry-run=client -o yaml | kubectl apply -f -

# 3. Скопировать Secret myuser с кредами в kafka-producer и kafka-consumer (через jq, без ownerReferences)
kubectl get secret myuser -n kafka-cluster -o json | jq 'del(.metadata.resourceVersion, .metadata.uid, .metadata.creationTimestamp, .metadata.ownerReferences) | .metadata.namespace = "kafka-producer"' | kubectl apply -f -
kubectl get secret myuser -n kafka-cluster -o json | jq 'del(.metadata.resourceVersion, .metadata.uid, .metadata.creationTimestamp, .metadata.ownerReferences) | .metadata.namespace = "kafka-consumer"' | kubectl apply -f -

# 4. Проверить: Secret должен быть в обоих namespace
# (при необходимости подождать 2–3 с и повторить)
kubectl get secret myuser -n kafka-producer
kubectl get secret myuser -n kafka-consumer

1) Установить Producer

helm upgrade --install kafka-producer ./helm/kafka-producer \
  --namespace kafka-producer \
  --create-namespace \
  --set image.tag="0.2.18" \
  --set kafka.brokers="kafka-cluster-kafka-bootstrap.kafka-cluster.svc.cluster.local:9092" \
  --set schemaRegistry.url="http://schema-registry.schema-registry:8081" \
  --set kafka.topic="test-topic" \
  --set kafka.existingSecret="myuser"

2) Установить Consumer

# In-cluster Redis (по умолчанию): Consumer получает данные из Redis, сверяет хеши
helm upgrade --install kafka-consumer ./helm/kafka-consumer \
  --namespace kafka-consumer \
  --create-namespace \
  --set image.tag="0.2.18" \
  --set kafka.brokers="kafka-cluster-kafka-bootstrap.kafka-cluster.svc.cluster.local:9092" \
  --set schemaRegistry.url="http://schema-registry.schema-registry:8081" \
  --set kafka.topic="test-topic" \
  --set kafka.groupId="test-group" \
  --set kafka.existingSecret="myuser"

3) Дождаться готовности подов Producer/Consumer

kubectl rollout status deploy/kafka-producer -n kafka-producer --timeout=120s
kubectl rollout status deploy/kafka-consumer -n kafka-consumer --timeout=120s
# Либо следить за подами: kubectl get pods -n kafka-producer; kubectl get pods -n kafka-consumer -w

4) Проверка подов и логов

# Убедиться, что все поды в статусе Running
kubectl get pods -n kafka-producer
kubectl get pods -n kafka-consumer
kubectl get pods -n schema-registry

# Producer logs (проверка на ошибки)
kubectl logs -n kafka-producer -l app.kubernetes.io/name=kafka-producer -f

# Consumer logs (проверка на ошибки)
kubectl logs -n kafka-consumer -l app.kubernetes.io/name=kafka-consumer -f

5) Настроить сбор метрик Prometheus

Go-приложение экспортирует метрики Prometheus на endpoint /metrics (на том же порту, что и health checks). Для сбора метрик через VMAgent примените VMServiceScrape:

# Метрики Producer
kubectl apply -f strimzi/kafka-producer-metrics.yaml

# Метрики Consumer
kubectl apply -f strimzi/kafka-consumer-metrics.yaml

Ссылка на исходный код: strimzi/kafka-producer-metrics.yaml · strimzi/kafka-consumer-metrics.yaml

Kafka UI

Web-интерфейс для управления Kafka - просмотр топиков, consumer groups, сообщений. Используется чарт kafbat-ui/kafka-ui.

# Добавить Helm-репозиторий
helm repo add kafbat-ui https://kafbat.github.io/helm-charts
helm repo update

# Kafka UI использует отдельного read-only пользователя kafka-ui-user
kubectl apply -n kafka-cluster -f strimzi/kafka-user-kafka-ui.yaml
kubectl wait kafkauser/kafka-ui-user -n kafka-cluster --for=condition=Ready --timeout=60s || true
# При таймауте: kubectl get kafkauser kafka-ui-user -n kafka-cluster; при Ready продолжайте.

kubectl create namespace kafka-ui --dry-run=client -o yaml | kubectl apply -f -
kubectl get secret kafka-ui-user -n kafka-cluster -o json | \
  jq 'del(.metadata.resourceVersion, .metadata.uid, .metadata.creationTimestamp, .metadata.ownerReferences) | .metadata.namespace = "kafka-ui"' | \
  kubectl apply -f -

# Установить Kafka UI
helm upgrade --install kafka-ui kafbat-ui/kafka-ui \
  -f helm/kafka-ui-values.yaml \
  --namespace kafka-ui \
  --create-namespace

Ссылка на исходный код: strimzi/kafka-user-kafka-ui.yaml · helm/kafka-ui-values.yaml

# Дождаться готовности (при первом запуске Kafka UI может потребоваться 2–3 минуты)
kubectl rollout status deploy/kafka-ui -n kafka-ui --timeout=300s
# При таймауте: kubectl get pods -n kafka-ui; при Running/Ready продолжайте.

Kafka UI будет доступен по адресу Ingress (в values: kafka-ui.apatsev.org.ru). Значения в helm/kafka-ui-values.yaml адаптированы под кластер kafka-cluster в namespace kafka-cluster и Schema Registry в schema-registry. Kafka UI подключается под пользователем kafka-ui-user с правами только на чтение (Describe, Read на topics и groups).

Kafka UI с Schema Registry
Kafka UI с Schema Registry

Верификация доставки сообщений через Redis

Верификация доставки через Redis: при указании REDIS_ADDR Producer записывает в Redis ключ (как у сообщения) и значение = content hash (id+data) + timestamp. Consumer сверяет хеш только по полям id и data; различие только по timestamp (ретраи, дубликаты) не считается ошибкой. При совпадении content hash — удаление ключа и счётчик полученных. При несовпадении тела сообщения (id или data другие) — ошибка в логах и метрика kafka_consumer_redis_hash_mismatch_total (проблема целостности данных). Метрики redis_pending_messages и redis_pending_old_messages (старее REDIS_SLO_SECONDS) дают SLO по задержке доставки.

Что даёт на стенде

  • Проверка целостности: сравнение content hash позволяет обнаружить искажение тела сообщения в пути (Kafka, сеть, код) во время хаос-тестов.

  • Факт доставки: удаление ключа из Redis после успешной обработки даёт явный сигнал «сообщение доставлено и проверено».

  • SLO по задержке доставки: счётчик «старых» pending-ключей (старше N секунд) показывает, укладывается ли система в допустимое время доставки при сбоях.

Ожидаемые значения на дашборде

На графике Pending Old (SLO Breach) допустимо видеть небольшое постоянное значение (например, 2). Это не потеря данных — это следствие отсутствия транзакции между Kafka и Redis (ограничение 1 ниже): при chaos-экспериментах (pod-kill, network partition) producer мог записать ключ в Redis, но сообщение не дошло до Kafka (брокер упал между записью в Redis и подтверждением commit) или consumer не получил его из-за rebalance. Эти «осиротевшие» ключи остаются в Redis и считаются старыми (> REDIS_SLO_SECONDS). При этом Consumer: Redis Hash Mismatch должен показывать «No data» — это подтверждает, что все доставленные сообщения имеют корректное содержимое (целостность данных не нарушена).

Известные ограничения

  1. Два хранилища без транзакции — при сбое между записью в Kafka и Redis возможна рассинхронизация: ключ есть в Redis, но сообщение не записано в Kafka (или наоборот). Это приводит к небольшому числу «осиротевших» pending-ключей (Pending Old на дашборде). На стенде это допустимо и учитывается при анализе результатов.

  2. Нет идемпотентности — при at-least-once повторная доставка может завысить счётчик received. Для целей хаос-тестирования это не критично.

  3. Утечка ключей при остановке Consumer — без TTL ключи накапливаются. На стенде используется TTL для автоматической очистки.

Примечание: данная верификация предназначена исключительно для этого тестового стенда и не рассчитана на использование в продакшене.

VictoriaLogs

VictoriaLogs - хранилище логов от VictoriaMetrics с поддержкой LogsQL в Grafana.

Важно: VictoriaMetrics K8s Stack должен быть установлен первым (он предоставляет CRD VMServiceScrape и т.п., используемые чартом VictoriaLogs).

Установка VictoriaLogs (cluster)

Используется файл victoria-logs-cluster-values.yaml из репозитория (Ingress для vlselect на victorialogs.apatsev.org.ru, retention 1d, PVC 20Gi).

helm repo add vm https://victoriametrics.github.io/helm-charts/
helm repo update

helm upgrade --install victoria-logs-cluster vm/victoria-logs-cluster \
  --namespace victoria-logs-cluster \
  --create-namespace \
  --wait \
  --version 0.0.27 \
  --timeout 15m \
  -f victoria-logs-cluster-values.yaml

Ссылка на исходный код: victoria-logs-cluster-values.yaml

Чтобы VMAgent из VictoriaMetrics K8s Stack собирал метрики VictoriaLogs, на VMServiceScrape должен быть label, по которому стэк выбирает цели (например release: vmks). Если чарт по умолчанию задаёт другой release, добавьте в values или --set нужный label для vlselect/vlinsert/vlstorage VMServiceScrape.

Проверка: kubectl get pods -n victoria-logs-cluster. Доступ к UI: по адресу Ingress из values (по умолчанию victorialogs.apatsev.org.ru).

Victoria-logs-collector

Victoria-logs-collector - Helm-чарт VictoriaMetrics, разворачивающий агент сбора логов (vlagent) как DaemonSet. Собирает логи со всех контейнеров в кластере и отправляет их в VictoriaLogs (vlinsert).

Требование: перед установкой должен быть развёрнут VictoriaLogs cluster (см. выше).

Используется файл victoria-logs-collector-values.yaml из репозитория (адрес vlinsert, поля для игнорирования, поля сообщения лога).

helm upgrade --install victoria-logs-collector vm/victoria-logs-collector \
  --namespace victoria-logs-collector \
  --create-namespace \
  --wait \
  --version 0.2.9 \
  --timeout 15m \
  -f victoria-logs-collector-values.yaml

Ссылка на исходный код: victoria-logs-collector-values.yaml

Проверка: kubectl get pods -n victoria-logs-collector.

Chaos Mesh

Chaos Mesh - платформа для chaos engineering в Kubernetes. Позволяет внедрять сбои (network, pod, I/O, time, DNS, JVM, HTTP) для тестирования отказоустойчивости Kafka и приложений. Манифесты взяты из strimzi-kafka-chaos-testing и адаптированы под namespace kafka-cluster и кластер kafka-cluster.

Установка Chaos Mesh

helm repo add chaos-mesh https://charts.chaos-mesh.org
helm repo update

helm upgrade --install chaos-mesh chaos-mesh/chaos-mesh \
  --namespace chaos-mesh \
  --create-namespace \
  -f chaos-mesh/chaos-mesh-values.yaml \
  --version 2.8.1 \
  --wait

Ссылка на исходный код: chaos-mesh/chaos-mesh-values.yaml

Проверка: kubectl get pods -n chaos-mesh

Chaos Mesh
Chaos Mesh

Для сбора метрик Chaos Mesh через VictoriaMetrics K8s Stack примените VMServiceScrape (в кластере используются CRD VictoriaMetrics, не Prometheus ServiceMonitor):

kubectl apply -f chaos-mesh/chaos-mesh-vmservicescrape.yaml

Ссылка на исходный код: chaos-mesh/chaos-mesh-vmservicescrape.yaml

Доступ к Dashboard

Dashboard использует RBAC-токен. Создайте ServiceAccount и токен:

kubectl apply -f chaos-mesh/chaos-mesh-rbac.yaml
sleep 3
kubectl get secret chaos-mesh-admin-token -n chaos-mesh -o jsonpath='{.data.token}' | base64 -d; echo

Ссылка на исходный код: chaos-mesh/chaos-mesh-rbac.yaml

Скопируйте токен и войдите в Chaos Mesh Dashboard. В chaos-mesh-values.yaml задан Ingress-хост chaos-dashboard.apatsev.org.ru (при необходимости измените под свой домен).

Chaos-эксперименты

После установки Chaos Mesh примените все эксперименты последовательно из chaos-experiments/ с таймаутом между ними (5–10 минут), проверьте статус каждого (kubectl get podchaos,... -n kafka-cluster) и убедитесь, что кластер реагирует (Grafana, логи). Пропуск этого шага означает неполное развёртывание.

В директории chaos-experiments/ лежат готовые эксперименты для Kafka, Schema Registry, Kafka UI и producer/consumer (CRD Chaos Mesh):

Файл

Тип

Описание

pod-kill.yaml

PodChaos + Schedule

Убийство одного брокера (одноразово + каждые 5 мин)

pod-failure.yaml

PodChaos

Сбой 1 брокера (ISR сохраняется) + сбой 2 из 3 (ISR нарушен, mode: fixed)

quorum-controller-loss.yaml

PodChaos

Потеря кворума: 2 из 3 контроллеров + убийство 1 контроллера

quorum-broker-loss.yaml

PodChaos

Потеря 2 из 3 брокеров (ISR нарушен) + убийство 1 брокера

controller-network-partition.yaml

NetworkChaos

Изоляция контроллера от Raft-кворума + изоляция контроллеров от брокеров

network-delay.yaml

NetworkChaos

Сетевые задержки 100–500 ms

cpu-stress.yaml

StressChaos

Нагрузка на CPU

memory-stress.yaml

StressChaos

Нагрузка на память

io-chaos.yaml

IOChaos

Задержки и ошибки дискового I/O

time-chaos.yaml

TimeChaos

Смещение системного времени

jvm-chaos.yaml

JVMChaos

GC, CPU/memory stress, latency, exception в JVM

http-chaos.yaml

HTTPChaos

Задержки/ошибки Schema Registry и Kafka UI

network-partition.yaml

NetworkChaos

Изоляция брокера / partition между брокерами и producer

network-loss.yaml

NetworkChaos

Потеря пакетов 10–30%

dns-chaos.yaml

DNSChaos

Ошибки DNS (брокеры, producer)

Ссылка на исходный код: chaos-experiments/

Порядок запуска и таймауты:

# === ЧАСТЬ 1: Тесты кворума и граничных сценариев ===

# 1. Убийство одного брокера (ISR сохраняется: 2/3 >= min.insync.replicas=2)
kubectl apply -f chaos-experiments/pod-kill.yaml
# Ожидание: партиции переизбирают лидера, under-replicated partitions, запись продолжается.
sleep 120
kubectl delete -f chaos-experiments/pod-kill.yaml

# 2. Сбой брокеров: 1 брокер (ISR сохраняется) + 2 из 3 (ISR нарушен, запись блокирована)
kubectl apply -f chaos-experiments/pod-failure.yaml
# Ожидание: kafka-pod-failure — запись работает; kafka-multi-pod-failure — producer получает NotEnoughReplicas.
sleep 120
kubectl delete -f chaos-experiments/pod-failure.yaml

# 3. Потеря кворума контроллеров: 2 из 3 KRaft-контроллеров недоступны
kubectl apply -f chaos-experiments/quorum-controller-loss.yaml
# Ожидание: кластер не может выбрать лидера контроллеров, метаданные недоступны.
# После восстановления кворума — автоматическое продолжение работы.
sleep 180
kubectl delete -f chaos-experiments/quorum-controller-loss.yaml

# 4. Потеря 2 из 3 брокеров (целенаправленно по pool-name: broker)
kubectl apply -f chaos-experiments/quorum-broker-loss.yaml
# Ожидание: ISR < min.insync.replicas, запись невозможна, чтение возможно.
# После восстановления: ISR восстанавливается, запись возобновляется.
sleep 180
kubectl delete -f chaos-experiments/quorum-broker-loss.yaml

# 5. Изоляция контроллера от Raft-кворума + изоляция контроллеров от брокеров
kubectl apply -f chaos-experiments/controller-network-partition.yaml
# Ожидание: переизбрание лидера контроллеров, кратковременная пауза метаданных.
sleep 180
kubectl delete -f chaos-experiments/controller-network-partition.yaml

# === ЧАСТЬ 2: Стресс и отказы инфраструктуры ===

# 6. CPU stress (нагрузка на CPU)
kubectl apply -f chaos-experiments/cpu-stress.yaml
# Ожидание: рост latency, request timeouts при высокой утилизации CPU.
sleep 120
kubectl delete -f chaos-experiments/cpu-stress.yaml

# 7. Memory stress (нагрузка на память)
kubectl apply -f chaos-experiments/memory-stress.yaml
# Ожидание: возможны OOMKilled, replication/request timeouts при нехватке памяти.
sleep 120
kubectl delete -f chaos-experiments/memory-stress.yaml

# 8. IO chaos (задержки и ошибки дискового I/O на JBOD-дисках)
kubectl apply -f chaos-experiments/io-chaos.yaml
# Ожидание: задержки записи/чтения партиций, I/O errors в логах Kafka.
sleep 120
kubectl delete -f chaos-experiments/io-chaos.yaml

# 9. Time chaos (смещение системного времени)
kubectl apply -f chaos-experiments/time-chaos.yaml
# Ожидание: NotControllerException, рассинхрон часов, проблемы с Raft.
sleep 120
kubectl delete -f chaos-experiments/time-chaos.yaml

# 10. JVM chaos (GC, stress и исключения в JVM)
kubectl apply -f chaos-experiments/jvm-chaos.yaml
# Ожидание: GC паузы, IOException в handleProduceRequest, задержки append.
sleep 120
kubectl delete -f chaos-experiments/jvm-chaos.yaml

# === ЧАСТЬ 3: Сетевые и прикладные сбои ===

# 11. HTTP chaos (задержки/ошибки Schema Registry и Kafka UI)
kubectl apply -f chaos-experiments/http-chaos.yaml
# Ожидание: Schema Registry отвечает с задержками/ошибками, producer retry на регистрацию схем.
sleep 120
kubectl delete -f chaos-experiments/http-chaos.yaml

# 12. DNS chaos (ошибки DNS для брокеров и producer)
kubectl apply -f chaos-experiments/dns-chaos.yaml
# Ожидание: UnknownHostException, потеря резолва bootstrap-адресов.
sleep 120
kubectl delete -f chaos-experiments/dns-chaos.yaml

# 13. Network partition (сетевая изоляция брокера от producer)
kubectl apply -f chaos-experiments/network-partition.yaml
# Ожидание: producer retries, connection timeouts, leader unavailable.
sleep 120
kubectl delete -f chaos-experiments/network-partition.yaml

# 14. Network loss (потеря пакетов)
kubectl apply -f chaos-experiments/network-loss.yaml
# Ожидание: потеря пакетов, retry в логах приложений, рост latency.
sleep 120
kubectl delete -f chaos-experiments/network-loss.yaml

# (опционально) Network delay — сетевые задержки
kubectl apply -f chaos-experiments/network-delay.yaml
# Ожидание: рост latency в логах и метриках.
sleep 120
kubectl delete -f chaos-experiments/network-delay.yaml

Проверка статуса (все задействованные namespace):

kubectl get podchaos,networkchaos,stresschaos,iochaos,timechaos,jvmchaos,httpchaos,dnschaos,schedule -n kafka-cluster
kubectl get dnschaos -n kafka-producer
kubectl get httpchaos -n schema-registry
kubectl get httpchaos -n kafka-ui

Остановка всех экспериментов: kubectl delete -f chaos-experiments/

Импорт дашбордов Grafana

Импорт: Grafana → Dashboards → Import → загрузить JSON. Источник метрик — VictoriaMetrics.

Strimzi Kafka

Ссылка: strimzi-kafka.json

Что смотреть при chaos: Состояние брокеров, реплик, under-replicated партиций; полезно при pod-kill, pod-failure, network-*

Strimzi Kafka 1
Strimzi Kafka 1
Strimzi Kafka 2
Strimzi Kafka 2

Strimzi KRaft

Ссылка: strimzi-kraft.json

Что смотреть при chaos: Состояние брокеров, реплик, under-replicated партиций; полезно при pod-kill, pod-failure, network-*

Strimzi KRaft 1
Strimzi KRaft 1
Strimzi KRaft 2
Strimzi KRaft 2

Strimzi Kafka Exporter

Ссылка: strimzi-kafka-exporter.json

Что смотреть при chaos: Метрики топиков, consumer groups, lag; при сетевых и pod-экспериментах — рост lag, изменение throughput

Strimzi Kafka Exporter
Strimzi Kafka Exporter

Strimzi Operators

Ссылка: strimzi-operators.json

Что смотреть при chaos: Реконсиляция Cluster Operator, Topic/User Operator; при убийстве подов — всплески активности

Strimzi Operators
Strimzi Operators

Kafka Go App Metrics (Producer/Consumer)

Ссылка: kafka-go-app-metrics.json — метрики Go-приложения (Producer/Consumer, Kafka, Schema Registry)

Что смотреть при chaos: Producer/Consumer: сообщения в сек, latency, ошибки, переподключения; при network-delay/loss — рост latency и ошибок

Дашборд включает панели для:

  • Producer метрики: скорость отправки сообщений, latency, ошибки

  • Consumer метрики: скорость получения сообщений, latency, lag, ошибки

  • Schema Registry метрики: запросы, latency, ошибки, кэш

  • Connection метрики: статус подключений, переподключения

У каждой панели на дашборде есть подробное описание прямо в Grafana.

Kafka Go App Metrics 1
Kafka Go App Metrics 1
Kafka Go App Metrics 2
Kafka Go App Metrics 2
Kafka Go App Metrics 3
Kafka Go App Metrics 3
Kafka Go App Metrics 4
Kafka Go App Metrics 4

Redis Delivery Verification

Ссылка: redis-delivery-verification.json — Redis, SLO и верификация доставки (подробнее)

Что смотреть при chaos: SLO доставки, pending/old messages; при сбоях доставки — рост старых сообщений в Redis

Redis Delivery Verification
Redis Delivery Verification

Наблюдение за состоянием кластера на дашбордах Grafana

Перед запуском экспериментов откройте дашборды Strimzi Kafka и kafka-go-app-metrics, установите автообновление (например, 10–30 s). После завершения каждого эксперимента убедитесь в восстановлении кластера (lag снижается, ошибок нет, latency в норме) перед запуском следующего.

Ресурсы и производительность

Ресурсы (конфигурация из манифестов и Helm values):

Компонент

Реплики

CPU (requests / limits)

Память (requests / limits)

Хранилище

Kafka (controller + broker)

3 + 3

по умолчанию Strimzi

по умолчанию Strimzi

controller: 100Gi (1 vol); broker: 3×~33Gi JBOD

Топик test-topic

30 партиций, 3 реплики

-

-

-

Producer

30

500m / 2000m на под

256Mi / 1Gi на под

-

Consumer

30

500m / 2000m на под

256Mi / 1Gi на под

-

Schema Registry (Karapace)

2

100m / 500m

256Mi / 512Mi

-

Redis

1

50m / 200m

128Mi / 256Mi

-

Целевые показатели производительности (расчёт по конфигу): Producer: до 200 msg/s на под при producerIntervalMs: 5 → до 6000 msg/s суммарно при 30 подах. Это создаёт заметную нагрузку на 3 брокера (~2000 msg/s на брокер), достаточную для выявления деградации при chaos-экспериментах. Consumer: 30 партиций, по одной на под, fetch до 100 MB за запрос; потребление ограничено скоростью producer и настройками minBytes/maxWaitMs. SLO доставки (Redis): сообщения должны быть обработаны consumer в течение 120 с (redis.sloSeconds); старые сообщения выше порога отображаются в дашборде redis-delivery-verification. Фактические throughput и latency зависят от кластера и нагрузки; их можно смотреть в Grafana (дашборды kafka-go-app-metrics, Strimzi Kafka Exporter).

JBOD-конфигурация: Каждый брокер использует 3 JBOD-диска (id 0, 1, 2) по ~33Gi. Это обеспечивает параллелизм I/O: 30 партиций × 3 реплики = 90 реплик / 3 брокера = 30 реплик на брокер, распределённых по 3 дискам (~10 реплик на диск). При chaos-экспериментах (IO chaos, pod-kill) нагрузка на I/O распределяется по дискам, а не концентрируется на одном.

Заключение

Chaos-эксперименты проводятся под нагрузкой ~6000 msg/s (200 msg/s × 30 producer-подов, ~2000 msg/s на брокер), что создаёт заметное давление на кластер и позволяет выявить реальную деградацию при сбоях.

Тестируемые граничные сценарии кворума

Помимо стандартных экспериментов (pod-kill, CPU/memory stress, IO/network chaos и др.), тестируются граничные случаи кворума, которые критичны для оценки реальной отказоустойчивости:

  • Потеря кворума контроллеров (2 из 3): при потере большинства KRaft-контроллеров кластер не может выбрать лидера, метаданные недоступны. Проверяется, что после восстановления хотя бы одного контроллера кворум восстанавливается автоматически.

  • Потеря 2 из 3 брокеров (ISR < min.insync.replicas): при min.insync.replicas: 2 потеря 2 брокеров делает запись невозможной (NotEnoughReplicas). Проверяется, что producer корректно получает ошибки, а после восстановления ISR запись возобновляется без потерь.

  • Сетевая изоляция контроллера от Raft-кворума: изолированный контроллер теряет связь с остальными; если это лидер — происходит переизбрание. Проверяется, что кластер продолжает работу через нового лидера.

  • Изоляция контроллеров от брокеров: брокеры теряют доступ к метаданным, но продолжают обслуживать существующие партиции.

Конфигурация хранения

Каждый брокер использует 3 JBOD-диска (~33Gi каждый), что обеспечивает параллелизм I/O: 30 реплик партиций на брокер распределяются по 3 дискам (~10 реплик на диск). Это позволяет адекватно тестировать IO chaos и создаёт реалистичную конфигурацию хранения.

Ключевые результаты

  • Отказ одного компонента (брокер или контроллер): кластер продолжает работу — ISR >= min.insync.replicas, кворум контроллеров сохраняется (2/3). Throughput и latency возвращаются к baseline после восстановления пода.

  • Потеря кворума (2 из 3): запись блокируется (для брокеров — NotEnoughReplicas, для контроллеров — недоступность метаданных). После восстановления — автоматическое возобновление без потери данных.

  • Целостность данных: верификация через Redis подтверждает сквозную доставку в рамках SLO (120 с) для всех сценариев, где запись возможна.

  • Наблюдаемость: дашборды Grafana (Strimzi Kafka, kafka-go-app-metrics, redis-delivery-verification) позволяют в реальном времени отследить деградацию и восстановление.

Конфигурация: 3 контроллера (Raft-кворум), 3 брокера (3 JBOD-диска каждый), min.insync.replicas: 2, 30 партиций × 3 реплики, нагрузка ~6000 msg/s.