Kafka Cluster using Containers - p1
Building a Kafka cluster using containers
Environment
Four servers running Debian 11
kfk231.domain.dom - 192.168.122.231 - zookeeper, broker, connect
kfk232.domain.dom - 192.168.122.232 - zookeeper, broker, connect
kfk233.domain.dom - 192.168.122.233 - zookeeper, broker, connect
kfk234.domain.dom - 192.168.122.234 - utilities
Docker 23.0.5
All four servers have a root drive and a second drive used for storing the data.
Docker's data-root points to the /data drive.
Also all the compose files will be run from the /data drive from their own directories.
Creating Certificates
Working on the fourth server, kfk234.
Installing openjdk-17-jre-headless:
apt install openjdk-17-jre-headless
which keytool
/usr/bin/keytool
Creating directories - on the second drive /data.
mkdir kafka
cd kafka
mkdir certificates
cd certificates/
mkdir cetificates-single
cd cetificates-single/
Creating an .env file to store the password for certificate.
.env file
SRVPASS=Password123
Config file for csr
openssl-config.cnf
[ v3_req ]
# Extensions to add to a certificate request
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = kfk231
DNS.2 = kfk231-kafka
DNS.3 = kfk231-zk
DNS.4 = kfk231-cnt
DNS.5 = kfk231-reg
DNS.6 = kfk231.domain.dom
DNS.7 = kfk232
DNS.8 = kfk232-kafka
DNS.9 = kfk232-zk
DNS.10 = kfk232-cnt
DNS.11 = kfk232-reg
DNS.12 = kfk232.domain.dom
DNS.13 = kfk233
DNS.14 = kfk233-kafka
DNS.15 = kfk233-zk
DNS.16 = kfk233-cnt
DNS.17 = kfk233-reg
DNS.18 = kfk233.domain.dom
DNS.19 = kfk234
DNS.20 = kfk234.domain.dom
DNS.21 = IP:192.168.122.231
DNS.22 = IP:192.168.122.232
DNS.23 = IP:192.168.122.233
DNS.24 = IP:192.168.122.234
The certificate create script create_certificate.sh:
#!/bin/bash
set -o nounset \
-o errexit \
-o verbose \
-o xtrace
# Get password from .env file
export $(grep -v '^#' .env | xargs)
# Generate CA key
openssl req -new \
-newkey rsa:4096 \
-x509 -keyout kfk-ca.key \
-out kfk-ca.crt \
-days 365 \
-subj '/CN=kfk/OU=Test/O=domain.dom/L=Toronto/S=On/C=CA' \
-passin pass:$SRVPASS \
-passout pass:$SRVPASS
i=kfk-cluster
echo $i
# Create keystores
keytool -genkey -noprompt \
-alias $i \
-dname "CN=*.domain.dom" \
-keystore kafka.$i.keystore.jks \
-keyalg RSA \
-validity 365 \
-storetype pkcs12 \
-storepass $SRVPASS \
-keypass $SRVPASS
# Create CSR, sign the key and import back into keystore
keytool -noprompt -keystore kafka.$i.keystore.jks \
-alias $i -certreq \
-sigalg SHA256WITHRSA \
-file $i.csr \
-storepass $SRVPASS \
-keypass $SRVPASS
openssl x509 -req -CA kfk-ca.crt \
-CAkey kfk-ca.key \
-in $i.csr \
-out $i-ca-signed.crt \
-days 365 \
-CAcreateserial \
-extfile openssl-config.cnf \
-extensions v3_req \
-passin pass:$SRVPASS
# import the public CA cert
keytool -noprompt -keystore kafka.$i.keystore.jks \
-alias CARoot \
-import \
-file kfk-ca.crt \
-storepass $SRVPASS \
-keypass $SRVPASS
# import the signed cert
keytool -noprompt -keystore kafka.$i.keystore.jks \
-alias $i -import \
-file $i-ca-signed.crt \
-storepass $SRVPASS \
-keypass $SRVPASS
# Create truststore and import the CA cert.
keytool -noprompt -keystore kafka.$i.truststore.jks \
-alias CARoot \
-import \
-file kfk-ca.crt \
-storepass $SRVPASS \
-keypass $SRVPASS
echo "$SRVPASS" > ${i}_sslkey_creds
echo "$SRVPASS" > ${i}_keystore_creds
echo "$SRVPASS" > ${i}_truststore_creds
chmod +x create_certificate.sh
./create_single_certificate.sh
The certificates and keys will be copied in secrets directory with the folder from where the compose files are run. That secrets directory will be mapped inside the container.
Creating directories
These would be on the second drive (/data)
On each of the three servers (kfk231, kfk232, kfk233) which will have zookeeper, broker, and cnnect
cd /data
mkdir kafka
cd kafka
mkdir connectors
mkdir data
mkdir secrets
chmod -R 755 data
On the fourth server (kfk234)
cd /data
mkdir kafka
cd kafka
mkdir secrets
Zookeeper
Same on each the three cluster nodes (kfk231, kfk232, kfk233)
Working in /data/kafka directory
On kfk231 - kfk231-zookeeper-compose.yaml
version: '2.4'
name: kfk231-zk
services:
kfk231-zk:
image: confluentinc/cp-zookeeper:6.2.1
hostname: kfk231.domain.dom
container_name: kfk231-zk
restart: "unless-stopped"
mem_limit: 512m
memswap_limit: 512m
mem_swappiness: 0
network_mode: "host"
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_SERVER_ID: 1
ZOOKEEPER_SERVERS: kfk231:2888:3888;kfk232:2888:3888;kfk233:2888:3888
extra_hosts:
- "kfk231:192.168.122.231"
- "kfk231-kafka:192.168.122.231"
- "kfk231-zk:192.168.122.231"
- "kfk231-cnt:192.168.122.231"
- "kfk231-reg:192.168.122.231"
- "kfk231.domain.dom:192.168.122.231"
- "kfk232:192.168.122.232"
- "kfk232-kafka:192.168.122.232"
- "kfk232-zk:192.168.122.232"
- "kfk232-cnt:192.168.122.232"
- "kfk232-reg:192.168.122.232"
- "kfk232.domain.dom:192.168.122.232"
- "kfk233:192.168.122.233"
- "kfk233-kafka:192.168.122.233"
- "kfk233-zk:192.168.122.233"
- "kfk233-cnt:192.168.122.233"
- "kfk233-reg:192.168.122.233"
- "kfk233.domain.dom:192.168.122.233"
- "kfk234:192.168.122.234"
- "kfk234.domain.dom:192.168.122.234"
docker compose -f kfk231-zookeeper-compose.yaml up -d
On kfk232 - kfk232-zookeeper-compose.yaml
version: '2.4'
name: kfk232-zk
services:
kfk232-zk:
image: confluentinc/cp-zookeeper:6.2.1
hostname: kfk232.domain.dom
container_name: kfk232-zk
restart: "unless-stopped"
mem_limit: 512m
memswap_limit: 512m
mem_swappiness: 0
network_mode: "host"
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_SERVER_ID: 2
ZOOKEEPER_SERVERS: kfk231:2888:3888;kfk232:2888:3888;kfk233:2888:3888
extra_hosts:
- "kfk231:192.168.122.231"
- "kfk231-kafka:192.168.122.231"
- "kfk231-zk:192.168.122.231"
- "kfk231-cnt:192.168.122.231"
- "kfk231-reg:192.168.122.231"
- "kfk231.domain.dom:192.168.122.231"
- "kfk232:192.168.122.232"
- "kfk232-kafka:192.168.122.232"
- "kfk232-zk:192.168.122.232"
- "kfk232-cnt:192.168.122.232"
- "kfk232-reg:192.168.122.232"
- "kfk232.domain.dom:192.168.122.232"
- "kfk233:192.168.122.233"
- "kfk233-kafka:192.168.122.233"
- "kfk233-zk:192.168.122.233"
- "kfk233-cnt:192.168.122.233"
- "kfk233-reg:192.168.122.233"
- "kfk233.domain.dom:192.168.122.233"
- "kfk234:192.168.122.234"
- "kfk234.domain.dom:192.168.122.234"
docker compose -f kfk232-zookeeper-compose.yaml up -d
On kfk233 - kfk233-zookeeper-compose.yaml
version: '2.4'
name: kfk233-zk
services:
kfk233-zk:
image: confluentinc/cp-zookeeper:6.2.1
hostname: kfk233.domain.dom
container_name: kfk233-zk
restart: "unless-stopped"
mem_limit: 512m
memswap_limit: 512m
mem_swappiness: 0
network_mode: "host"
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ZOOKEEPER_SERVER_ID: 3
ZOOKEEPER_SERVERS: kfk231:2888:3888;kfk232:2888:3888;kfk233:2888:3888
extra_hosts:
- "kfk231:192.168.122.231"
- "kfk231-kafka:192.168.122.231"
- "kfk231-zk:192.168.122.231"
- "kfk231-cnt:192.168.122.231"
- "kfk231-reg:192.168.122.231"
- "kfk231.domain.dom:192.168.122.231"
- "kfk232:192.168.122.232"
- "kfk232-kafka:192.168.122.232"
- "kfk232-zk:192.168.122.232"
- "kfk232-cnt:192.168.122.232"
- "kfk232-reg:192.168.122.232"
- "kfk232.domain.dom:192.168.122.232"
- "kfk233:192.168.122.233"
- "kfk233-kafka:192.168.122.233"
- "kfk233-zk:192.168.122.233"
- "kfk233-cnt:192.168.122.233"
- "kfk233-reg:192.168.122.233"
- "kfk233.domain.dom:192.168.122.233"
- "kfk234:192.168.122.234"
- "kfk234.domain.dom:192.168.122.234"
docker compose -f kfk233-zookeeper-compose.yaml up -d
Kafka Broker
Same on each the three cluster nodes (kfk231, kfk232, kfk233)
Working in /data/kafka directory
On kfk231 - kfk231-kafka-broker-compose.yaml
version: '2.4'
name: kfk231-kafka
services:
kfk231-kafka:
image: confluentinc/cp-kafka:6.2.1
hostname: kfk231.domain.dom
container_name: kfk231-kafka
restart: "unless-stopped"
# Increase open files limit
ulimits:
nofile:
soft: 100000
hard: 100000
mem_limit: 1500m
memswap_limit: 1500m
# Disable RAM swap
mem_swappiness: 0
network_mode: "host"
volumes:
- ./data/kafka:/data/kafka/
- ./secrets:/etc/kafka/secrets
- ./jolokia-jvm-1.7.1.jar:/usr/app/jolokia-jvm-1.7.1.jar
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://192.168.122.231:9092,SSL://192.168.122.231:9093
KAFKA_SECURITY_INTER_BROKER_PROTOCOL: SSL
KAFKA_ZOOKEEPER_CONNECT: "kfk231:2181,kfk232:2181,kfk233:2181/kafka"
KAFKA_BROKER_ID: 1
KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO"
KAFKA_DEFAULT_REPLICATION_FACTOR: 3
KAFKA_NUM_PARTITIONS: 6
KAFKA_MIN_INSYNC_REPLICAS: 2
KAFKA_LOG_RETENTION_HOURS: 168
KAFKA_OFFSETS_RETENTION_MINUTES: 4320
KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS: 6000
KAFKA_AUTO_CREATE_TOPICS_ENABLE: true
KAFKA_DELETE_TOPIC_ENABLE: true
KAFKA_LOG_DIRS: /data/kafka
KAFKA_SSL_KEYSTORE_FILENAME: kafka.kfk-cluster.keystore.jks
KAFKA_SSL_KEYSTORE_CREDENTIALS: kfk-cluster_keystore_creds
KAFKA_SSL_KEY_CREDENTIALS: kfk-cluster_sslkey_creds
KAFKA_SSL_TRUSTSTORE_FILENAME: kafka.kfk-cluster.truststore.jks
KAFKA_SSL_TRUSTSTORE_CREDENTIALS: kfk-cluster_truststore_creds
KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: " "
KAFKA_SSL_CLIENT_AUTH: required
KAFKA_JMX_HOSTNAME: "kfk231"
KAFKA_JMX_PORT: 9999
KAFKA_JMX_OPTS: "-Djava.rmi.server.hostname=kfk231
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.rmi.port=9999
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-javaagent:/usr/app/jolokia-jvm-1.7.1.jar=host=*"
extra_hosts:
- "kfk231:192.168.122.231"
- "kfk231-kafka:192.168.122.231"
- "kfk231-zk:192.168.122.231"
- "kfk231-cnt:192.168.122.231"
- "kfk231-reg:192.168.122.231"
- "kfk231.domain.dom:192.168.122.231"
- "kfk232:192.168.122.232"
- "kfk232-kafka:192.168.122.232"
- "kfk232-zk:192.168.122.232"
- "kfk232-cnt:192.168.122.232"
- "kfk232-reg:192.168.122.232"
- "kfk232.domain.dom:192.168.122.232"
- "kfk233:192.168.122.233"
- "kfk233-kafka:192.168.122.233"
- "kfk233-zk:192.168.122.233"
- "kfk233-cnt:192.168.122.233"
- "kfk233-reg:192.168.122.233"
- "kfk233.domain.dom:192.168.122.233"
- "kfk234:192.168.122.234"
- "kfk234.domain.dom:192.168.122.234"
docker compose -f kfk231-kafka-broker-compose.yaml up -d
On kfk232 - kfk232-kafka-broker-compose.yaml
version: '2.4'
name: kfk232-kafka
services:
kfk232-kafka:
image: confluentinc/cp-kafka:6.2.1
hostname: kfk232.domain.dom
container_name: kfk232-kafka
restart: "unless-stopped"
# Increase open files limit
ulimits:
nofile:
soft: 100000
hard: 100000
mem_limit: 1500m
memswap_limit: 1500m
# Disable RAM swap
mem_swappiness: 0
network_mode: "host"
volumes:
- ./data/kafka:/data/kafka/
- ./secrets:/etc/kafka/secrets
- ./jolokia-jvm-1.7.1.jar:/usr/app/jolokia-jvm-1.7.1.jar
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://192.168.122.232:9092,SSL://192.168.122.232:9093
KAFKA_SECURITY_INTER_BROKER_PROTOCOL: SSL
KAFKA_ZOOKEEPER_CONNECT: "kfk231:2181,kfk232:2181,kfk233:2181/kafka"
KAFKA_BROKER_ID: 2
KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO"
KAFKA_DEFAULT_REPLICATION_FACTOR: 3
KAFKA_NUM_PARTITIONS: 6
KAFKA_MIN_INSYNC_REPLICAS: 2
KAFKA_LOG_RETENTION_HOURS: 168
KAFKA_OFFSETS_RETENTION_MINUTES: 4320
KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS: 6000
KAFKA_AUTO_CREATE_TOPICS_ENABLE: true
KAFKA_DELETE_TOPIC_ENABLE: true
KAFKA_LOG_DIRS: /data/kafka
KAFKA_SSL_KEYSTORE_FILENAME: kafka.kfk-cluster.keystore.jks
KAFKA_SSL_KEYSTORE_CREDENTIALS: kfk-cluster_keystore_creds
KAFKA_SSL_KEY_CREDENTIALS: kfk-cluster_sslkey_creds
KAFKA_SSL_TRUSTSTORE_FILENAME: kafka.kfk-cluster.truststore.jks
KAFKA_SSL_TRUSTSTORE_CREDENTIALS: kfk-cluster_truststore_creds
KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: " "
KAFKA_SSL_CLIENT_AUTH: required
KAFKA_JMX_HOSTNAME: "kfk232"
KAFKA_JMX_PORT: 9999
KAFKA_JMX_OPTS: "-Djava.rmi.server.hostname=kfk232
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.rmi.port=9999
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-javaagent:/usr/app/jolokia-jvm-1.7.1.jar=host=*"
extra_hosts:
- "kfk231:192.168.122.231"
- "kfk231-kafka:192.168.122.231"
- "kfk231-zk:192.168.122.231"
- "kfk231-cnt:192.168.122.231"
- "kfk231-reg:192.168.122.231"
- "kfk231.domain.dom:192.168.122.231"
- "kfk232:192.168.122.232"
- "kfk232-kafka:192.168.122.232"
- "kfk232-zk:192.168.122.232"
- "kfk232-cnt:192.168.122.232"
- "kfk232-reg:192.168.122.232"
- "kfk232.domain.dom:192.168.122.232"
- "kfk233:192.168.122.233"
- "kfk233-kafka:192.168.122.233"
- "kfk233-zk:192.168.122.233"
- "kfk233-cnt:192.168.122.233"
- "kfk233-reg:192.168.122.233"
- "kfk233.domain.dom:192.168.122.233"
- "kfk234:192.168.122.234"
- "kfk234.domain.dom:192.168.122.234"
docker compose -f kfk232-kafka-broker-compose.yaml up -d
On kfk233 - kfk233-kafka-broker-compose.yaml
version: '2.4'
name: kfk233-kafka
services:
kfk233-kafka:
image: confluentinc/cp-kafka:6.2.1
hostname: kfk233.domain.dom
container_name: kfk233-kafka
restart: "unless-stopped"
# Increase open files limit
ulimits:
nofile:
soft: 100000
hard: 100000
mem_limit: 1500m
memswap_limit: 1500m
# Disable RAM swap
mem_swappiness: 0
network_mode: "host"
volumes:
- ./data/kafka:/data/kafka/
- ./secrets:/etc/kafka/secrets
- ./jolokia-jvm-1.7.1.jar:/usr/app/jolokia-jvm-1.7.1.jar
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://192.168.122.233:9092,SSL://192.168.122.233:9093
KAFKA_SECURITY_INTER_BROKER_PROTOCOL: SSL
KAFKA_ZOOKEEPER_CONNECT: "kfk231:2181,kfk232:2181,kfk233:2181/kafka"
KAFKA_BROKER_ID: 3
KAFKA_LOG4J_LOGGERS: "kafka.controller=INFO,kafka.producer.async.DefaultEventHandler=INFO,state.change.logger=INFO"
KAFKA_DEFAULT_REPLICATION_FACTOR: 3
KAFKA_NUM_PARTITIONS: 6
KAFKA_MIN_INSYNC_REPLICAS: 2
KAFKA_LOG_RETENTION_HOURS: 168
KAFKA_OFFSETS_RETENTION_MINUTES: 4320
KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS: 6000
KAFKA_AUTO_CREATE_TOPICS_ENABLE: true
KAFKA_DELETE_TOPIC_ENABLE: true
KAFKA_LOG_DIRS: /data/kafka
KAFKA_SSL_KEYSTORE_FILENAME: kafka.kfk-cluster.keystore.jks
KAFKA_SSL_KEYSTORE_CREDENTIALS: kfk-cluster_keystore_creds
KAFKA_SSL_KEY_CREDENTIALS: kfk-cluster_sslkey_creds
KAFKA_SSL_TRUSTSTORE_FILENAME: kafka.kfk-cluster.truststore.jks
KAFKA_SSL_TRUSTSTORE_CREDENTIALS: kfk-cluster_truststore_creds
KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: " "
KAFKA_SSL_CLIENT_AUTH: required
KAFKA_JMX_HOSTNAME: "kfk233"
KAFKA_JMX_PORT: 9999
KAFKA_JMX_OPTS: "-Djava.rmi.server.hostname=kfk233
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.rmi.port=9999
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-javaagent:/usr/app/jolokia-jvm-1.7.1.jar=host=*"
extra_hosts:
- "kfk231:192.168.122.231"
- "kfk231-kafka:192.168.122.231"
- "kfk231-zk:192.168.122.231"
- "kfk231-cnt:192.168.122.231"
- "kfk231-reg:192.168.122.231"
- "kfk231.domain.dom:192.168.122.231"
- "kfk232:192.168.122.232"
- "kfk232-kafka:192.168.122.232"
- "kfk232-zk:192.168.122.232"
- "kfk232-cnt:192.168.122.232"
- "kfk232-reg:192.168.122.232"
- "kfk232.domain.dom:192.168.122.232"
- "kfk233:192.168.122.233"
- "kfk233-kafka:192.168.122.233"
- "kfk233-zk:192.168.122.233"
- "kfk233-cnt:192.168.122.233"
- "kfk233-reg:192.168.122.233"
- "kfk233.domain.dom:192.168.122.233"
- "kfk234:192.168.122.234"
- "kfk234.domain.dom:192.168.122.234"
docker compose -f kfk233-kafka-broker-compose.yaml up -d