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())
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"
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())
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"
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.
- Check the subscriber logs:
kubectl logs -l app=subscriber
- Trigger the publisher manually:
kubectl delete pod -l app=publisher
- 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!![]()