I am a load testing engineer and have recently been working on a project where Apache Kafka is expected to be actively used. Due to the remote work mode, gaining access to the test environment took weeks. In order not to waste time, I decided to deploy a local stand in Kubernetes.
Those who have worked with Apache Kafka will confirm that the official documentation does not cover all the details of installation and configuration. I hope this step-by-step guide will help you to reduce the time it takes to deploy your test environment. I draw your attention to the fact that installing stateful in containers is far from a good idea, so this instruction is not intended for deploying an industrial stand.
The instructions describe how to create a virtual machine in VirtualBox, install and configure the operating system, install Docker, Kubernetes and a monitoring system. There are two Apache Kafka clusters deployed on Kubernetes: "production" and "backup". MirrorMaker 2.0 is used to replicate messages from production to backup. Communication between the nodes of the production cluster is protected by TLS. Unfortunately, I cannot upload a script for generating certificates to Git. As an example, you can use the certificates from the certs / certs.tar.gz archive. At the very end of the tutorial, you will learn how to deploy a Jmeter cluster and run a test script.
Sources are available in the repository: github.com/kildibaev/k8s-kafka
The instructions are designed for newcomers to Kubernetes, so if you already have experience with containers, you can go straight to the section "12. Deploying an Apache Kafka cluster" .
Q&A:
- Why is Ubuntu used? I originally deployed Kubernetes on CentOS 7, but after one of the updates, the environment stopped working. In addition, I noticed that on CentOS, load tests running in Jmeter behave unpredictably. If you encountered, please write in the comments a possible solution to this problem. Everything is much more stable in Ubuntu.
- Why not k3s or MicroK8s? In short, neither k3s nor MicroK8s out of the box know how to work with a local Docker repository.
- ? .
- Flannel? kubernetes Flannel β , .
- Docker, CRI-O? CRI-O .
- MirrorMaker 2.0 Kafka Connect? Kafka Connect MirrorMaker 2.0 " " REST API.
11. Prometheus, Grafana, Alert Manager Node Exporter
14.1. MirrorMaker 2.0 Kafka Connect
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 https://raw.githubusercontent.com/kildibaev/k8s-kafka/master/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