14: Service ClusterIP

14: Service ClusterIP

Objective

Understand the purpose of Kubernetes Services, learn how ClusterIP (the default Service type) provides stable internal networking for Pods, and practice creating and testing Services within the cluster.


Theory

Why Do We Need Services?

Pods in Kubernetes are ephemeral — they can be created, destroyed, and rescheduled at any time. Each Pod receives a unique IP address, but that IP changes every time the Pod is replaced. This creates a fundamental problem: how do other components reliably communicate with a set of Pods if their IPs keep changing?

A Service solves this by providing:

  • A stable IP address (ClusterIP) that does not change for the lifetime of the Service
  • A stable DNS name that other Pods can use to connect
  • Automatic load balancing across all matching Pods
  • Automatic endpoint updates when Pods are added or removed

ClusterIP — The Default Service Type

When you create a Service without specifying a type, Kubernetes creates a ClusterIP Service. This Service:

  • Is only accessible from within the cluster
  • Gets assigned a virtual IP from the cluster’s Service CIDR range
  • Routes traffic to Pods matching its selector

How It Works: Selectors and Endpoints

A Service uses label selectors to find which Pods should receive traffic. Kubernetes maintains an Endpoints (or EndpointSlice) object that lists the IP addresses of all matching, healthy Pods.

When a Pod matches the selector and passes its readiness checks, its IP is added to the Endpoints list. When a Pod is deleted or fails readiness, its IP is removed.

Load Balancing

kube-proxy (running on every node) programs iptables or IPVS rules to distribute traffic from the Service’s ClusterIP across all endpoints. By default, the distribution is round-robin (iptables mode) or least connections (IPVS mode).

Service Architecture Diagram

graph TB
    Client["Client Pod"]

    subgraph Service["Service: nginx-service-XX<br/>ClusterIP: 10.0.100.50"]
        LB["Load Balancer<br/>(kube-proxy rules)"]
    end

    subgraph Endpoints["Endpoints"]
        EP1["Pod 1<br/>10.244.0.5:80"]
        EP2["Pod 2<br/>10.244.1.8:80"]
        EP3["Pod 3<br/>10.244.2.3:80"]
    end

    Client -->|"nginx-service-XX:80"| LB
    LB --> EP1
    LB --> EP2
    LB --> EP3

    style Service fill:#e1f5fe,stroke:#0288d1,stroke-width:2px
    style Endpoints fill:#e8f5e9,stroke:#388e3c,stroke-width:1px
    style Client fill:#fff3e0,stroke:#f57c00,stroke-width:1px

Practical Tasks

Task 1: Create an nginx Deployment and ClusterIP Service

Create a file called nginx-clusterip.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-XX          # Replace XX with your student number
  namespace: student-XX
  labels:
    app: nginx-XX
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-XX
  template:
    metadata:
      labels:
        app: nginx-XX
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80
              name: http
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service-XX              # Replace XX with your student number
  namespace: student-XX
spec:
  type: ClusterIP                      # This is also the default if omitted
  selector:
    app: nginx-XX                      # Must match Pod labels
  ports:
    - protocol: TCP
      port: 80                         # Service port
      targetPort: 80                   # Container port
      name: http

Deploy the resources:

kubectl apply -f nginx-clusterip.yaml

Verify the Deployment and Service:

kubectl get deployment nginx-deployment-XX -n student-XX
kubectl get svc nginx-service-XX -n student-XX
kubectl get endpoints nginx-service-XX -n student-XX

You should see the Service with a ClusterIP address and the Endpoints listing the IPs of all 3 Pods.

Task 2: Test Connectivity from a Test Pod

Launch a temporary busybox Pod to test connectivity from within the cluster:

kubectl run test-pod-XX --rm -it --image=busybox -n student-XX -- sh

Inside the busybox shell, test the Service:

# Test using the Service name
wget -qO- nginx-service-XX

# Test using the Service ClusterIP (replace with actual IP from kubectl get svc)
wget -qO- 10.0.100.50

# Test multiple times to observe load balancing
for i in 1 2 3 4 5; do wget -qO- nginx-service-XX 2>/dev/null | head -1; done

You should see the default nginx welcome page returned. Type exit to leave the busybox Pod (it will be automatically deleted due to --rm).

Task 3: Scale and Verify Endpoints Update Automatically

Scale the Deployment up:

kubectl scale deployment nginx-deployment-XX --replicas=5 -n student-XX

Watch the endpoints update in real time:

kubectl get endpoints nginx-service-XX -n student-XX -w

You should see additional Pod IPs appear as new Pods become ready. Now scale down:

kubectl scale deployment nginx-deployment-XX --replicas=2 -n student-XX

Observe that the endpoints list shrinks automatically. This demonstrates that the Service always tracks the current set of healthy, matching Pods.

Clean Up

kubectl delete -f nginx-clusterip.yaml

Useful Commands

Command Description
kubectl get svc -n student-XX List all Services in your namespace
kubectl get svc nginx-service-XX -n student-XX -o wide Show Service with selector details
kubectl describe svc nginx-service-XX -n student-XX Detailed Service info including endpoints
kubectl get endpoints nginx-service-XX -n student-XX Show current endpoint IPs for the Service
kubectl get endpointslices -n student-XX Show EndpointSlice objects (newer API)
kubectl get svc nginx-service-XX -n student-XX -o yaml Full Service YAML

Common Problems

Problem Possible Cause Solution
Service has no endpoints Selector does not match any Pod labels Compare spec.selector in Service with metadata.labels in Pod template
Cannot connect to Service from test Pod Service or Pod not in same namespace, or wrong port Verify namespace and port configuration
Connection hangs or times out Target container not listening on targetPort Verify the container is running and listening on the expected port
Endpoints show fewer IPs than replicas Some Pods are not passing readiness checks Check Pod readiness with kubectl get pods and kubectl describe pod

Best Practices

  1. Always use label selectors carefully — A mismatch between Service selector and Pod labels is the most common cause of “Service has no endpoints.”
  2. Name your ports — Named ports make it easier to reference ports in other resources and improve readability.
  3. Use ClusterIP for internal communication — If a Service only needs to be accessed by other Pods within the cluster, ClusterIP is the right choice.
  4. Monitor endpoints — Use kubectl get endpoints to verify that the Service is tracking the expected set of Pods.
  5. Do not hardcode Pod IPs — Always use the Service name or ClusterIP to communicate with Pods, never individual Pod IPs.

Summary

In this exercise you learned:

  • Why Services are needed: Pods are ephemeral and their IPs change
  • ClusterIP is the default Service type, accessible only within the cluster
  • Services use label selectors to discover Pods and maintain an Endpoints list
  • kube-proxy provides load balancing across all matching Pods
  • Endpoints update automatically when Pods are added, removed, or fail readiness checks
  • How to test Service connectivity from within the cluster using a temporary Pod

results matching ""

    No results matching ""