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
- Always use label selectors carefully — A mismatch between Service selector and Pod labels is the most common cause of “Service has no endpoints.”
- Name your ports — Named ports make it easier to reference ports in other resources and improve readability.
- Use ClusterIP for internal communication — If a Service only needs to be accessed by other Pods within the cluster, ClusterIP is the right choice.
- Monitor endpoints — Use
kubectl get endpointsto verify that the Service is tracking the expected set of Pods. - 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