A comprehensive, hands-on reference project for building, containerising, and deploying a cloud-native microservices architecture from scratch โ using Spring Boot, Spring Cloud, Docker, Kubernetes, and RabbitMQ.
This project walks through every stage of building a production-style microservices system:
- Maven multi-module project structure with a parent POM
- Synchronous communication via REST Template โ Eureka Service Discovery โ OpenFeign
- Asynchronous messaging with RabbitMQ (AMQP)
- Distributed tracing with Spring Cloud Sleuth + Zipkin
- API Gateway routing with Spring Cloud Gateway
- Containerisation using Google Jib (no Dockerfile needed)
- Orchestration with Kubernetes (Minikube locally, Linode in cloud)
- Security with API Key authentication at the gateway level
โโโโโโโโโโโโโโโโโโโ
HTTP โ API Gateway โ :8083
Client โโโโโโโโโโโโโโโโถ โ (apigw) โ
โโโโโโโโโโฌโโโโโโโโโ
โ routes via Eureka / K8s DNS
โโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโ
โผ โผ โผ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ Customer โ โ Fraud โ โ Notification โ
โ Service โ โ Service โ โ Service โ
โ :8080 โ โ :8081 โ โ :8082 โ
โโโโโโโโฌโโโโโโโ โโโโโโโโฌโโโโโโโ โโโโโโโโโโฌโโโโโโโโโโ
โ Feign (sync) โ โฒ
โโโโโโโโโโโโโโโโโโโ RabbitMQ โ (async)
โโโโโโโโโโโดโโโโโโโโโ
โ RabbitMQ โ
โ :5672 / :15672 โ
โโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโ
โ Eureka Server โ โ Zipkin UI โ โ PostgreSQL โ
โ :8761 โ โ :9411 โ โ :5432 โ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโ
| Module | Description | Port |
|---|---|---|
customer |
Customer registration service. Validates registrations, calls Fraud service synchronously, and publishes async events to RabbitMQ | 8080 |
fraud |
Fraud detection service. Called synchronously by Customer via OpenFeign; checks if a customer registration is fraudulent | 8081 |
notification |
Notification service. Listens on a RabbitMQ queue and persists notification records to the database asynchronously | 8082 |
apigw |
Spring Cloud API Gateway โ single external entry point. Routes requests to downstream services and integrates with Eureka for load balancing | 8083 |
eureka-server |
Netflix Eureka service registry. All microservices register here so they can discover and call each other without hardcoded hostnames/ports | 8761 |
clients |
Shared library of OpenFeign client interfaces used by other services for inter-service HTTP calls | โ |
amqp |
Shared RabbitMQ/AMQP configuration library. Provides RabbitMQMessageProducer and Jackson message converter setup |
โ |
| Layer | Technology | Purpose |
|---|---|---|
| Language | Java 17 | Core language (Eclipse Temurin JRE in containers) |
| Framework | Spring Boot 2.5.7 | Application framework |
| Service Discovery | Spring Cloud Netflix Eureka | Dynamic service registration & lookup |
| API Gateway | Spring Cloud Gateway | Single entry point, path-based routing, load balancing |
| Inter-service calls | Spring Cloud OpenFeign | Declarative REST client with Eureka integration |
| Async messaging | RabbitMQ 3.9 (AMQP 0-9-1) | Decoupled async communication between services |
| Distributed tracing | Spring Cloud Sleuth + Zipkin | End-to-end request tracing across services |
| Persistence | PostgreSQL | Relational database for all services |
| DB Admin UI | pgAdmin 4 | Web-based PostgreSQL management |
| Caching | Caffeine | In-memory cache (shared via parent POM) |
| Build | Maven (multi-module) | Multi-module project management & build lifecycle |
| Containerisation | Google Jib | Builds optimised Docker images without a Dockerfile or Docker daemon |
| Orchestration | Kubernetes (Minikube / Linode) | Container orchestration, scaling, zero-downtime deploys |
| Boilerplate | Lombok | Reduces boilerplate (getters, builders, logging) |
- Java 17 (Eclipse Temurin recommended)
- Maven 3.8+
- Docker & Docker Compose
- (Optional) Minikube + kubectl for Kubernetes deployment
- (Optional) Docker Hub account for pushing images with Jib
Start PostgreSQL, pgAdmin, RabbitMQ and Zipkin locally:
docker compose up -d| Service | URL | Credentials |
|---|---|---|
| pgAdmin | http://localhost:5050 | b.dostumski@syscomz.com / password |
| RabbitMQ Management | http://localhost:15672 | guest / guest |
| Zipkin UI | http://localhost:9411 | โ |
| PostgreSQL | localhost:5432 |
syscomz / password |
# Build all modules from the root
mvn clean install -DskipTests
# Start each service (in order)
cd eureka-server && mvn spring-boot:run
cd apigw && mvn spring-boot:run
cd customer && mvn spring-boot:run
cd fraud && mvn spring-boot:run
cd notification && mvn spring-boot:runOr use java -jar:
java -jar customer/target/customer-1.0-SNAPSHOT.jar๐ก Tip: To run multiple instances of a service (for load balancing testing), duplicate the run configuration in your IDE and add
--server.port=8085to Program Arguments.
Uses pre-built images from Docker Hub (includes all microservices):
docker-compose -f docker-compose-v1.yml up -d| Service | URL |
|---|---|
| Customer API | http://localhost:8080 |
| Fraud API | http://localhost:8081 |
| Notification API | http://localhost:8082 |
| API Gateway | http://localhost:8083 |
| Eureka Dashboard | http://localhost:8761 |
๐ก Use
docker-compose pullfirst to get the latest images from Docker Hub.
Images are built and pushed to Docker Hub automatically during mvn package via Google Jib. No Dockerfile required.
# Build & push all microservices
mvn clean package
# Build & push only the API Gateway (using a Maven profile)
cd apigw
mvn clean package -P build-docker-imageMake sure you're logged in before pushing:
docker loginImages are published as bdostumski/<service-name>:latest, e.g. bdostumski/customer:latest.
โ ๏ธ Ifmvn -versionshows the wrong Java version, fix it with:export JAVA_HOME=$(/usr/libexec/java_home -v 17)
# Start Minikube with enough memory
minikube start --memory=14g
# Apply bootstrap resources (namespaces, PVCs, etc.)
kubectl apply -f k8s/minikube/bootstrap/
# Deploy all services
kubectl apply -f k8s/minikube/services/
# Expose LoadBalancer services (required for Minikube)
minikube tunnelโน๏ธ When running on Kubernetes, Eureka is disabled โ K8s DNS handles service discovery natively.
# Set kubeconfig to your Linode cluster
export KUBECONFIG=~/syscomz-kubeconfig.yml
# Apply all resources
kubectl apply -f k8s/minikube/bootstrap/
kubectl apply -f k8s/minikube/services/
# Verify
kubectl get po
kubectl get svcEvery service is instrumented with Spring Cloud Sleuth, which automatically injects and propagates a TraceId and SpanId into every log line and across HTTP/AMQP boundaries.
Example log output:
[customer, a86f2368573ce95f, a86f2368573ce95f] โ same TraceId, root Span
[fraud, a86f2368573ce95f, 4bdf86c263154fe5] โ same TraceId, new Span
[notification, a86f2368573ce95f, c64809a52a015a37] โ same TraceId, new Span
All trace data is forwarded to Zipkin for visualisation:
๐ Open http://localhost:9411 and click Run Query to view traces.
The Customer service publishes notification events to a RabbitMQ exchange. The Notification service listens on a dedicated queue and persists the message to the database โ completely decoupled.
AMQP Exchange types used:
| Type | Behaviour |
|---|---|
| Direct | Routes to queues where routing-key == binding-key |
| Fanout | Broadcasts to all bound queues |
| Topic | Partial match routing (e.g. foo.* matches foo.bar) |
| Headers (Default/Nameless) | Routes where routing-key == queue name |
๐ก If the Notification service goes down, RabbitMQ holds the messages in the queue until the service recovers. Messages are only removed after the consumer sends an ACK.
When to use RabbitMQ vs Kafka:
- RabbitMQ โ best for task queues, complex routing, inter-service messaging, and scenarios where messages should be removed once consumed.
- Kafka โ best for high-throughput event streaming, data pipelines, replay scenarios, and audit logs.
The apigw module uses Spring Cloud Gateway to route all external traffic. It integrates with Eureka for client-side load balancing using the Round Robin algorithm (requests distributed sequentially across service instances).
Key gateway responsibilities:
- Path-based routing โ e.g.
/api/v1/customers/**โcustomerservice - Load balancing across multiple service instances
- TLS termination (delegated to cloud provider in production)
- Authentication (API Key filter โ see Security section)
๐ก In production, delegate the external load balancer to your cloud provider (AWS ALB, GCP Cloud Load Balancing, Azure Application Gateway). Use Spring Cloud Gateway only as an internal ingress.
The gateway enforces API Key authentication. Each client presents a key which is validated at the apigw filter before the request is forwarded to the private network.
Master Node (Control Plane) Worker Nodes
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ
โ API Server โ kubectl โ โ Kubelet โ
โ Scheduler โโโโโโโโถโ Container Runtime โ
โ Controller Manager โ โ Kube Proxy โ
โ etcd (cluster state) โ โ Pods (our apps) โ
โ Cloud Controller Manager โ โโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
| Component | Role |
|---|---|
| API Server | Frontend for the control plane; all kubectl commands go here |
| Scheduler | Assigns new pods to nodes based on resource availability |
| etcd | Distributed key-value store; single source of truth for cluster state |
| Controller Manager | Runs Node, ReplicaSet, Endpoint, and other controllers to reconcile desired vs actual state |
| Cloud Controller | Integrates with cloud provider for load balancers, storage, and VMs |
| Kubelet | Agent on each node; manages pod lifecycle |
| Kube Proxy | Handles network routing rules on each node |
| Resource | Purpose |
|---|---|
| Deployment | Manages rolling updates and ReplicaSets for stateless services |
| StatefulSet | Used for PostgreSQL (stable network identity + persistent storage) |
| Service (ClusterIP) | Internal service discovery between pods |
| Service (LoadBalancer) | Exposes services externally (provisioned by cloud or minikube tunnel) |
| ConfigMap | Stores non-sensitive environment configuration |
โ ๏ธ Never deploy databases inside your K8s cluster in production. Use managed services like Amazon RDS or Linode Managed Databases instead.
learning-microservices/
โโโ amqp/ # Shared RabbitMQ/AMQP configuration module
โโโ apigw/ # Spring Cloud API Gateway
โโโ clients/ # Shared OpenFeign client interfaces
โโโ customer/ # Customer microservice
โโโ eureka-server/ # Netflix Eureka service registry
โโโ fraud/ # Fraud detection microservice
โโโ notification/ # Notification microservice (async, via RabbitMQ)
โโโ resources/ # Architecture diagrams and screenshots
โโโ k8s/
โ โโโ minikube/
โ โโโ bootstrap/ # Namespace, PVC, ConfigMap manifests
โ โโโ services/ # Deployment & Service manifests per microservice
โโโ docker-compose.yml # Infrastructure only (PostgreSQL, pgAdmin, RabbitMQ, Zipkin)
โโโ docker-compose-v1.yml # Full stack (all microservices + infrastructure)
โโโ docker-compose-v3.yml # Alternative compose variant
โโโ pom.xml # Parent Maven POM (dependency & plugin management)
mvn spring-boot:run # Run a service with Spring Boot
java -jar target/service-1.0-SNAPSHOT.jar # Run a packaged JAR directly
mvn clean package -P build-docker-image # Build & push a Docker image via Jib
mvn clean package # Build & push all images (Jib, from root)| Phase | Description |
|---|---|
validate |
Validate project configuration |
compile |
Compile source code |
test |
Run unit tests |
package |
Package into a JAR |
verify |
Run integration test checks |
install |
Install JAR to local .m2 repository |
deploy |
Deploy JAR to remote repository |
docker compose up -d # Start all infrastructure containers
docker compose -f docker-compose-v1.yml up -d # Start full stack
docker-compose pull # Pull latest images from Docker Hub
docker logs <container_name> # View container logs
docker network ls # List Docker networks
docker volume ls # List Docker volumes
docker network prune # Remove unused networks
docker volume prune # Remove unused volumes
docker info # Show Docker host info (CPU, RAM)
docker network create postgres # Create the postgres bridge network
docker run -d -p 9411:9411 openzipkin/zipkin # Run Zipkin as a standalone containerminikube start --memory=14g # Start Minikube with 14 GB RAM
minikube status # Show cluster status
minikube ip # Get master node IP
minikube tunnel # Expose LoadBalancer services locally
minikube ssh # SSH into the Minikube node
minikube service --url <service_name> # Get the URL for a service
kubectl apply -f k8s/minikube/bootstrap/ # Apply all bootstrap manifests
kubectl apply -f k8s/minikube/services/ # Deploy all microservice manifests
kubectl delete -f <path> # Delete resources from a manifest
kubectl get all # List all pods, services, deployments
kubectl get pods # List running pods
kubectl get svc # List services
kubectl describe pod <pod_name> # Show detailed pod information
kubectl logs <pod_name> # Print pod logs
kubectl logs -f <pod_name> # Follow pod logs (tail -f)
kubectl port-forward pod/<pod_name> 8080:80 # Forward pod port to localhost
kubectl delete pod <pod_name> # Delete a pod (will be recreated)
kubectl scale --replicas=0 deployment <name> # Scale a deployment to 0 instances
kubectl exec -it postgres-0 -- psql -U syscomz # Open psql CLI in the postgres podmvn archetype:generate \
-DgroupId=com.syscomz \
-DartifactId=syscomzservices \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.4 \
-DinteractiveMode=false- Spring Cloud โ Distributed systems patterns for Spring Boot
- Spring Cloud Netflix (Eureka) โ Service Discovery
- Spring Cloud OpenFeign โ Declarative REST client
- Spring Cloud Gateway โ API Gateway & internal load balancer
- Spring Cloud Sleuth โ Distributed tracing auto-configuration
- Spring Profiles โ Environment-specific configuration
- RabbitMQ AMQP 0-9-1 Model Explained
- Apache Kafka โ Distributed event streaming platform
- Amazon SQS โ Fully managed cloud message queuing
- When to use RabbitMQ over Kafka?
- Zipkin โ Distributed tracing system and UI
- OpenTracing โ Distributed tracing standards
- Apache Maven โ Project management & build tool
- Maven Compiler Plugin
- Spring Boot Maven Plugin
- Maven Build Lifecycle
- Jib โ Containerise Java Apps โ Build Docker images without a Dockerfile
- Eclipse Temurin (OpenJDK images)
- Docker Resource Constraints
- Remove Docker Images, Containers and Volumes
- Kubernetes Official Docs
- Learn Kubernetes Basics
- Install Kubernetes Tools
- Minikube
- ConfigMap
- Spring Cloud Kubernetes PropertySource
- Amazon EKS โ Managed Kubernetes on AWS
- Amazon ECR โ Container image registry
- Amazon RDS โ Managed relational databases
- Amazon MQ โ Managed RabbitMQ / ActiveMQ
- Amazon MSK โ Managed Apache Kafka
- AWS Elastic Load Balancing
- GCP Cloud Load Balancing
- NGINX Load Balancer
- HashiCorp Vault โ Secrets management
- Spring Vault
- diagrams.net โ Architecture diagram tool
- Spring Boot Banner Generator
- Maven Archetype Guide
A multi-module project is built from an aggregator POM that manages a group of submodules. The aggregator lives in the root directory with
<packaging>pom</packaging>. Submodules can be built independently or together through the root POM โ reducing duplication and centralising dependency management.
Setup steps:
- Generate parent module with
mvn archetype:generate - Delete the
src/folder from the parent (it's an aggregator only) - Configure
<modules>and<dependencyManagement>in the parentpom.xml - Add
<packaging>jar</packaging>to each child module
| Step | Method | Notes |
|---|---|---|
| 1 | RestTemplate |
Hardcoded http://localhost:8081/... โ brittle, not scalable |
| 2 | RestTemplate + @LoadBalanced + Eureka |
Uses service name http://FRAUD/... โ dynamic, load balanced |
| 3 | OpenFeign + Eureka |
Declarative interface, cleanest approach |
| 4 | RabbitMQ (async) | For fire-and-forget events (e.g. notifications) |
| 5 | K8s DNS | Replaces Eureka entirely in Kubernetes environments |
- Register โ each service registers its host/port with Eureka on startup
- Lookup โ when Service A needs to call Service B, it queries Eureka
- Connect โ Service A connects directly to Service B using the discovered address
When services run inside Docker containers, localhost no longer refers to another container. Fix:
- Define a shared Docker network (e.g.
spring) indocker-compose.yml - Use the container name as the hostname in config (e.g.
rabbitmq,postgres) - Create an
application-docker.ymlprofile in each service with container hostnames - Set
SPRING_PROFILES_ACTIVE=dockerin the Docker Compose environment
Source Code โ Maven Package โ Jib Plugin โ Docker Image โ Docker Hub Registry
No Docker daemon needed. Jib builds layered, reproducible images directly from the classpath.
| Feature | Eureka (Spring Cloud) | Kubernetes |
|---|---|---|
| Service registry | Eureka Server pod | Built-in etcd + kube-dns |
| Load balancing | Ribbon (client-side) | kube-proxy + Service (server-side) |
| Health checks | Eureka heartbeat | Liveness & Readiness probes |
| Usage | Local / Docker Compose | K8s environments |
In this project, Eureka is disabled (
eureka.client.enabled: false) when deploying to Kubernetes.
# 1. Build and push Docker images
mvn clean package
# 2. Start Minikube
minikube start --memory=14g
# 3. Apply bootstrap (databases, config)
kubectl apply -f k8s/minikube/bootstrap/
# 4. Log into the postgres pod and create databases
kubectl exec -it postgres-0 -- psql -U syscomz
# 5. Deploy microservices
kubectl apply -f k8s/minikube/services/
# 6. Expose LoadBalancers
minikube tunnel
# 7. Test via API Gateway external IP
kubectl get svc # grab EXTERNAL-IP for apigw| Topic | Resource |
|---|---|
| Config management | Spring Cloud Config or Kubernetes ConfigMap |
| Secrets management | HashiCorp Vault + Spring Vault |
| Reporting / Analytics | Dedicated read-model microservice with optimised queries (avoid long JPA queries across services) |
| Production DB | Amazon RDS or Linode Managed Databases โ never run DBs inside K8s in production |
| Production MQ | Amazon MQ or Amazon MSK |
Borislav Dostumski
๐ @bdostumski
Built for learning purposes โ contributions and feedback are welcome!

















