How Customer Requests Reach the Right Spring Boot Pod
Sarah’s deployment has finally reached an important milestone.
The Payment Service has been deployed successfully.
The kubelet has started the container.
The JVM has initialized.
Spring Boot has completed its startup sequence.
The Startup Probe has passed.
The Readiness Probe now reports:
READY = TRUE
The Pod has officially become part of the application.
Everything looks perfect.
A customer opens the bank’s mobile application and clicks Transfer Money.
The mobile app sends a request to the API Gateway.
POST /payments
The request reaches Kubernetes.
Now another question arises.
Which Payment Service Pod should process this request?
Remember, we deployed three replicas.
Payment Pod-1
Payment Pod-2
Payment Pod-3
All three expose exactly the same REST APIs.
Which one should receive the request?
More importantly…
What happens if one of them crashes five seconds later?
Would every client need to know the IP addresses of every Pod?
Fortunately, the answer is no.
This is exactly why Kubernetes introduced one of its most fundamental abstractions:
The Service.
The Biggest Problem with Pods
Let’s first understand why Services exist.
When Kubernetes creates a Pod, it also assigns an IP address.
For example:
Payment Pod-1
10.244.2.18
Tomorrow, suppose the node fails.
The ReplicaSet automatically creates a replacement Pod.
The new Pod receives a completely different IP.
Old Pod
10.244.2.18
Destroyed
----------------------
New Pod
10.244.5.71
Notice something important.
Pods are ephemeral.
Their identity is temporary.
Their IP addresses are temporary.
Their lifetime is temporary.
If applications communicated directly using Pod IP addresses, every restart would require every client to update its configuration.
Imagine thousands of banking applications trying to keep track of constantly changing Pod addresses.
It would be impossible.
Kubernetes therefore introduces a stable abstraction between clients and Pods.
That abstraction is the Service.
A Service Is Not a Pod
One of the most common misconceptions among newcomers is thinking a Service is another container.
It isn’t.
A Service never runs application code.
It doesn’t execute Java.
It doesn’t host Spring Boot.
It doesn’t process REST requests.
Instead, think of a Service as a stable virtual endpoint.
For example:
payment-service
10.96.18.20
Unlike Pods, this IP remains constant.
Whether one Pod exists or one hundred Pods exist, clients always communicate with the same Service address.
The Architecture
Let’s revisit our banking platform.
API Gateway
│
▼
payment-service
ClusterIP Service
│
┌──────────────┼──────────────┐
▼ ▼ ▼
Payment Pod-1 Payment Pod-2 Payment Pod-3
Notice something subtle.
The API Gateway never communicates with Pods directly.
It communicates only with the Service.
This level of indirection gives Kubernetes enormous flexibility.
Pods may come and go.
The Service remains unchanged.
Creating a Service
A typical Service definition looks like this.
apiVersion: v1
kind: Service
metadata:
name: payment-service
spec:
selector:
app: payment-service
ports:
- port: 80
targetPort: 8080
type: ClusterIP
At first glance, this YAML appears almost trivial.
However, one field deserves special attention.
selector:
app: payment-service
This single line is how Kubernetes knows which Pods belong to this Service.
Labels – The Secret Glue of Kubernetes
Labels are one of the simplest yet most powerful concepts in Kubernetes.
Suppose Sarah’s Deployment creates Pods with:
metadata:
labels:
app: payment-service
Every Pod now carries this label.
Pod-1
app = payment-service
-----------------------
Pod-2
app = payment-service
-----------------------
Pod-3
app = payment-service
The Service continuously searches for Pods matching:
app=payment-service
Any matching Pod automatically becomes part of the Service.
No registration.
No configuration files.
No manual updates.
Everything happens dynamically.
Endpoints — The Real Destination
Many developers think a Service somehow “contains” Pods.
That’s not actually how Kubernetes works.
Instead, another controller continuously builds an Endpoint object.
Conceptually:
Service
payment-service
↓
Endpoints
10.244.2.18
10.244.4.21
10.244.5.71
This Endpoint list is continuously updated.
Suppose Pod-2 crashes.
The Endpoint controller immediately removes it.
Before
10.244.2.18
10.244.4.21
10.244.5.71
------------------
After
10.244.2.18
10.244.5.71
The Service itself never changes.
Only its backend endpoints change.
This separation allows Kubernetes to reroute traffic almost instantly.
EndpointSlices – Scaling Beyond Large Clusters
Earlier versions of Kubernetes stored every Pod inside one large Endpoint object.
For clusters containing thousands of Pods, this became inefficient.
Modern Kubernetes solves this using EndpointSlices.
Instead of one massive list:
10,000 Endpoints
Kubernetes creates multiple smaller slices.
EndpointSlice-1
100 Endpoints
--------------------
EndpointSlice-2
100 Endpoints
--------------------
EndpointSlice-3
100 Endpoints
This dramatically improves scalability and reduces update traffic inside the control plane.
Most developers rarely interact with EndpointSlices directly, but understanding their purpose helps explain why Kubernetes scales to very large environments.
How Traffic Reaches a Pod
Suppose a customer submits a payment.
POST /payments
The request flows through the platform like this.
Customer
│
▼
API Gateway
│
▼
payment-service
ClusterIP
│
▼
EndpointSlice
│
▼
Selected Payment Pod
Notice that the client never knows which Pod ultimately processes the request.
That decision is made dynamically.
Load Balancing Across Replicas
Suppose three Payment Service Pods are available.
Pod-1
Pod-2
Pod-3
Five customers submit payment requests.
Kubernetes distributes them.
Conceptually:
Customer A ─────► Pod-1
Customer B ─────► Pod-2
Customer C ─────► Pod-3
Customer D ─────► Pod-1
Customer E ─────► Pod-2
The exact algorithm depends on the networking implementation, but the important takeaway is that traffic is distributed across healthy Pods rather than targeting a single instance.
This enables horizontal scaling without requiring clients to understand the underlying infrastructure.
What Happens During a Rolling Update?
Let’s revisit Sarah’s deployment.
Version 2.4.1 is currently running.
Pod A
Pod B
Pod C
She deploys version 2.5.0.
The Deployment gradually creates new Pods.
Old Pods
A
B
C
↓
New Pods
D
E
F
As each new Pod passes its Readiness Probe:
- It is added to the Service.
- An old Pod is removed.
- Traffic shifts gradually.
Customers never notice.
The Service continues presenting one stable endpoint while its backend changes continuously.
This is the secret behind Kubernetes’ zero-downtime rolling deployments.
What If a Pod Suddenly Crashes?
Suppose Pod-2 encounters an OutOfMemoryError.
The JVM terminates.
The kubelet reports:
Container Terminated
Immediately:
- The Readiness Probe fails.
- The Pod is removed from the Service.
- New requests stop flowing to it.
- Existing requests complete or fail according to application behavior.
- The ReplicaSet creates a replacement Pod.
Notice how many Kubernetes components collaborate:
- kubelet detects failure.
- ReplicaSet restores the replica count.
- Service removes the failed endpoint.
- EndpointSlice updates automatically.
- Scheduler places the replacement Pod.
- kubelet starts the new container.
- Readiness Probe validates it.
- Service begins routing traffic again.
None of these steps require manual intervention.
This orchestration is the essence of Kubernetes self-healing.
Service Discovery with DNS
Imagine the Fraud Service needs to call the Payment Service.
Would it use an IP address?
No.
Inside the cluster, applications communicate using DNS names.
For example:
http://payment-service
or, across namespaces:
http://payment-service.banking.svc.cluster.local
This means Spring Boot applications can use stable service names in configuration files rather than fragile IP addresses.
For example:
payment.service.url=http://payment-service
No matter how many times Pods restart, this URL remains unchanged.
Different Types of Services
So far we’ve focused on ClusterIP, the default Service type used for communication inside the cluster.
Kubernetes supports several Service types, each designed for a different networking requirement.
| Service Type | Typical Use Case |
|---|---|
| ClusterIP | Internal microservice-to-microservice communication |
| NodePort | Expose an application through a worker node’s IP and port |
| LoadBalancer | Integrate with a cloud provider’s external load balancer |
| ExternalName | Map a Kubernetes Service to an external DNS name |
In modern enterprise environments—particularly on OpenShift—you’ll most commonly use ClusterIP for internal communication, while external access is typically handled through Ingress or Routes, which we’ll cover later in this series.
The Bigger Picture
Let’s step back and look at the complete request flow we’ve built throughout the series.
Developer
│
kubectl apply
│
▼
API Server
│
▼
Deployment
│
▼
ReplicaSet
│
▼
Scheduler
│
▼
Worker Node
│
▼
kubelet
│
▼
Container Runtime
│
▼
Spring Boot
│
Startup Probe
│
Readiness Probe
│
▼
Service
│
▼
Customer Request
This journey began with a single command executed by Sarah.
What seemed like a simple deployment actually involved dozens of Kubernetes components working together, each with a clearly defined responsibility.
Understanding this sequence transforms Kubernetes from a mysterious platform into a predictable, observable, and debuggable system.
What’s Next?
Our Payment Service is now successfully receiving customer requests.
But production systems rarely remain static.
As transaction volume increases throughout the day, CPU usage rises, request latency grows, and more users begin accessing the service.
Should we manually deploy additional replicas?
Should Operations monitor dashboards around the clock?
Or can Kubernetes scale the application automatically?
In the next article, we’ll explore Horizontal Pod Autoscaling (HPA) and discover how Kubernetes automatically adjusts the number of Spring Boot Pods based on real-time demand, ensuring performance while optimizing infrastructure costs.
Next Article
Part 9 – Horizontal Pod Autoscaler (HPA): Automatically Scaling Spring Boot Microservices
We’ll cover:
- Why fixed replica counts are inefficient
- Metrics Server architecture
- CPU vs Memory vs Custom Metrics
- HPA decision-making algorithm
- Scaling up and scaling down
- Interaction with Deployments and ReplicaSets
- Stabilization windows and cooldown periods
- Enterprise banking traffic scenarios
- Production tuning and common pitfalls
- HPA, Cluster Autoscaler, and VPA—how they work together