Я инженер по нагрузочному тестированию и не так давно работаю над проектом, где предполагается активное использование 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 и запуск тестового сценария.
Инструкция рас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
6. Установка kubeadm, kubelet и kubectl
7. Разворачиваем кластер Kubernetes
9. Разрешаем запуск pod-ов на ноде control-plane
10. Добавляем алиас для команды kubectl
11. Устанавливаем Prometheus, Grafana, Alert Manager и Node Exporter
12. Разворачиваем кластер Apache Kafka
12.1. Запускаем Apache Zookeeper
13. Проверяем отправку и получение сообщений
13.1. Запускаем генератор сообщений
13.2. Запускаем получателя сообщений
14. Настраиваем репликацию сообщений с помощью MirrorMaker 2.0
14.1. Запуск MirrorMaker 2.0 как конектор в кластере Kafka Connect
14.2. Проверяем репликацию сообщений
15. Выполнение сценариев Jmeter
1. Создание виртуальной машины
Виртуальной машине должно быть доступно как минимум 2 ядра ЦПУ и 6-8 ГБ оперативной памяти. Если нет возможности выделить достаточный объем оперативной памяти для виртуальной машины, то посмотрите в сторону Rancher K3S.






2. Установка Ubuntu Server 20.04
Установка операционной системы довольно тривиальный процесс, но на всякий случай приведу скриншоты. На что нужно обратить внимание:
- IP адрес, который будет присвоен ноде (по умолчанию 10.0.2.15);
- Kubernetes требует, чтобы swap был отключен, поэтому дисковые разделы необходимо создать вручную;
- В самом конце процесса установки можно выбрать опцию "Install OpenSSH server".













3. Настройка Ubuntu
3.1. Отключить Firewall
sudo ufw disable
3.2. Отключить Swap
sudo swapoff -a sudo sed -i 's/^\/swap/#\/swap/' /etc/fstab
3.3. Установить OpenJDK
В поставку OpenJDK входит утилита keytool, которая понадобится для генерирования сертификатов:
sudo apt install openjdk-8-jdk-headless
3.4. Авторизация по ключу (опционально)
Подробная инструкция от DigitalOcean:
https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-ubuntu-20-04-ru
4. Установка Docker [источник]
# 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
5. Настройка iptables [источник]
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
6. Установка kubeadm, kubelet и kubectl [источник]
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
7. Разворачиваем кластер Kubernetes
Запускаем инициализацию ноды 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
8. Устанавливаем Flannel [источник]
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
9. Разрешаем запуск pod-ов на ноде control-plane [источник]
Так как наш кластер Kubernetes развернут в режиме standalone, то необходимо разрешить запуск pod-ов на ноде control-plane:
kubectl taint nodes --all node-role.kubernetes.io/master-
10. Добавляем алиас для команды kubectl [источник]
alias k='kubectl' echo "alias k='kubectl'" >> ~/.bashrc
11. Устанавливаем Prometheus, Grafana, Alert Manager и Node Exporter
Устанавливаем 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 servicemonitor/jmx-exporter-servicemonitor.yaml
Создадим сервис, чтобы получить доступ к веб-интерфейсу Grafana из виртуальной машины:
kubectl apply -f https://raw.githubusercontent.com/kildibaev/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
12. Разворачиваем кластер Apache Kafka
# Скачиваем содержимое репозитория git clone https://github.com/kildibaev/k8s-kafka.git $HOME/k8s-kafka cd $HOME/k8s-kafka
12.1. Запускаем Apache Zookeeper
Чтобы получить предсказуемые имена хостов воспользуемся 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
12.2. Запускаем Apache Kafka
Кластер 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
13. Проверяем отправку и получение сообщений
13.1. Запускаем генератор сообщений
Запустим генератор сообщений, который будет отправлять 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
13.2. Запускаем получателя сообщений
# Запускаем новый 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
14. Настраиваем репликацию сообщений с помощью MirrorMaker 2.0
14.1. Запуск MirrorMaker 2.0 как конектор в кластере Kafka Connect
Кластер 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" } }'
14.2. Проверяем репликацию сообщений
Если репликация прошла успешно, то в 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" }'
15. Выполнение сценариев Jmeter
# Собираем образ 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
16. Удаление данных
Удаляем 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
Всем добра! Не болейте.
ссылка на оригинал статьи https://habr.com/ru/post/515584/
Добавить комментарий