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)