Skip to content

bdostumski/learning-microservices

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

31 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿงฉ Microservices & Distributed Systems

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.

Java Spring Boot Spring Cloud Docker Kubernetes RabbitMQ Zipkin


๐Ÿ“– Overview

This project walks through every stage of building a production-style microservices system:

  1. Maven multi-module project structure with a parent POM
  2. Synchronous communication via REST Template โ†’ Eureka Service Discovery โ†’ OpenFeign
  3. Asynchronous messaging with RabbitMQ (AMQP)
  4. Distributed tracing with Spring Cloud Sleuth + Zipkin
  5. API Gateway routing with Spring Cloud Gateway
  6. Containerisation using Google Jib (no Dockerfile needed)
  7. Orchestration with Kubernetes (Minikube locally, Linode in cloud)
  8. Security with API Key authentication at the gateway level

๐Ÿ—๏ธ Architecture

"Software Architecture"

                        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
           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       โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ“ฆ Modules

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 โ€”

๐Ÿ› ๏ธ Tech Stack

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)

๐Ÿš€ Getting Started

Prerequisites

  • 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

1. Start Infrastructure Services

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

2. Run Services Locally (Development)

# 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:run

Or 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=8085 to Program Arguments.


3. Run Full Stack with Docker Compose

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 pull first to get the latest images from Docker Hub.


4. Build & Push Docker Images (Jib)

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-image

Make sure you're logged in before pushing:

docker login

Images are published as bdostumski/<service-name>:latest, e.g. bdostumski/customer:latest.

โš ๏ธ If mvn -version shows the wrong Java version, fix it with:

export JAVA_HOME=$(/usr/libexec/java_home -v 17)

5. Deploy to Kubernetes (Minikube)

# 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.


6. Cloud Deployment (Linode Kubernetes)

# 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 svc

๐Ÿ” Distributed Tracing with Sleuth + Zipkin

Every 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.

"Zipkin UI" "Zipkin UI Dependencies"


๐Ÿ‡ Asynchronous Messaging with RabbitMQ

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

"RabbitMQ Architecture" "RabbitMQ Messages Exchange"

๐Ÿ’ก 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.

๐ŸŒ API Gateway & Load Balancing

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/** โ†’ customer service
  • 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.

"Internal and External Load Balancers" "Load Balancer Algorithms" "Load Balancer Health Checks"


๐Ÿ”’ Security โ€” API Key Authentication

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.

"Security Architecture" "API Key DB Schema" "Security Filter Check"


โ˜ธ๏ธ Kubernetes Concepts

Cluster Architecture

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

"K8s Control Plane" "K8s Pods" "K8s Controller Manager"

Key K8s Resources Used

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.

"K8s Deployment Architecture" "K8s kubectl get all"


๐Ÿ“ Project Structure

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)

๐Ÿ“‹ Cheat Sheet

Application Commands

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)

Maven Lifecycle

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 Commands

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 container

kubectl / Minikube Commands

minikube 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 pod

Maven Multi-Module Archetypes

mvn archetype:generate \
  -DgroupId=com.syscomz \
  -DartifactId=syscomzservices \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DarchetypeVersion=1.4 \
  -DinteractiveMode=false

๐Ÿ“š Reference Links

Core Technologies

Messaging

Tracing & Observability

  • Zipkin โ€” Distributed tracing system and UI
  • OpenTracing โ€” Distributed tracing standards

Build & Containerisation

Kubernetes

Cloud Deployment

Security & Secrets

Tooling


๐Ÿ“ Learning Notes

Maven Multi-Module Project

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:

  1. Generate parent module with mvn archetype:generate
  2. Delete the src/ folder from the parent (it's an aggregator only)
  3. Configure <modules> and <dependencyManagement> in the parent pom.xml
  4. Add <packaging>jar</packaging> to each child module

Service Communication Evolution in This Project

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

Eureka Service Discovery Flow

"Eureka Server Communication" "Eureka Server Communication Example"

  1. Register โ€” each service registers its host/port with Eureka on startup
  2. Lookup โ€” when Service A needs to call Service B, it queries Eureka
  3. Connect โ€” Service A connects directly to Service B using the discovered address

Docker Networking & Spring Profiles

When services run inside Docker containers, localhost no longer refers to another container. Fix:

  1. Define a shared Docker network (e.g. spring) in docker-compose.yml
  2. Use the container name as the hostname in config (e.g. rabbitmq, postgres)
  3. Create an application-docker.yml profile in each service with container hostnames
  4. Set SPRING_PROFILES_ACTIVE=docker in the Docker Compose environment

Jib Image Build Flow

Source Code โ†’ Maven Package โ†’ Jib Plugin โ†’ Docker Image โ†’ Docker Hub Registry

No Docker daemon needed. Jib builds layered, reproducible images directly from the classpath.

Kubernetes vs Eureka

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.

Kubernetes Deployment Checklist

# 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

๐Ÿ”ฎ Further Reading & Next Steps

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

๐Ÿ‘ค Author

Borislav Dostumski
๐Ÿ™ @bdostumski


Built for learning purposes โ€” contributions and feedback are welcome!

Releases

No releases published

Packages

 
 
 

Contributors

Languages