UserβOrder Microservices Application
Business Use Case (Realistic)
A simple e-commerce backend where:
User Service manages users
Order Service creates orders for users
Services communicate via REST
Each service is independently deployable
π§± Architecture Overview
Components
user-service (Spring Boot)
order-service (Spring Boot)
MySQL (separate DB per service)
Docker
Kubernetes (local or EKS)
Git-based deployment
1οΈβ£ Project Structure (Mono-Repo for Learning)
microservices-project/
βββ user-service/
β βββ src/main/java/...
β βββ Dockerfile
β βββ pom.xml
βββ order-service/
β βββ src/main/java/...
β βββ Dockerfile
β βββ pom.xml
βββ k8s/
β βββ user-deployment.yaml
β βββ order-deployment.yaml
β βββ mysql-user.yaml
β βββ mysql-order.yaml
β βββ ingress.yaml
2οΈβ£ Develop β User Service
User Entity
@entity
public class User {
@id @GeneratedValue
private Long id;
private String name;
private String email;
}
REST Controller
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public User createUser(@RequestBody User user) {
return userRepo.save(user);
}
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userRepo.findById(id).orElseThrow();
}
}
Health Check (mandatory in prod)
@GetMapping("/health")
public String health() {
return "UP";
}
3οΈβ£ Develop β Order Service (Calls User Service)
Order Entity
C
@entity
public class Order {
@id @GeneratedValue
private Long id;
private Long userId;
private String product;
}
REST Control
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
RestTemplate restTemplate;
@PostMapping
public Order createOrder(@RequestBody Order order) {
String userUrl = "http://user-service:8080/users/" + order.getUserId();
restTemplate.getForObject(userUrl, String.class);
return orderRepo.save(order);
}
}
π‘ This is real inter-service communication using Kubernetes DNS.
4οΈβ£ Build β Dockerize Both Services
Dockerfile (same pattern for both
Dockerfile:
FROM openjdk:17-jdk-slim
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
Build Images
mvn clean package
docker build -t user-service:1.0 .
docker build -t order-service:1.0 .
5οΈβ£ Kubernetes β Database Deployment
MySQL for User Service
Yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-user
spec:
replicas: 1
template:
spec:
containers:
- name: mysql
image: mysql:8
env:
- name: MYSQL_DATABASE
value: userdb
- name: MYSQL_ROOT_PASSWORD
value: root
6οΈβ£ Kubernetes β User Service Deploymen
Yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 2
template:
spec:
containers:
- name: user-service
image: user-service:1.0
ports:
- containerPort: 8080
Service
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 8080
7οΈβ£ Kubernetes β Order Service Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 2
template:
spec:
containers:
- name: order-service
image: order-service:1.0
8οΈβ£ Ingress β Single Entry Point
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
rules:
- http:
paths:
- path: /users pathType: Prefix backend: service: name: user-service port: number: 8080
- path: /orders pathType: Prefix backend: service: name: order-service port: number: 8080
9οΈβ£ Deploy Everything
kubectl apply -f k8s/
Bash
kubectl get pods
kubectl get svc
kubectl get ingress
π Functional Proof (This is Critical)
Create Use
curl -X POST /users -d '{"name":"Raj","email":"raj@mail.com"}'
Create Order
Copy code
Bash
curl -X POST /orders -d '{"userId":1,"product":"Laptop"}'
β Order service calls user-service β Kubernetes DNS works β DB persists data
1οΈβ£1οΈβ£ CI/CD Flow (Real Production Model)
CI
Git push
Build
Test
Docker image build
Push to registry
CD (GitOps)
Update image tag in Git
Auto sync to cluster
1οΈβ£2οΈβ£ Rollback Scenario (Very Important)
kubectl rollout undo deployment user-service
OR Git revert (GitOps)
1οΈβ£3οΈβ£ Production-Grade Improvements (Next Level)
ConfigMaps & Secrets
HPA (auto scaling)
Circuit breaker
Distributed tracing
Canary deployme
π PART 2 β FULL CI PIPELINE (Jenkins)
Trigger β Build β Test β Docker β Push β Update Git
2.1 Jenkins Prerequisites
Jenkins Server Must Have
Docker installed
Git access
Docker registry credentials
Java 17
kubectl (optional)
2.2 Jenkinsfile (Complete & Real)
Place this Jenkinsfile in repo root.
pipeline {
agent any
environment {
REGISTRY = "docker.io/yourrepo"
IMAGE_TAG = "${BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/your-org/microservices-project.git'
}
}
stage('Build User Service') {
steps {
dir('user-service') {
sh 'mvn clean package'
}
}
}
stage('Build Order Service') {
steps {
dir('order-service') {
sh 'mvn clean package'
}
}
}
stage('Docker Build') {
steps {
sh '''
docker build -t $REGISTRY/user-service:$IMAGE_TAG user-service/
docker build -t $REGISTRY/order-service:$IMAGE_TAG order-service/
'''
}
}
stage('Docker Push') {
steps {
withDockerRegistry([credentialsId: 'dockerhub-creds', url: '']) {
sh '''
docker push $REGISTRY/user-service:$IMAGE_TAG
docker push $REGISTRY/order-service:$IMAGE_TAG
'''
}
}
}
stage('Update K8s Manifests Repo') {
steps {
sh '''
git clone https://github.com/your-org/k8s-manifests.git
cd k8s-manifests
sed -i "s|image:.*user-service.*|image: $REGISTRY/user-service:$IMAGE_TAG|" user-deployment.yaml
sed -i "s|image:.*order-service.*|image: $REGISTRY/order-service:$IMAGE_TAG|" order-deployment.yaml
git commit -am "Update images to $IMAGE_TAG"
git push
'''
}
}
}
}
2.3 What This Pipeline Achieves
β Builds both microservices
β Creates Docker images
β Pushes images to registry
β Updates Git (GitOps trigger)
β No direct kubectl in Jenkins (BEST PRACTICE)
Trigger β Build β Test β Docker β Push β Update Git
2.1 Jenkins Prerequisites
Jenkins Server Must Have
Docker installed
Git access
Docker registry credentials
Java 17
kubectl (optional)
2.2 Jenkinsfile (Complete & Real)
Place this Jenkinsfile in repo root.
Groovy
pipeline {
agent any
environment {
REGISTRY = "docker.io/yourrepo"
IMAGE_TAG = "${BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/your-org/microservices-project.git'
}
}
stage('Build User Service') {
steps {
dir('user-service') {
sh 'mvn clean package'
}
}
}
stage('Build Order Service') {
steps {
dir('order-service') {
sh 'mvn clean package'
}
}
}
stage('Docker Build') {
steps {
sh '''
docker build -t $REGISTRY/user-service:$IMAGE_TAG user-service/
docker build -t $REGISTRY/order-service:$IMAGE_TAG order-service/
'''
}
}
stage('Docker Push') {
steps {
withDockerRegistry([credentialsId: 'dockerhub-creds', url: '']) {
sh '''
docker push $REGISTRY/user-service:$IMAGE_TAG
docker push $REGISTRY/order-service:$IMAGE_TAG
'''
}
}
}
stage('Update K8s Manifests Repo') {
steps {
sh '''
git clone https://github.com/your-org/k8s-manifests.git
cd k8s-manifests
sed -i "s|image:.*user-service.*|image: $REGISTRY/user-service:$IMAGE_TAG|" user-deployment.yaml
sed -i "s|image:.*order-service.*|image: $REGISTRY/order-service:$IMAGE_TAG|" order-deployment.yaml
git commit -am "Update images to $IMAGE_TAG"
git push
'''
}
}
}
}
2.3 What This Pipeline Achieves
β Builds both microservices
β Creates Docker images
β Pushes images to registry
β Updates Git (GitOps trigger)
β No direct kubectl in Jenkins (BEST PRACTICE)
CI ends here. CD starts via Argo CD
CI ends here. CD starts via Argo CD
PART 3 β ARGO CD (GitOps DEPLOYMENT)
3.1 Why Argo CD (Production Reality)
Jenkins should NOT deploy to Kubernetes directly
Argo CD:
Watches Git
Applies desired state
Auto-rollbacks
Auditable
3.2 Install Argo CD
kubectl create namespace argocd
kubectl apply -n argocd \
-f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
Expose UI:
kubectl port-forward svc/argocd-server -n argocd 8080:443
Login:
kubectl get secret argocd-initial-admin-secret -n argocd -o yaml
3.3 GitOps Repository Structure
Copy code
Text
k8s-manifests/
βββ user/
β βββ deployment.yaml
βββ order/
β βββ deployment.yaml
βββ ingress.yaml
3.4 Argo CD Application (Single App)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: microservices-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/k8s-manifests.git
targetRevision: main
path: .
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
Apply:
kubectl apply -f application.yaml
3.5 What Happens Automatically
β Jenkins updates image tag in Git
β Argo CD detects Git change
β Syncs Kubernetes state
β Pods rolling update
β Health checks validated
3.6 Verify Deploymen
kubectl get pods
kubectl rollout status deployment/user-service
kubectl rollout status deployment/order-service
3.7 Rollback (This Is POWER)
Option 1 β Git Revert
git revert
git push
Option 2 β Argo CD UI
Click previous revision
Sync
β Zero downtime rollback
β No Jenkins involved
π§ REAL PRODUCTION FLOW (FINAL PICTURE)
Developer
β
Git Push
β
Jenkins (CI)
β
Docker Registry
β
Git (Manifests)
β
Argo CD
β
Kubernetes
Tools Used (Industry Standard)
CI: Jenkins
GitOps CD: Argo CD
Containers: Docker
Orchestration: Kubernetes
BLUE-GREEN & CANARY DEPLOYMENTS WITH ARGO CD:
Blue-Green
Two identical environments: Blue (live) and Green (new)
Traffic switches instantly
Fast rollback
Canary
Release to small % of users
Observe metrics
Gradually increase traffic
π΅π’ BLUE-GREEN DEPLOYMENT
5.1 Kubernetes Layout (User Service)
We create TWO deployments:
user-service-blue (v1 β live)
user-service-green (v2 β new)
Service decides traffic
π Service selector switch = deployment switch
5.2 Blue Deployment (Live)
Copy code
Yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service-blue
spec:
replicas: 2
selector:
matchLabels:
app: user-service
version: blue
template:
metadata:
labels:
app: user-service
version: blue
spec:
containers:
- name: user-service
image: yourrepo/user-service:1.0
5.3 Green Deployment (New Version)
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service-green
spec:
replicas: 2
selector:
matchLabels:
app: user-service
version: green
template:
metadata:
labels:
app: user-service
version: green
spec:
containers:
- name: user-service
image: yourrepo/user-service:2.0
5.4 Service (Traffic Controller)
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
version: blue # LIVE
ports:
- port: 8080 5.5 Deploy Using Argo CD Commit all manifests Argo CD syncs Only BLUE receives traffic 5.6 Switch Traffic (ZERO DOWNTIME) Change one line only selector: app: user-service version: green Commit β Push β Argo CD syncs β Traffic moves instantly β No pod restart β Rollback = revert commit 5.7 Blue-Green Rollback (1 second)
git revert
git push
Argo CD restores BLUE
π€ CANARY DEPLOYMENT (STEP-BY-STEP)
Now progressive delivery, not instant switch.
5.8 Canary Strategy (Real Production)
Version
Replicas
Traffic
v1
9
90%
v2
1
10%
Kubernetes distributes traffic via Service.
5.9 Stable Deployment (v1)
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service-stable
spec:
replicas: 9
selector:
matchLabels:
app: user-service
track: stable
5.10 Canary Deployment (v2)
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service-canary
spec:
replicas: 1
selector:
matchLabels:
app: user-service
track: canary
5.11 Shared Service (Traffic Split)
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 8080
Both deployments share:
Yaml
labels:
app: user-service
β‘ Kubernetes load-balances based on pod count
β‘ Canary gets ~10% traffic
5.12 Increase Canary Gradually
Step 1: Canary 1 pod (10%)
Step 2: Canary 3 pods (25%)
Step 3: Canary 5 pods (50%)
Step 4: Promote to stable
Just update replica count in Git.
5.13 Promote Canary to Stable
Remove stable deployment
Rename canary as stable
Commit β Argo CD sync
5.14 Canary Rollback (Instant)
kubectl scale deployment user-service-canary --replicas=0
OR Git revert (recommended)
Top comments (0)