Skip to content

Test environment in Kubernetes: Apache Kafka + Kafka Connect + MirrorMaker 2.0 + Jmeter

Notifications You must be signed in to change notification settings

ruslanbay/k8s-kafka

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Поднимаем тестовое окружение в Kubernetes: Apache Kafka, Kafka Connect, MirrorMaker 2.0 и Jmeter

Я инженер по нагрузочному тестированию и не так давно работаю над проектом, где предполагается активное использование Apache Kafka. Из-за режима удаленной работы получение доступа к тестовому окружению затянулось на недели. Чтобы не терять время я решил развернуть локальный стенд в Kubernetes.

Кто работал с Apache Kafka подтвердит, что официальная документация покрывает далеко не все тонкости инсталляции и настройки. Я надеюсь, что данная пошаговая инструкция позволит вам сократить время развертывания тестового окружения. Обращаю внимание на то, что установка stateful в контейнерах - далеко не лучшая идея, поэтому данная инструкция не предназначена для развертывания промышленного стенда.

Инструкция описывает создание виртуальной машины в VirtualBox, установку и настройку операционной системы, установку Docker, Kubernetes и системы мониторинга. В Kubernetes развертывается два кластера Apache Kafka: "production" и "backup". Для репликации сообщений из production в backup используется MirrorMaker 2.0. Взаимодействие между узлами production кластера защищено TLS. К сожалению, не могу выложить в Git скрипт для генерирования сертификатов. В качестве примера можете использовать сертификаты из архива certs/certs.tar.gz. В самом конце инструкции описывается развертывание кластера Jmeter и запуск тестового сценария.

Исходники доступны в репозитории: github.com/ruslanbay/k8s-kafka

Инструкция расcчитана на новичков в Kubernetes, поэтому если вы уже имеете опыт работы с контейнерами, то можете сразу перейти в раздел "12. Разворачиваем кластер Apache Kafka".

Q&A:

  • Почему используется Ubuntu? Изначально я развернул Kubernetes в CentOS 7, но после одного из обновлений окружение перестало работать. К тому же я заметил, что в CentOS нагрузочные тесты, запущенные в Jmeter, ведут себя непредсказуемо. Если сталкивались, пожалуйста, напишите в комментариях возможное решение этой проблемы. В Ubuntu всё намного стабильнее.
  • Почему не k3s или MicroK8s? Если коротко, ни k3s, ни MicroK8s из коробки не умеют работать с локальным Docker-репозиторием.
  • Почему не оптимизированы параметры конфигурации? Я намеренно использовал параметры по умолчанию где это возможно.
  • Почему Flannel? Я новичок в kubernetes и Flannel - единственный плагин, который мне удалось завести без проблем.
  • Почему Docker, а не CRI-O? Мне интересен CRI-O и я планирую изучить его в будущем.
  • Почему MirrorMaker 2.0 развернут в Kafka Connect? Kafka Connect позволяет редактировать параметры конфигурации MirrorMaker 2.0 "на лету" через REST API.

Оглавление

1. Создание виртуальной машины

2. Установка Ubuntu Server 20.04

3. Настройка Ubuntu

4. Установка Docker

5. Настройка iptables

6. Установка kubeadm, kubelet и kubectl

7. Разворачиваем кластер Kubernetes

8. Устанавливаем Flannel

9. Разрешаем запуск pod-ов на ноде control-plane

10. Добавляем алиас для команды kubectl

11. Устанавливаем Prometheus, Grafana, Alert Manager и Node Exporter

12. Разворачиваем кластер Apache Kafka

12.1. Запускаем Apache Zookeeper

12.2. Запускаем Apache Kafka

13. Проверяем отправку и получение сообщений

13.1. Запускаем генератор сообщений

13.2. Запускаем получателя сообщений

14. Настраиваем репликацию сообщений с помощью MirrorMaker 2.0

14.1. Запуск MirrorMaker 2.0 как конектор в кластере Kafka Connect

14.2. Проверяем репликацию сообщений

15. Выполнение сценариев Jmeter

16. Удаление данных


Виртуальной машине должно быть доступно как минимум 2 ядра ЦПУ и 6-8 ГБ оперативной памяти. Если нет возможности выделить достаточный объем оперативной памяти для виртуальной машины, то посмотрите в сторону Rancher K3S.

Показать скриншоты

''

''

''

''

''

''

Установка операционной системы довольно тривиальный процесс, но на всякий случай приведу скриншоты. На что нужно обратить внимание:

  • IP адрес, который будет присвоен ноде (по умолчанию 10.0.2.15);
  • Kubernetes требует, чтобы swap был отключен, поэтому дисковые разделы необходимо создать вручную;
  • В самом конце процесса установки можно выбрать опцию "Install OpenSSH server".
Показать скриншоты

''

''

''

''

''

''

''

''

''

''

''

''

''

sudo ufw disable
sudo swapoff -a

sudo sed -i 's/^\/swap/#\/swap/' /etc/fstab

В поставку OpenJDK входит утилита keytool, которая понадобится для генерирования сертификатов:

sudo apt install openjdk-8-jdk-headless

Подробная инструкция от DigitalOcean: https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-ubuntu-20-04-ru

# Switch to the root user 
sudo su

# (Install Docker CE)
## Set up the repository:
### Install packages to allow apt to use a repository over HTTPS
apt-get update && apt-get install -y \
  apt-transport-https ca-certificates curl software-properties-common gnupg2

# Add Docker’s official GPG key:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -

# Add the Docker apt repository:
add-apt-repository \
  "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) \
  stable"

# Install Docker CE
apt-get update && apt-get install -y \
  containerd.io=1.2.13-2 \
  docker-ce=5:19.03.11~3-0~ubuntu-$(lsb_release -cs) \
  docker-ce-cli=5:19.03.11~3-0~ubuntu-$(lsb_release -cs)

# Set up the Docker daemon
cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF

mkdir -p /etc/systemd/system/docker.service.d

# Restart Docker
systemctl daemon-reload
systemctl restart docker

# If you want the docker service to start on boot, run the following command:
sudo systemctl enable docker
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

sudo sysctl --system
sudo apt-get update && sudo apt-get install -y apt-transport-https curl

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -

cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF

sudo apt-get update

sudo apt-get install -y kubelet kubeadm kubectl

sudo apt-mark hold kubelet kubeadm kubectl

Запускаем инициализацию ноды control-plane: [источник]

# Pulling images required for setting up a Kubernetes cluster
# This might take a minute or two, depending on the speed of your internet connection
sudo kubeadm config images pull

# Initialize a Kubernetes control-plane node
sudo kubeadm init --pod-network-cidr=10.244.0.0/16

Следующие команды необходимо выполнить под обычным пользователем (не root): [источник]

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

Так как наш кластер Kubernetes развернут в режиме standalone, то необходимо разрешить запуск pod-ов на ноде control-plane:

kubectl taint nodes --all node-role.kubernetes.io/master-
alias k='kubectl'

echo "alias k='kubectl'" >> ~/.bashrc

Устанавливаем kube-prometheus: [источник]

curl -O -L https://github.com/coreos/kube-prometheus/archive/master.zip

sudo apt install -y unzip

unzip master.zip

cd kube-prometheus-master

kubectl create -f manifests/setup

kubectl create -f manifests/

Выполните следующую команду, чтобы посмотреть процесс запуска pod-ов. Необходимо дождаться когда все pod-ы перейдут в статус Running:

kubectl get pods -w -n monitoring

Для сбора метрик Kafka и Zookeeper будем использовать JMX Exporter. Чтобы Prometheus получил доступ к экспортируемым метрикам необходимо добавить ServiceMonitor:

k apply -f https://raw.githubusercontent.com/ruslanbay/k8s-kafka/master/servicemonitor/jmx-exporter-servicemonitor.yaml

Создадим сервис, чтобы получить доступ к веб-интерфейсу Grafana из виртуальной машины:

kubectl apply -f https://raw.githubusercontent.com/ruslanbay/k8s-kafka/master/service/grafana-svc.yaml

После запуска сервиса Grafana будет доступна из гостевой системы по адресу http://localhost:32000

Выполним проброс порта, чтобы получить доступ к Grafana на хост машине:

''

Теперь веб-интерфейс Grafana доступен на хост машине по адресу http://127.0.0.1:3000

Для просмотра метрик в Grafana можете воспользоваться готовым дашбордом. Для этого перейдите на страницу http://127.0.0.1:3000/dashboard/import и в поле "Import via panel json" скопируйте содержимое файла grafana-dashboard.json

# Скачиваем содержимое репозитория
git clone https://github.com/ruslanbay/k8s-kafka.git $HOME/k8s-kafka
cd $HOME/k8s-kafka

Чтобы получить предсказуемые имена хостов воспользуемся Statefulset. В нашем случае кластер Apache Zookeeper будет состоять из трех инстансов: zookeeper-0.zookeeper, zookeeper-1.zookeeper и zookeeper-2.zookeeper

# Собираем образ zookeeper-base
sudo docker build -t zookeeper-base:local-v1 -f dockerfile/zookeeper-base.dockerfile .

# Запускаем сервис Zookeeper
k apply -f service/zookeeper-svc.yaml

# Запускаем кластер Apache Zookeeper
k apply -f statefulset/zookeeper-statefulset.yaml

# Перед выполнением следующих шагов необходимо дождаться когда все три pod-а перейдут в статус Running. Проверить состояние pod-ов можно следующей командой:
k get pods -w

Кластер Apache Kafka будет состоять из двух брокеров: kafka-0.kafka и kafka-1.kafka

# Собираем образ kafka-base
sudo docker build -t kafka-base:local-v1 -f dockerfile/kafka-base.dockerfile .

# Запускаем сервис Kafka
k apply -f service/kafka-svc.yaml

# Запускаем кластер Apache Kafka
k apply -f statefulset/kafka-statefulset.yaml

# Перед выполнением следующих шагов необходимо дождаться когда оба pod-а перейдут в статус Running. Проверить состояние pod-ов можно следующей командой:
k get pods -w

Запустим генератор сообщений, который будет отправлять 10 сообщений в секунду. Размер каждого сообщения составляет 100 байт. В общей сложности будет отправлено 30000 сообщений.

# Запускаем новый pod - producer
k run --rm -i --tty producer --image=kafka-base:local-v1 -- bash

# Создаем топик topicname и отправляем в него сообщения
bin/kafka-producer-perf-test.sh \
   --topic topicname \
   --num-records 30000 \
   --record-size 100 \
   --throughput 10 \
   --producer.config /config/client.properties \
   --producer-props acks=1 \
   bootstrap.servers=kafka-0.kafka:9092,kafka-1.kafka:9092 \
   buffer.memory=33554432 \
   batch.size=8196
# Запускаем новый pod - consumer
k run --rm -i --tty consumer --image=kafka-base:local-v1 -- bash 

# Получаем сообщения из топика topicname
bin/kafka-consumer-perf-test.sh \
--broker-list kafka-0.kafka:9092,kafka-1.kafka:9092 \
--consumer.config /config/client.properties \
--messages 30000 \
--topic topicname \
--threads 2

Кластер Apache Kafka, который мы запустили ранее, условно назовем production. Поднимем еще один pod, в котором будет запущено три контейнера: Apache Zookeeper, Apache Kafka и Kafka Connect. Новый инстанс Apache Kafka назовем backup и настроим репликацию сообщений из production в backup.

k apply -f service/mirrormaker-svc.yaml

# Поднимем pod, в котором будет запущено три контейнера: Apache Zookeeper, Apache Kafka и Kafka Connect
k apply -f statefulset/mirrormaker-statefulset.yaml

# Необходимо дождаться когда pod с именем mirrormaker-0 перейдет в статус Running
k get pods -w

# Подключимся к контейнеру connect в pod-е mirrormaker-0 и откроем командную строку
k exec -ti mirrormaker-0 -c connect -- bash

# В кластере Kafka Connect создадим коннектор MirrorMaker 2.0
curl -X POST -H "Content-Type: application/json" mirrormaker-0.mirrormaker:8083/connectors -d \
'{
    "name": "MirrorSourceConnector",
    "config": {
      "connector.class": "org.apache.kafka.connect.mirror.MirrorSourceConnector",
      "source.cluster.alias": "production",
      "target.cluster.alias": "backup",
      "source.cluster.bootstrap.servers": "kafka-0.kafka:9092,kafka-1.kafka:9092",
      "source.cluster.group.id": "mirror_maker_consumer",
      "source.cluster.enable.auto.commit": "true",
      "source.cluster.auto.commit.interval.ms": "1000",
      "source.cluster.session.timeout.ms": "30000",
      "source.cluster.security.protocol": "SSL",
      "source.cluster.ssl.truststore.location": "/certs/kafkaCA-trusted.jks",
      "source.cluster.ssl.truststore.password": "kafkapilot",
      "source.cluster.ssl.truststore.type": "JKS",
      "source.cluster.ssl.keystore.location": "/certs/kafka-consumer.jks",
      "source.cluster.ssl.keystore.password": "kafkapilot",
      "source.cluster.ssl.keystore.type": "JKS",
      "target.cluster.bootstrap.servers": "localhost:9092",
      "target.cluster.compression.type": "none",
      "topics": ".*",
      "rotate.interval.ms": "1000",
      "key.converter.class": "org.apache.kafka.connect.converters.ByteArrayConverter",
      "value.converter.class": "org.apache.kafka.connect.converters.ByteArrayConverter"
    }
}'

Если репликация прошла успешно, то в backup будет создан топик production.topicname. Префикс ".production" MirrorMaker 2.0 добавит, чтобы избежать зацикливания, например, когда репликация настроена в режиме active-active.

# Запускаем новый pod - consumer
k exec -ti mirrormaker-0 -c kafka -- bash

# Получим список топиков 
bin/kafka-topics.sh --list --bootstrap-server mirrormaker-0.mirrormaker:9092

# Получаем сообщения из топика production.topicname
bin/kafka-console-consumer.sh \
--bootstrap-server mirrormaker-0.mirrormaker:9092 \
--topic production.topicname \
--from-beginning

Если топик production.topicname присутствует, но сообщения из него не считываются, проверьте логи Kafka Connect:

k logs mirrormaker-0 connect

Если в логах присутствуют следующие записи

ERROR WorkerSourceTask{id=MirrorSourceConnector-0} Failed to flush, timed out while waiting for producer to flush outstanding 1 messages (org.apache.kafka.connect.runtime.WorkerSourceTask:438)

ERROR WorkerSourceTask{id=MirrorSourceConnector-0} Failed to commit offsets (org.apache.kafka.connect.runtime.SourceTaskOffsetCommitter:114)

для решения проблемы можете уменьшить значение параметра producer.buffer.memory:

k exec -ti mirrormaker-0 -c connect -- bash

curl -X PUT -H "Content-Type: application/json" mirrormaker-0.mirrormaker:8083/connectors/MirrorSourceConnector/config -d \
'{
  "connector.class": "org.apache.kafka.connect.mirror.MirrorSourceConnector",
  "source.cluster.alias": "production",
  "target.cluster.alias": "backup",
  "source.cluster.bootstrap.servers": "kafka-0.kafka:9092,kafka-1.kafka:9092",
  "source.cluster.group.id": "mirror_maker_consumer",
  "source.cluster.enable.auto.commit": "true",
  "source.cluster.auto.commit.interval.ms": "1000",
  "source.cluster.session.timeout.ms": "30000",
  "source.cluster.security.protocol": "SSL",
  "source.cluster.ssl.truststore.location": "/certs/kafkaCA-trusted.jks",
  "source.cluster.ssl.truststore.password": "kafkapilot",
  "source.cluster.ssl.truststore.type": "JKS",
  "source.cluster.ssl.keystore.location": "/certs/kafka-consumer.jks",
  "source.cluster.ssl.keystore.password": "kafkapilot",
  "source.cluster.ssl.keystore.type": "JKS",
  "target.cluster.bootstrap.servers": "localhost:9092",
  "target.cluster.compression.type": "none",
  "topics": ".*",
  "rotate.interval.ms": "1000",
  "producer.buffer.memory:" "1000",
  "key.converter.class": "org.apache.kafka.connect.converters.ByteArrayConverter",
  "value.converter.class": "org.apache.kafka.connect.converters.ByteArrayConverter"
}'
# Собираем образ jmeter-base
sudo docker build -t jmeter-base:local-v1 -f dockerfile/jmeter-base.dockerfile .

# Запускаем сервис Jmeter
k apply -f service/jmeter-svc.yaml

# Запускаем четыре pod-а Jmeter, которые будут непосредственно генерировать нагрузку
k apply -f statefulset/jmeter-statefulset.yaml

Создадим pod jmeter-producer и выполним в нем сценарий producer.jmx

k run --rm -i --tty jmeter-producer --image=jmeter-base:local-v1 -- bash ./jmeter -n -t /tests/producer.jmx -r -Jremote_hosts=jmeter-0.jmeter:1099,jmeter-1.jmeter:1099

Создадим pod jmeter-consumer и выполним в нем сценарий consumer.jmx

k run --rm -i --tty jmeter-consumer --image=jmeter-base:local-v1 -- bash ./jmeter -n -t /tests/consumer.jmx -r -Jremote_hosts=jmeter-2.jmeter:1099,jmeter-3.jmeter:1099

Удаляем statefulset

k delete statefulset jmeter zookeeper kafka mirrormaker

Удаляем контейнеры

sudo docker rmi -f zookeeper-base:local-v1 kafka-base:local-v1 jmeter-base:local-v1

Удаляем сервисы

k delete svc grafana jmeter kafka mirrormaker zookeeper
k delete servicemonitor jmxexporter




linkedin: ruslanbay

Всем добра! Не болейте.

About

Test environment in Kubernetes: Apache Kafka + Kafka Connect + MirrorMaker 2.0 + Jmeter

Topics

Resources

Stars

Watchers

Forks