Apache Kafka on NERC OpenShift
Apache Kafka Overview
Apache Kafka is a distributed event streaming platform capable of handling trillions of events per day. Originally developed at LinkedIn and open-sourced in 2011, Kafka is designed for high-throughput, fault-tolerant, and scalable real-time data pipelines and streaming applications.
Kafka uses a publish-subscribe model organized around the following core concepts:
- Broker: A Kafka server that stores and serves messages.
- Topic: A named stream to which producers publish records and from which consumers read records.
- Partition: Topics are split into partitions for parallelism and fault tolerance.
- Producer: A client application that publishes records to one or more topics.
- Consumer: A client application that subscribes to topics and processes records.
- Consumer Group: A group of consumers that collectively consume a topic, with each partition assigned to exactly one member.
Running Kafka on NERC OpenShift is the recommended approach for course workloads requiring persistent, scalable message streaming. This guide uses the Strimzi Operator, which is the standard Kubernetes-native method for deploying Kafka on OpenShift on NERC.
Prerequisites
Before proceeding, ensure you have:
- Access to a NERC OpenShift project
- The
ocCLI installed and authenticated to the NERC OpenShift cluster - Sufficient quota in your project (at least 3 vCPUs and 6 GiB memory recommended for a minimal Kafka cluster)
Checking Your Quota
You can view your project's resource quota by running:
If you need additional resources, contact your project PI or NERC support.
Deploy Kafka Using the Strimzi Operator
Strimzi provides a Kubernetes Operator that manages the full lifecycle of Kafka clusters on OpenShift. On NERC OpenShift, you will install Strimzi into your own namespace.
Install the Strimzi Operator
-
Log in to the NERC OpenShift cluster and switch to your project namespace:
For example:
-
Download the Strimzi installation YAML files. Always check the Strimzi releases page for the latest version:
STRIMZI_VERSION="0.50.1" wget https://github.com/strimzi/strimzi-kafka-operator/releases/download/${STRIMZI_VERSION}/strimzi-${STRIMZI_VERSION}.tar.gz tar -xzf strimzi-${STRIMZI_VERSION}.tar.gz cd strimzi-${STRIMZI_VERSION}Very Important Note
Check the Strimzi compatibility matrix to confirm the Strimzi version supports the Kafka version and Kubernetes/OpenShift version running on NERC. Mismatched versions can prevent the operator from starting. For Kafka 4.0+, use Strimzi 0.50.0 or later.
-
Update the installation files to use your project namespace. Replace all occurrences of
myprojectwith your actual namespace:For example:
sed -i '' 's/namespace: .*/namespace: ds551-2026-spring-9ab13b/' install/cluster-operator/*RoleBinding*.yamlMake sure to update the namespace
The
-n <your-project>flag explicitly specifies the namespace for all subsequentoccommands. Always include this flag when working with multiple projects to avoid accidentally operating on the wrong namespace. -
Apply the Strimzi Cluster Operator installation files:
-
Verify the operator pod is running:
The output should look similar to:
Note
It may take 1–2 minutes for the operator pod to reach
Runningstatus.
Create a Kafka Cluster
Once the Strimzi Operator is running, you can deploy a Kafka cluster by creating a Kafka custom resource and a KafkaNodePool resource.
Important: KafkaNodePool is Required
As of Kafka 4.0+, Strimzi uses KafkaNodePool to define broker and controller nodes. Both resources must be created together. The KafkaNodePool should define at least one node pool with both broker and controller roles for KRaft mode operation. Without a KafkaNodePool, the Kafka cluster will not deploy.
-
Create a file named
kafka-cluster.yamlwith the Kafka cluster definition:apiVersion: kafka.strimzi.io/v1 kind: KafkaNodePool metadata: name: dual-role namespace: <your-project> labels: strimzi.io/cluster: my-cluster spec: replicas: 1 roles: - broker - controller storage: type: persistent-claim size: 1Gi --- apiVersion: kafka.strimzi.io/v1beta2 kind: Kafka metadata: name: my-cluster namespace: <your-project> spec: kafka: version: 4.1.1 listeners: - name: plain port: 9092 type: internal tls: false - name: tls port: 9093 type: internal tls: true config: offsets.topic.replication.factor: 1 transaction.state.log.replication.factor: 1 transaction.state.log.min.isr: 1 default.replication.factor: 1 min.insync.replicas: 1 entityOperator: topicOperator: {} userOperator: {}Very Important Note
- Kafka 4.0+ requires
KafkaNodePoolwith bothbrokerandcontrollerroles for KRaft (Kraft Raft) consensus mode operation. - This configuration uses persistent storage (1Gi) suitable for testing and demo purposes. For production or larger workloads, increase the
sizevalue or use a specificstorageClass. See the Strimzi storage documentation for details. - Make sure the
KafkaNodePoolmetadata includes the labelstrimzi.io/cluster: my-clusterto link it to the Kafka resource.
- Kafka 4.0+ requires
-
Apply the Kafka cluster definition:
-
Watch the cluster come up. It may take 3–5 minutes for all pods to reach
Runningstatus:A healthy cluster will show output similar to:
NAME READY STATUS RESTARTS AGE my-cluster-dual-role-0 1/1 Running 0 3m my-cluster-entity-operator-6d7f9c7d4b-xqtlp 2/2 Running 0 2mNote about Kafka 4.0+ Differences
In Kafka 4.0+: - There are no ZooKeeper pods. The broker manages its own metadata using KRaft. - Pod names follow the pattern
<cluster-name>-<nodepool-name>-<id>. - With this single-node setup usingdual-role, you'll see pods namedmy-cluster-dual-role-0.
Create a Kafka Topic
-
Create a file named
kafka-topic.yaml: -
Apply the topic:
-
Verify the topic was created:
Expected output:
Test the Kafka Cluster
Strimzi ships with pre-built container images with Kafka command-line tools that you can use to verify your cluster is working correctly.
API Deprecation Warnings
You may see deprecation warnings about Kafka API versions during deployment and testing. These are safe to ignore. The deprecation warnings occur because the v1beta2 API version is being phased out in favor of v1. Your cluster will still function correctly.
Run a Producer
The producer tool lets you send messages to a Kafka topic. In interactive mode, you can type messages directly:
-
Start a producer pod in interactive mode:
oc run kafka-producer -ti \ --image=quay.io/strimzi/kafka:0.50.1-kafka-4.1.1 \ --rm=true --restart=Never \ -n <your-project> \ -- bash -c 'bin/kafka-console-producer.sh \ --bootstrap-server my-cluster-kafka-bootstrap:9092 \ --topic my-topic'The
-tiflags enable interactive terminal mode, which allows you to type messages at a prompt. The--rm=trueflag automatically removes the pod after it exits. -
At the prompt, type test messages and press
Enterafter each one:Press
Ctrl+Cto stop the producer and exit.Important: Interactive Mode (
-ti --rm)The
-ti --rmflags work together to create an interactive session that automatically cleans up the pod. Do not use these flags in scripts or CI/CD pipelines—instead, pipe your messages to stdin or use a heredoc. For example:
Run a Consumer
-
In a separate terminal, start a consumer pod to read messages from the beginning:
oc run kafka-consumer -ti \ --image=quay.io/strimzi/kafka:0.50.1-kafka-4.1.1 \ --rm=true --restart=Never \ -n <your-project> \ -- bash -c 'bin/kafka-console-consumer.sh \ --bootstrap-server my-cluster-kafka-bootstrap:9092 \ --topic my-topic \ --from-beginning'You should see the messages published by the producer:
Press
Ctrl+Cto stop the consumer.Consumer Groups
To test multiple consumers sharing a topic workload, add the flag
--group <group-name>to the consumer command. Each consumer in the same group will receive messages from a distinct subset of partitions.
Connecting Applications to Kafka
Applications running inside the same OpenShift project can reach the Kafka broker using the internal bootstrap address:
For Python applications, use the kafka-python or confluent-kafka client libraries:
from kafka import KafkaProducer, KafkaConsumer
# Producer example
producer = KafkaProducer(bootstrap_servers='my-cluster-kafka-bootstrap:9092')
producer.send('my-topic', b'Hello from Python!')
producer.flush()
# Consumer example
consumer = KafkaConsumer(
'my-topic',
bootstrap_servers='my-cluster-kafka-bootstrap:9092',
auto_offset_reset='earliest',
group_id='my-group'
)
for msg in consumer:
print(f"Received: {msg.value.decode()}")
Note
The bootstrap address my-cluster-kafka-bootstrap is an OpenShift Service created automatically by Strimzi. It is only reachable from within the same project namespace. If you need external access, configure a route or loadbalancer type listener in the Kafka CR.
Clean Up Resources
When you are finished, remove all Kafka resources to free up project quota:
# Delete the Kafka topic
oc delete kafkatopic my-topic -n <your-project>
# Delete the Kafka cluster (also removes Entity Operator pods)
oc delete kafka my-cluster -n <your-project>
# If using KafkaNodePool (in some configurations), delete it as well
oc delete kafkanodepool dual-role -n <your-project> 2>/dev/null || true
# Remove the Strimzi Operator
oc delete -f install/cluster-operator/ -n <your-project>
Very Important Note
Deleting the Kafka cluster with ephemeral storage permanently destroys all messages stored in that cluster. Make sure you have consumed or exported any data you need before running these commands.