Цель проекта — проверить отказоустойчивость 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 и др.) и проверяется, что кластер корректно восстанавливается без потери данных.

Пор��док развёртывания
Strimzi Operator и Cruise Control (Strimzi)
Strimzi Kafka (namespace, Kafka CR, топик, пользователь, PDB, Cruise Control с CronJob для ребаланса, метрики, Kafka Exporter)
Сбор метрик Kafka через JMX, Kafka Exporter и kube-state-metrics для Strimzi CRD
Schema Registry (Karapace) для Avro
Redis в Kubernetes (верификация доставки, хеши сообщений Producer → Consumer)
Golang producer/consumer (Helm)
Chaos Mesh — установка (Helm, VMServiceScrape, RBAC/Dashboard)
Импорт дашбордов Grafana
Chaos Mesh для проведения хаос-экспериментов (pod-kill, network-delay, CPU/memory stress, I/O chaos и др.)
Установка стека мониторинга (VictoriaMetrics K8s Stack)
VictoriaMetrics K8s Stack: Готовый стек VictoriaMetrics + Grafana для метрик и дашбордов; совместим с Prometheus (PromQL), экономичен по ресурсам и удобно разворачивается через Helm.
Репозиторий Helm для VictoriaMetrics:
helm repo add vm https://victoriametrics.github.io/helm-charts/ helm repo update
Установить VictoriaMetrics K8s Stack с values из
victoriametrics-values.yaml(Ingress для Grafana наgrafana.apatsev.org.ru). Имя релиза и namespacevmksвыбраны короткими, чтобы не упираться в лимит 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
Получить пароль администратора Grafana:
kubectl get secret vmks-grafana -n vmks -o jsonpath='{.data.admin-password}' | base64 --decode; echo
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 (логин/пароль из KafkaUserschema-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_URI: kafka-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 groupschema-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.
Используемые библиотеки
segmentio/kafka-go - клиент для работы с Kafka
riferrei/srclient - клиент для Schema Registry API (совместим с Karapace)
linkedin/goavro - работа с Avro схемами
prometheus/client_golang - экспорт Prometheus-метрик
Структура исходного кода
main.go - основной код Go-приложения (producer/consumer)
metrics.go - определение Prometheus-метрик
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
Переменные окружения
Переменная | Описание | Значение по умолчанию |
|---|---|---|
| Режим работы: |
|
| Список брокеров Kafka (через запятую) |
|
| Название топика |
|
| Имя пользователя Kafka (SASL SCRAM-SHA-512), обязательно | - |
| Пароль пользователя Kafka (из Secret | - |
| URL Schema Registry | |
| Consumer Group ID (только для consumer) |
|
| Порт для health-проверок (liveness/readiness) |
|
| Адрес Redis для верификации доставки (хеш тела сообщения) |
|
| Пароль Redis (если нужен) | - |
| Префикс ключей сообщений в Redis |
|
| Порог в секундах: сообщения в Redis старше этого считаются нарушением SLO |
|
| Кол-во попыток отправки при ошибке (Producer) |
|
| Минимум байт для fetch - ждать накопления перед ответом (Consumer) |
|
| Максимум байт за один fetch (Consumer) |
|
| Макс ожидание при отсутствии данных, ms (Consumer) |
|
Запуск Producer/Consumer в кластере используя Helm
Для запуска приложений в кластере используйте Helm charts из директории helm. Kafka использует SASL SCRAM-SHA-512; учётные данные KafkaUser передаются только через Secret (kind: Secret) - указывается kafka.existingSecret="myuser" (Secret создаётся Strimzi при применении kafka-user.yaml). Имена приведены к примерам Strimzi: test-topic, test-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).

Верификация доставки сообщений через 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» — это подтверждает, что все доставленные сообщения имеют корректное содержимое (целостность данных не нарушена).
Известные ограничения
Два хранилища без транзакции — при сбое между записью в Kafka и Redis возможна рассинхронизация: ключ есть в Redis, но сообщение не записано в Kafka (или наоборот). Это приводит к небольшому числу «осиротевших» pending-ключей (Pending Old на дашборде). На стенде это допустимо и учитывается при анализе результатов.
Нет идемпотентности — при at-least-once повторная доставка может завысить счётчик
received. Для целей хаос-тестирования это не критично.Утечка ключей при остановке 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 через 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):
Файл | Тип | Описание |
|---|---|---|
| PodChaos + Schedule | Убийство одного брокера (одноразово + каждые 5 мин) |
| PodChaos | Сбой 1 брокера (ISR сохраняется) + сбой 2 из 3 (ISR нарушен, mode: fixed) |
| PodChaos | Потеря кворума: 2 из 3 контроллеров + убийство 1 контроллера |
| PodChaos | Потеря 2 из 3 брокеров (ISR нарушен) + убийство 1 брокера |
| NetworkChaos | Изоляция контроллера от Raft-кворума + изоляция контроллеров от брокеров |
| NetworkChaos | Сетевые задержки 100–500 ms |
| StressChaos | Нагрузка на CPU |
| StressChaos | Нагрузка на память |
| IOChaos | Задержки и ошибки дискового I/O |
| TimeChaos | Смещение системного времени |
| JVMChaos | GC, CPU/memory stress, latency, exception в JVM |
| HTTPChaos | Задержки/ошибки Schema Registry и Kafka UI |
| NetworkChaos | Изоляция брокера / partition между брокерами и producer |
| NetworkChaos | Потеря пакетов 10–30% |
| 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 KRaft
Ссылка: strimzi-kraft.json
Что смотреть при chaos: Состояние брокеров, реплик, under-replicated партиций; полезно при pod-kill, pod-failure, network-*


Strimzi Kafka Exporter
Ссылка: strimzi-kafka-exporter.json
Что смотреть при chaos: Метрики топиков, consumer groups, lag; при сетевых и pod-экспериментах — рост lag, изменение throughput

Strimzi Operators
Ссылка: strimzi-operators.json
Что смотреть при chaos: Реконсиляция Cluster Operator, Topic/User Operator; при убийстве подов — всплески активности

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.




Redis Delivery Verification
Ссылка: redis-delivery-verification.json — Redis, SLO и верификация доставки (подробнее)
Что смотреть при chaos: SLO доставки, pending/old messages; при сбоях доставки — рост старых сообщений в Redis

Наблюдение за состоянием кластера на дашбордах 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.
