4.2 Services Deep Dive: ClusterIP, NodePort, LoadBalancer & EndpointSlices

Key Takeaways

  • A Kubernetes Service provides a stable, persistent virtual IP (VIP) and DNS name abstraction over a dynamic, ephemeral pool of backend Pod IPs.
  • ClusterIP is the default service type providing internal cluster-only reachability; Headless Services (clusterIP: None) bypass VIP allocation to return direct Pod IPs via DNS for stateful clustering.
  • NodePort opens a dedicated high-order port (default 30000-32767) across all cluster nodes, while LoadBalancer provisions an external cloud load balancer pointing to those NodePorts.
  • The EndpointSlice API (discovery.k8s.io/v1) splits large endpoint sets into scalable 100-endpoint chunks, eliminating the O(N) etcd write amplification and control plane churn of legacy Endpoints.
  • Setting externalTrafficPolicy: Local preserves client source IP addresses and avoids second-hop routing at the cost of potential load imbalance and dropped traffic on nodes lacking local backend pods.
Last updated: August 2026

4.2 Services Deep Dive: ClusterIP, NodePort, LoadBalancer & EndpointSlices

Pods in Kubernetes are ephemeral; they are dynamically created, scaled, rescheduled, and terminated, resulting in constantly changing IP addresses. Applications cannot reliably connect directly to individual Pod IPs. The Service abstraction provides a persistent, deterministic network endpoint—encapsulating a stable Virtual IP (ClusterIP) and a canonical DNS name—that load-balances traffic across a dynamic set of backend Pods matching a label selector.


1. Service Types & Mechanics

Kubernetes defines four primary Service types in spec.type:

+-----------------------------------------------------------------------------------------+
|                                KUBERNETES SERVICE TYPES                                 |
|                                                                                         |
|  1. ClusterIP (Default)            2. NodePort                   3. LoadBalancer        |
|  +-----------------------+         +-----------------------+     +--------------------+ |
|  | Internal Clients Only |         | External Client       |     | External Internet  | |
|  +-----------+-----------+         +-----------+-----------+     +---------+----------+ |
|              |                                 | (NodeIP:30080)            | (Public IP)|
|              v                                 v                           v            |
|      [ ClusterIP VIP ]                 [ NodePort 30080 ]           [ Cloud Provider  ] |
|              |                                 |                    [  Load Balancer  ] |
|              |                                 v                           |            |
|              |                         [ ClusterIP VIP ]                   v            |
|              |                                 |                    [ NodePort 30080  ] |
|              v                                 v                           |            |
|      +-------+-------+                 +-------+-------+                   v            |
|      | Matching Pods |                 | Matching Pods |           [ ClusterIP VIP ]    |
|      +---------------+                 +---------------+                   |            |
|                                                                            v            |
|  4. ExternalName: Returns CNAME DNS record (e.g., db.corp.com)     +-------+-------+    |
|  5. Headless (clusterIP: None): Direct DNS to Pod IPs               | Matching Pods |    |
|                                                                     +---------------+    |
+-----------------------------------------------------------------------------------------+

1. ClusterIP (Default)

  • Allocates an immutable virtual IP from the cluster's --service-cluster-ip-range CIDR (e.g., 10.96.0.0/12).
  • Reachable only from within the cluster (by Pods and node host processes).
  • kube-proxy programs Netfilter/IPVS rules on every node to intercept traffic to this VIP and translate the destination IP (DNAT) to one of the healthy backend Pod IPs.

2. NodePort

  • Builds on top of ClusterIP. In addition to allocating a ClusterIP, it allocates a dedicated port from the node port range (default: 30000–32767, defined by --service-node-port-range).
  • Every worker and control plane node opens this port on all network interfaces (0.0.0.0) and forwards incoming traffic to the underlying ClusterIP.

3. LoadBalancer

  • Builds on top of NodePort and ClusterIP.
  • Integrates with the cloud provider (via cloud-controller-manager) to provision an external hardware/cloud load balancer (e.g., AWS NLB/ALB, Google Cloud Network Load Balancer, Azure Load Balancer).
  • The cloud load balancer routes external traffic to the worker nodes on the allocated NodePort.

4. ExternalName

  • A special Service type that does not use label selectors, virtual IPs, or proxying.
  • Configures CoreDNS to return a CNAME record pointing to an external FQDN (e.g., external-db.rds.amazonaws.com).
apiVersion: v1
kind: Service
metadata:
  name: my-database-svc
spec:
  type: ExternalName
  externalName: prod-db.postgres.database.azure.com

5. Headless Services (clusterIP: None)

  • When spec.clusterIP is explicitly set to None, Kubernetes disables virtual IP allocation and proxying.
  • CoreDNS returns direct A/AAAA records containing the actual IP addresses of all ready matching backend Pods.
  • Indispensable for stateful distributed databases (Cassandra, MongoDB, Kafka, Elasticsearch, StatefulSets) where clients must establish direct peer-to-peer connections to specific instances.

2. Endpoints vs. EndpointSlices (discovery.k8s.io/v1)

Historically, the control plane maintained a single Endpoints resource per Service. When a Service had 2,000 backend Pod replicas, the single Endpoints object stored all 2,000 IP addresses in a massive list.

+-----------------------------------------------------------------------------------------+
|                        ENDPOINTS VS. ENDPOINTSLICE SCALABILITY                          |
|                                                                                         |
|   LEGACY ENDPOINTS (Monolithic)             MODERN ENDPOINTSLICE (Chunked)              |
|   +---------------------------------+       +------------------------------------+      |
|   | Service: payment-svc            |       | Service: payment-svc               |      |
|   | Endpoints:                      |       |                                    |      |
|   |   - 10.244.1.2, 10.244.1.3      |       | EndpointSlice-01 (100 Endpoints)   |      |
|   |   - 10.244.1.4, 10.244.2.10     |       | EndpointSlice-02 (100 Endpoints)   |      |
|   |   ... [2,000 IP addresses]     |       | EndpointSlice-03 (100 Endpoints)   |      |
|   +---------------------------------+       +------------------------------------+      |
|   * Problem: Single Pod rollout             * Solution: Only 1 slice updated            |
|     re-writes 1.5MB to etcd & sends           (Tiny JSON diff sent to kube-proxy)       |
|     full object to ALL kube-proxies!                                                    |
+-----------------------------------------------------------------------------------------+

The EndpointSlice Architecture:

  • Introduced to solve the $O(N)$ control plane scalability bottleneck.
  • The endpointslice-controller breaks down large sets of network endpoints into multiple smaller EndpointSlice resources (default: 100 endpoints per slice).
  • When a single Pod restarts or updates its IP address, only the single affected EndpointSlice object is mutated in etcd and transmitted across the cluster network, drastically reducing memory usage and API server load.
  • Supports Dual-Stack IPv4/IPv6 addressing and topological routing hints (topology.kubernetes.io/zone).
# Inspect EndpointSlices for a service:
kubectl get endpointslices -l kubernetes.io/service-name=payment-svc
kubectl describe endpointslice payment-svc-abc12

3. Selectorless Services and Manual Endpoints

Services can be created without a spec.selector. In this scenario, the control plane does not automatically generate Endpoints or EndpointSlices, allowing administrators to manually create them to point to external systems (e.g., on-premises Oracle database, external REST API, or staging environments):

# Step 1: Create Service without selector
apiVersion: v1
kind: Service
metadata:
  name: legacy-oracle-db
spec:
  ports:
    - protocol: TCP
      port: 1521
      targetPort: 1521
---
# Step 2: Manually create matching Endpoints object
apiVersion: v1
kind: Endpoints
metadata:
  name: legacy-oracle-db # MUST match Service name
subsets:
  - addresses:
      - ip: 192.168.50.100
      - ip: 192.168.50.101
    ports:
      - port: 1521

4. Source IP Preservation & externalTrafficPolicy

When external traffic enters a Kubernetes cluster via a NodePort or LoadBalancer Service, the standard behavior can obscure the client's genuine source IP address.

Traffic PolicyConfigurationSource IP Preserved?Routing Hop MechanicsPotential Drawbacks
Cluster (Default)externalTrafficPolicy: ClusterNO (Client IP replaced with Node IP via SNAT)Traffic landing on Node A can be forwarded across the internal overlay to Node B if Node B hosts backend Pods.Extra network hop introduces latency; security logs see Node IP instead of real client IP.
LocalexternalTrafficPolicy: LocalYES (Direct Client IP preserved)Node A only routes traffic to Pods running locally on Node A. No internal forwarding hop.If Node A has zero local backend Pods, traffic sent to Node A is dropped. Cloud LBs must use node health checks. Uneven load distribution.
apiVersion: v1
kind: Service
metadata:
  name: web-ingress-svc
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local # Preserves real client source IP
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

[!NOTE] Session Affinity: To ensure requests from the same client IP are consistently directed to the same backend Pod, set spec.sessionAffinity: ClientIP and tune spec.sessionAffinityConfig.clientIP.timeoutSeconds (default: 10800 seconds / 3 hours).

Loading diagram...
Service Traffic Routing: externalTrafficPolicy Cluster vs Local
Test Your Knowledge

An administrator configures a Kubernetes Service of type NodePort with 'externalTrafficPolicy: Local'. When an external client sends a request to Worker Node 03, the request hangs and times out. However, requests sent to Worker Node 01 and Node 02 succeed immediately and display the true client IP in the application logs. What is the root cause?

A
B
C
D
Test Your Knowledge

A team is designing a distributed Cassandra database cluster deployed via a StatefulSet. The database client application requires direct TCP communication with individual Cassandra member Pods rather than load-balanced proxying through a single virtual IP. How should the Service be configured?

A
B
C
D
Test Your Knowledge

Why did Kubernetes introduce the EndpointSlice API ('discovery.k8s.io/v1') to replace the monolithic 'Endpoints' API in large-scale production clusters?

A
B
C
D