1. How Does Service Discovery Work in Kubernetes?
What is Kubernetes Service Discovery?
In Kubernetes, application instances normally run as Pods.
The important problem is that Pods are dynamic:
- Pods can be created.
- Pods can be deleted.
- Pods can crash and restart.
- Scaling can create more Pods.
- A replacement Pod can receive a different IP address.
Therefore, one microservice should generally not call another microservice using an individual Pod IP.
Instead, Kubernetes provides a stable abstraction called a Service.
Order Service
|
| http://product-service
v
Kubernetes Service
|
+-------> Product Pod 1
+-------> Product Pod 2
+-------> Product Pod 3
The key idea is:
Pods are dynamic, but a Kubernetes Service provides a stable network identity for a group of Pods.
Why Do We Need It?
Suppose ProductService currently has three Pods:
Product Pod 1 → 10.244.1.10
Product Pod 2 → 10.244.2.15
Product Pod 3 → 10.244.3.20
If OrderService directly calls:
http://10.244.1.10:8080
and Pod 1 is terminated, that address is no longer a reliable destination.
A replacement Pod might become:
Product Pod 4 → 10.244.4.25
We don't want to modify OrderService every time this happens.
Instead:
OrderService
|
| http://product-service
v
Kubernetes Service
|
v
Available Product Pods
Purpose
Kubernetes service discovery gives applications:
- Stable service names
- Stable virtual service addresses for normal Services
- DNS-based discovery
- Dynamic backend membership
- Routing/load distribution to eligible Pods
- Decoupling from individual Pod IP addresses
Main Components
To understand Kubernetes service discovery, remember these components:
Pod
Service
Labels
Selectors
EndpointSlice
CoreDNS
kube-proxy / service dataplane
Let's examine them.
1. Pods
Suppose we deploy three instances of Product API:
Product API
Pod 1
Pod 2
Pod 3
Example Pod IPs:
Pod 1 → 10.244.1.10
Pod 2 → 10.244.2.15
Pod 3 → 10.244.3.20
These addresses should be considered dynamic.
2. Labels
Pods are assigned labels.
Example:
metadata:
labels:
app: product-service
All Product API Pods might have:
app=product-service
For example:
Pod 1 → app=product-service
Pod 2 → app=product-service
Pod 3 → app=product-service
Labels identify which Pods belong to a logical application/group.
3. Kubernetes Service
Now we create a Kubernetes Service.
Example:
apiVersion: v1
kind: Service
metadata:
name: product-service
spec:
selector:
app: product-service
ports:
- port: 80
targetPort: 8080
The most important part is:
selector:
app: product-service
It means:
This Service targets Pods matching
app=product-service.
Service → Pod Relationship
Now Kubernetes can logically associate:
product-service
|
| selector:
| app=product-service
|
+--------------------+
| |
v v
Product Pod 1 Product Pod 2
app=product-service app=product-service
|
v
Product Pod 3
app=product-service
4. EndpointSlice
Kubernetes needs to maintain the actual backend endpoints belonging to the Service.
Modern Kubernetes primarily represents these using EndpointSlice objects.
Conceptually:
Service:
product-service
↓
EndpointSlice
10.244.1.10:8080
10.244.2.15:8080
10.244.3.20:8080
So the relationship is approximately:
Service
↓
Selector
↓
Matching Pods
↓
EndpointSlices
↓
Current backend endpoints
EndpointSlices are maintained automatically by Kubernetes controllers for Services that use selectors.
5. CoreDNS
How does OrderService find product-service?
Kubernetes provides DNS-based service discovery, commonly through CoreDNS.
Therefore, an application can call:
http://product-service
instead of:
http://10.244.1.10
Conceptually:
Order Pod
|
| DNS lookup
| product-service
v
CoreDNS
|
| Service address
v
product-service
|
v
Product Pods
Kubernetes DNS Name
Within the same namespace, the short name is commonly enough:
product-service
A more complete DNS name is:
product-service.default.svc.cluster.local
Its general structure is:
<ServiceName>.<Namespace>.svc.cluster.local
For example:
product-service.default.svc.cluster.local
If the services are in the same namespace:
http://product-service
is typically sufficient.
ASP.NET Core Example
Suppose OrderService needs to call ProductService.
You can configure:
{
"Services": {
"ProductService": "http://product-service"
}
}
Then:
builder.Services.AddHttpClient<ProductClient>((sp, client) =>
{
var configuration =
sp.GetRequiredService<IConfiguration>();
client.BaseAddress = new Uri(
configuration["Services:ProductService"]!);
});
And:
public class ProductClient
{
private readonly HttpClient _httpClient;
public ProductClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<string> GetProductAsync(int id)
{
return await _httpClient
.GetStringAsync($"/api/products/{id}");
}
}
Notice that the ASP.NET Core application does not need to know:
10.244.1.10
10.244.2.15
10.244.3.20
It knows:
product-service
Complete Kubernetes Example
Product Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-deployment
spec:
replicas: 3
selector:
matchLabels:
app: product-service
template:
metadata:
labels:
app: product-service
spec:
containers:
- name: product-api
image: mycompany/product-api:1.0
ports:
- containerPort: 8080
The important setting:
replicas: 3
Kubernetes creates:
Product Pod 1
Product Pod 2
Product Pod 3
All receive:
app: product-service
Product Service
apiVersion: v1
kind: Service
metadata:
name: product-service
spec:
selector:
app: product-service
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
Now:
OrderService
|
| http://product-service
v
product-service
|
| port 80
v
Kubernetes service networking
|
+----------+----------+
| | |
v v v
Product 1 Product 2 Product 3
:8080 :8080 :8080
ClusterIP
The default Kubernetes Service type is:
type: ClusterIP
It provides a stable virtual IP for communication inside the cluster.
For example:
product-service
ClusterIP:
10.96.100.20
Pods may change:
10.244.1.10
10.244.2.15
10.244.3.20
but clients continue using:
product-service
This is one of the central ideas behind Kubernetes networking.
How the Complete Request Works
Suppose OrderService executes:
GET http://product-service/api/products/10
Conceptually:
ASP.NET Core OrderService
|
| GET
v
http://product-service
|
| DNS resolution
v
CoreDNS
|
v
Kubernetes Service
|
| Current eligible endpoints
v
Service dataplane
|
+----+----+
| | |
v v v
P1 P2 P3
One eligible backend receives the request.
Is Kubernetes Service Discovery Client-Side or Server-Side?
Standard Kubernetes Service-based discovery is generally considered a server-side/infrastructure-side discovery model.
Your application normally says:
Call product-service
rather than:
Give me all ProductService Pods,
then I'll select one myself.
Kubernetes networking infrastructure handles the backend routing.
Advantages
1. No hard-coded Pod IPs
Applications use:
product-service
instead of:
10.244.1.10
2. Dynamic scaling
You can change:
replicas: 3
to:
replicas: 10
without changing OrderService.
3. Automatic backend updates
When Pods appear or disappear, Kubernetes updates the service's endpoint information.
4. DNS-based naming
Applications can communicate using readable names:
product-service
order-service
payment-service
inventory-service
5. Load distribution
Normal Kubernetes Services can distribute connections/traffic among available backends through the cluster's service networking implementation.
6. Application is decoupled from infrastructure details
ASP.NET Core does not need to maintain the addresses of every replica.
Disadvantages
1. Kubernetes dependency
The mechanism depends on Kubernetes networking and DNS infrastructure.
2. Troubleshooting can be more complex
A connectivity problem might involve:
Application
DNS
Service
Selector
EndpointSlice
Readiness
NetworkPolicy
CNI
kube-proxy/service dataplane
3. DNS problems affect discovery
If cluster DNS is unhealthy, service-name resolution can fail.
4. Misconfigured labels/selectors can produce no backends
For example:
Pod:
app: product-api
Service:
selector:
app: product-service
They don't match.
The Service may therefore have no corresponding application endpoints.
2. What Happens When a New Service Instance Is Added or Removed?
This is one of the most important benefits of Kubernetes.
Suppose ProductService starts with three Pods:
ProductService
P1
P2
P3
Scenario 1: A New Pod Is Added
Suppose we scale:
replicas: 3
to:
replicas: 4
Kubernetes creates:
P4
Step 1 — New Pod Created
P1
P2
P3
P4 ← New
P4 receives the expected label:
app: product-service
Step 2 — Service Selector Matches It
The Service contains:
selector:
app: product-service
P4 contains:
labels:
app: product-service
Therefore, it belongs to the Service's selected Pod set.
Step 3 — Readiness Matters
Creating a Pod does not necessarily mean it should immediately receive traffic.
Normally, production applications configure a readiness probe.
Example:
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
Conceptually:
P4 Created
↓
Container starts
↓
Application initializes
↓
Readiness check
↓
Ready
↓
Eligible for normal Service traffic
This is important.
You don't want Kubernetes routing production requests to an application that is still initializing.
Step 4 — EndpointSlice Updated
Before:
EndpointSlice
P1
P2
P3
After the new Pod becomes an eligible endpoint:
EndpointSlice
P1
P2
P3
P4
Step 5 — P4 Can Receive Traffic
Now traffic can reach the new backend:
product-service
|
+-------------+-------------+
| | |
v v v
P1 P2 P3
|
v
P4
No ASP.NET Core configuration change is necessary.
Scenario 2: A Pod Is Removed
Suppose:
P2
is terminated.
Before:
P1
P2
P3
P4
After:
P1
P3
P4
Kubernetes updates the endpoint state so the removed/unavailable Pod is no longer a normal destination.
Conceptual Flow
Pod P2 removed/unavailable
↓
Kubernetes detects state change
↓
Endpoint information updated
↓
P2 no longer eligible for new Service traffic
↓
Remaining Pods continue serving requests
Pod Failure Example
Suppose:
P1 → Healthy
P2 → Failed
P3 → Healthy
The service should eventually route using the remaining eligible endpoints:
product-service
|
+----------+
| |
v v
P1 P3
Meanwhile, if the Pods are managed by a Deployment and the desired replica count is three, Kubernetes can create a replacement:
P4
Then:
product-service
|
+----------+----------+
| | |
v v v
P1 P3 P4
Readiness Probe vs Liveness Probe
This is important when discussing service discovery.
Readiness Probe
Answers:
Can this Pod currently receive traffic?
If readiness fails, the Pod is normally removed from eligible Service endpoints until it becomes ready again.
Readiness fails
↓
Pod becomes NotReady
↓
Removed from normal Service traffic
Liveness Probe
Answers:
Is this container/application still alive, or should Kubernetes restart it?
Conceptually:
Liveness fails repeatedly
↓
Container considered unhealthy
↓
Kubelet restarts container
Therefore:
Readiness → Should I receive traffic?
Liveness → Should my container be restarted?
Startup Probe
There is also a startup probe.
It answers:
Has this application successfully started?
This is useful for applications with slow startup.
Startup Probe
↓
Application started?
↓ Yes
Readiness/Liveness operate normally
ASP.NET Core Health Check Example
In Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapControllers();
app.MapHealthChecks("/health/live");
app.MapHealthChecks("/health/ready");
app.Run();
Then Kubernetes could configure probes:
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 10
periodSeconds: 20
In a real application, the readiness endpoint often checks dependencies that are necessary for serving traffic, while liveness is usually kept focused on whether the process itself should be restarted.
Autoscaling Example
Suppose traffic increases.
Initially:
ProductService
P1
P2
The Horizontal Pod Autoscaler may increase replicas:
P1
P2
P3
P4
P5
Conceptually:
Traffic increases
↓
HPA determines more replicas are needed
↓
Deployment/ReplicaSet creates Pods
↓
Pods start
↓
Pods become Ready
↓
EndpointSlices updated
↓
New Pods receive traffic
When traffic decreases:
P1
P2
P3
P4
P5
may scale down to:
P1
P2
The removed Pods cease to be normal Service endpoints.
Important Clarification: DNS Does Not Usually Return Every Pod
A common interview misunderstanding is:
"CoreDNS gives the client all Pod IP addresses."
For a normal ClusterIP Service, that is not generally how the client interacts with it.
Typically:
product-service
↓
DNS
↓
Service ClusterIP
↓
Kubernetes service networking
↓
Backend Pod
For a headless Service, however:
clusterIP: None
DNS can expose Pod/endpoints more directly.
This distinction is important.
Normal Service vs Headless Service
| Normal Service | Headless Service |
|---|---|
| Usually has ClusterIP | clusterIP: None |
| Stable virtual service IP | No virtual ClusterIP |
| Infrastructure routes to Pods | DNS exposes endpoints more directly |
| Common for ordinary APIs | Useful for some stateful/discovery scenarios |
Example headless Service:
apiVersion: v1
kind: Service
metadata:
name: product-service
spec:
clusterIP: None
selector:
app: product-service
ports:
- port: 8080
For ordinary ASP.NET Core REST microservices, a normal Kubernetes Service is usually the simpler model.
Full Architecture
Kubernetes Cluster
+---------------------------------------------+
| |
| Order Service |
| Pod |
| | |
| | |
| http://product-service |
| | |
| v |
| CoreDNS |
| | |
| v |
| Product Service |
| ClusterIP |
| | |
| Service routing |
| | |
| +----------+----------+ |
| | | | |
| v v v |
| P1 P2 P3 |
| |
+---------------------------------------------+
Now suppose autoscaling creates P4:
Endpoint set:
P1
P2
P3
P4 ← automatically added when eligible
If P2 disappears:
P1
P3
P4
The application still calls exactly the same address:
http://product-service
That is the main benefit.
Advantages
- No hard-coded Pod IP addresses
- Automatic handling of changing Pods
- Stable DNS names
- Works naturally with scaling
- Integrates with Kubernetes health/readiness
- Built-in service abstraction
- Applications remain largely unaware of individual instances
Disadvantages
- Requires understanding Kubernetes networking
- DNS/service networking becomes critical infrastructure
- Debugging can involve several Kubernetes components
- Incorrect labels/selectors can silently leave a Service without application backends
- Readiness configuration must be designed carefully
- Long-lived connections may behave differently from simple request-per-connection assumptions during scaling or termination
Key Points
For interviews, remember:
- Pods are temporary/dynamic.
- Pod IP addresses can change.
- Do not normally hard-code Pod IPs.
- Kubernetes
Serviceprovides a stable abstraction over Pods. - Services normally select Pods using labels/selectors.
- CoreDNS provides DNS-based service-name resolution.
- EndpointSlice represents backend endpoint information.
- A normal
ClusterIPService provides a stable virtual IP inside the cluster. - New eligible/Ready Pods can automatically become Service endpoints.
- Removed or unavailable Pods are removed from normal endpoint routing.
- Readiness determines whether a Pod should receive normal Service traffic.
- Liveness determines whether a container should be restarted.
- Kubernetes service discovery works naturally with horizontal scaling.
- Applications normally call a stable name such as
product-service, not individual Pod IP addresses.
Interview Questions & Answers
Q1. How does service discovery work in Kubernetes?
Answer:
Kubernetes normally uses Services and DNS for service discovery. Pods are grouped using labels and Service selectors. CoreDNS resolves a Service name such as product-service, while Kubernetes service networking routes traffic to eligible backend Pods represented through EndpointSlices.
Q2. Why shouldn't we use Pod IP addresses directly?
Because Pods are ephemeral. They can be recreated, scaled, moved, or terminated, and replacement Pods may receive different IP addresses.
Q3. What does a Kubernetes Service provide?
It provides a stable network identity for a logical group of Pods.
For example:
product-service
can remain stable even though its backend Pods change.
Q4. What is CoreDNS?
CoreDNS is commonly Kubernetes' cluster DNS implementation. It enables applications to resolve service names such as:
product-service
or:
product-service.default.svc.cluster.local
Q5. How does a Service know which Pods belong to it?
Usually through labels and selectors.
selector:
app: product-service
selects Pods with:
labels:
app: product-service
Q6. What is EndpointSlice?
EndpointSlice is a Kubernetes API resource used to represent sets of network endpoints backing Services.
For example:
product-service
↓
EndpointSlice
↓
10.244.1.10:8080
10.244.2.15:8080
10.244.3.20:8080
Q7. What happens when a new Pod is added?
If it matches the Service selector and becomes eligible/Ready, Kubernetes updates the endpoint information. The new Pod can then start receiving Service traffic.
Q8. What happens when a Pod is removed?
Kubernetes updates the endpoint information so that the removed Pod is no longer selected for new normal Service traffic.
Q9. What happens when readiness fails?
The Pod becomes NotReady and is normally removed from eligible Service endpoints until readiness succeeds again.
Q10. What happens when liveness fails?
After the configured failure threshold is reached, the kubelet restarts the unhealthy container according to Kubernetes' restart behavior.
Q11. What is the difference between readiness and liveness?
Readiness
↓
Should this Pod receive traffic?
Liveness
↓
Should this container be restarted?
Q12. What happens during Kubernetes autoscaling?
New Pods are created during scale-out. After they become Ready, they become eligible Service endpoints. During scale-in, removed Pods cease to be available endpoints.
Q13. Does ASP.NET Core need a Kubernetes service-discovery library?
For normal Kubernetes Service-based communication, usually no.
Your ASP.NET Core service can simply call a stable Kubernetes DNS name:
http://product-service
Kubernetes handles the underlying service discovery and routing.
Interview Summary
Kubernetes solves service discovery by giving dynamic Pods a stable Service identity. Pods are associated with Services through labels and selectors, CoreDNS resolves Service names, and EndpointSlices track backend endpoints. When Pods are created, removed, or become unready, Kubernetes updates the endpoint information automatically. Therefore, an ASP.NET Core microservice can continue calling
product-servicewithout knowing the IP addresses of individual Product Service Pods.