← Back to Articles         
Kubernetes Service Discovery & Instance Changes        2/2

Kubernetes Service Discovery & Instance Changes 2/2

Published on 29 Sep 2026     13 min read Microservices
Service Discovery

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:

  1. Pods are temporary/dynamic.
  2. Pod IP addresses can change.
  3. Do not normally hard-code Pod IPs.
  4. Kubernetes Service provides a stable abstraction over Pods.
  5. Services normally select Pods using labels/selectors.
  6. CoreDNS provides DNS-based service-name resolution.
  7. EndpointSlice represents backend endpoint information.
  8. A normal ClusterIP Service provides a stable virtual IP inside the cluster.
  9. New eligible/Ready Pods can automatically become Service endpoints.
  10. Removed or unavailable Pods are removed from normal endpoint routing.
  11. Readiness determines whether a Pod should receive normal Service traffic.
  12. Liveness determines whether a container should be restarted.
  13. Kubernetes service discovery works naturally with horizontal scaling.
  14. 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-service without knowing the IP addresses of individual Product Service Pods.

← Back to Articles