Building Event-Driven Architectures with Kubernetes and NATS

Introduction

Modern cloud-native applications demand scalability, flexibility, and resilience. Traditional request-response communication patterns often lead to tight coupling between services, making them hard to scale independently. Event-driven architectures (EDA) solve this by enabling asynchronous, loosely coupled communication between microservices.

In this article, we will explore how to build an event-driven system using NATS (a lightweight, high-performance messaging system) on Kubernetes. We will:

  • Deploy a NATS messaging broker
  • Create a publisher service that emits events
  • Develop a subscriber service that listens and processes events
  • Demonstrate event-driven communication with Kubernetes

Prerequisites

Before we begin, ensure you have the following:

  • A running Kubernetes cluster (Minikube, k3s, or a self-managed cluster)
  • kubectl installed and configured
  • Helm installed for deploying NATS
  • Docker installed for building container images

Step 1: Deploy NATS on Kubernetes

We will use Helm to deploy NATS.

Install NATS using Helm

helm repo add nats https://nats-io.github.io/k8s/helm/charts/
helm repo update
helm install nats nats/nats --namespace default

Verify that the NATS pods are running:

kubectl get pods -l app.kubernetes.io/name=nats

Step 2: Create a Publisher Service

Our publisher will send messages to NATS on a specific subject.

Publisher Code (publisher.py)

import nats
import asyncio

async def main():
    nc = await nats.connect("nats://nats.default.svc.cluster.local:4222")
    await nc.publish("events.data", b"Hello, this is an event message!")
    print("Message sent!")
    await nc.close()

if __name__ == "__main__":
    asyncio.run(main())
Click Here to Copy Python Code

Dockerfile for Publisher

FROM python:3.8
WORKDIR /app
COPY publisher.py .
RUN pip install nats-py
CMD ["python", "publisher.py"]

Build the Image

docker build -t mygrpc-server:latest .

Publisher Deployment YAML

apiVersion: apps/v1
kind: Deployment
metadata:
  name: publisher
spec:
  replicas: 1
  selector:
    matchLabels:
      app: publisher
  template:
    metadata:
      labels:
        app: publisher
    spec:
      containers:
        - name: publisher
          image: mygrpc-server:latest
          env:
            - name: NATS_SERVER
              value: "nats://nats.default.svc.cluster.local:4222"
Click Here to Copy YAML

Deploy the publisher:

kubectl apply -f publisher-deployment.yaml

Step 3: Create a Subscriber Service

Our subscriber listens to the events.data subject and processes messages.

Subscriber Code (subscriber.py)

import nats
import asyncio

async def message_handler(msg):
    subject = msg.subject
    data = msg.data.decode()
    print(f"Received message on {subject}: {data}")

async def main():
    nc = await nats.connect("nats://nats.default.svc.cluster.local:4222")
    await nc.subscribe("events.data", cb=message_handler)
    print("Listening for events...")
    while True:
        await asyncio.sleep(1)

if __name__ == "__main__":
    asyncio.run(main())
Click Here to Copy Python Code

Dockerfile for Subscriber

FROM python:3.8
WORKDIR /app
COPY subscriber.py .
RUN pip install nats-py
CMD ["python", "subscriber.py"]

Build the Image

docker build -t mygrpc-subscriber:latest .

Subscriber Deployment YAML

apiVersion: apps/v1
kind: Deployment
metadata:
  name: subscriber
spec:
  replicas: 1
  selector:
    matchLabels:
      app: subscriber
  template:
    metadata:
      labels:
        app: subscriber
    spec:
      containers:
        - name: subscriber
          image: mygrpc-subscriber:latest
          env:
            - name: NATS_SERVER
              value: "nats://nats.default.svc.cluster.local:4222"
Click Here to Copy YAML

Deploy the subscriber:

kubectl apply -f subscriber-deployment.yaml

Step 4: Test the Event-Driven Architecture

Once all components are deployed, check logs for event propagation.

  1. Check the subscriber logs:
kubectl logs -l app=subscriber
  1. Trigger the publisher manually:
kubectl delete pod -l app=publisher
  1. Observe subscriber receiving events: If everything is set up correctly, the subscriber should print:
Received message on events.data: Hello, this is an event message!

Conclusion

We successfully built an event-driven system using Kubernetes and NATS. This architecture allows microservices to communicate asynchronously, improving scalability, resilience, and maintainability.

Key takeaways:

  • NATS simplifies pub-sub messaging in Kubernetes.
  • Event-driven patterns decouple services and improve scalability.
  • Kubernetes provides a flexible infrastructure to deploy and manage such systems.

This architecture can be extended with multiple subscribers, durable streams, and event filtering for more advanced use cases. If you have any questions let me know in the comments!👇

Leave a comment