Chapter 18: Kubernetes Workloads
18.12 The Service spreads traffic across ready Pods
Why: the problem
Pods come and go. Every new Pod gets a new IP. If the Myntra cart service talked to the pricing service by Pod IP, it would break on every deploy. A Service fixes this. It gives a group of Pods one stable virtual IP and one DNS name, like pricing. It watches which Pods match its label selector and are ready, and sends each connection to one of them. Pods that fail their readiness probe are removed from the list until they recover. So the Service is also a simple load balancer and a safety net. In this lesson you will put a Service in front of three Pods that print their own name, prove that traffic spreads, make one Pod unready and see it leave the list, and look at the EndpointSlice that holds the real Pod IPs.
What we will build
You will run three http-echo Pods that answer with their Pod name, a ClusterIP Service called who, and a client loop that counts answers. Then you will break readiness on one Pod, scale up, and expose the Service on your laptop with port-forward and NodePort.
What: every term in plain English
- Service — A stable virtual IP and DNS name for a set of Pods.
- ClusterIP — The default Service type. Reachable only inside the cluster.
- NodePort — Opens the same port (30000-32767) on every node.
- Selector — The labels a Service uses to find Pods.
- EndpointSlice — The list of ready Pod IPs behind a Service.
- kube-proxy — The agent on each node that programs iptables or IPVS rules for Services.
- Readiness — A Pod is added to the Service only when it is ready.
- port and targetPort — The Service port clients use, and the container port it forwards to.
How: step by step
-
Start a lab cluster
kind create cluster --name lab kubectl get nodesWhat it does: Creates a one-node Kubernetes cluster inside Docker. On minikube use: minikube start.
Output
Creating cluster "lab" ... ✓ Ensuring node image (kindest/node:v1.30.0) ✓ Preparing nodes Set kubectl context to "kind-lab" NAME STATUS ROLES AGE VERSION lab-control-plane Ready control-plane 40s v1.30.0 -
Make a practice namespace
kubectl create namespace practice kubectl config set-context --current --namespace=practiceWhat it does: Keeps all lab objects in one folder that is easy to delete.
Output
namespace/practice created Context "kind-lab" modified. -
Deploy three Pods that say their name
kubectl apply -f who.yamlWhat it does: The file is in Example 1.
Output
deployment.apps/who created service/who created -
See the Service
kubectl get svc whoWhat it does: A stable ClusterIP.
Output
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) who ClusterIP 10.96.81.200 none 80/TCP -
See the endpoints
kubectl get endpointslice -l kubernetes.io/service-name=whoWhat it does: The real Pod IPs.
Output
NAME ADDRESSTYPE PORTS ENDPOINTS who-8x2kq IPv4 5678 10.244.0.11,10.244.0.12,10.244.0.13 -
Call it by DNS
kubectl run c --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- whoWhat it does: Any Pod can use the name.
Output
who-6b9f7c8d5-4kx2m -
Port-forward to your laptop
kubectl port-forward svc/who 8080:80What it does: Your laptop port 8080 goes to the Service. Ctrl+C to stop.
Output
Forwarding from 127.0.0.1:8080 -> 5678
Example 1: Three Pods and a ClusterIP Service
Stack. kind, Kubernetes 1.30
Goal. Three Pods and a ClusterIP Service.
who.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: who
spec:
replicas: 3
selector:
matchLabels: {app: who}
template:
metadata:
labels: {app: who}
spec:
containers:
- name: echo
image: hashicorp/http-echo:1.0
args: ["-text=$(POD_NAME)"]
env:
- name: POD_NAME
valueFrom:
fieldRef: {fieldPath: metadata.name}
ports: [{containerPort: 5678}]
readinessProbe:
httpGet: {path: /, port: 5678}
periodSeconds: 3
---
apiVersion: v1
kind: Service
metadata:
name: who
spec:
selector: {app: who}
ports:
- port: 80
targetPort: 5678
What each line does
fieldRef metadata.name— Puts the Pod name into an env var.-text=$(POD_NAME)— Each Pod answers with its own name.readinessProbe— Decides if the Pod is in the Service.selector app: who— Matches the three Pods.port 80 targetPort 5678— Clients use 80; Pods listen on 5678.
Green output
deployment.apps/who created
service/who created
NAME READY UP-TO-DATE AVAILABLE
who 3/3 3 3
Expected result. The Service name who now works from any Pod in the namespace, and as who.practice.svc.cluster.local from others.
Example 2: Prove traffic is spread
Stack. kube-proxy iptables mode
Goal. Prove traffic is spread.
terminal
kubectl run load --image=busybox:1.36 --restart=Never -- sh -c 'for i in $(seq 300); do wget -qO- who; done'
kubectl wait --for=jsonpath='{.status.phase}'=Succeeded pod/load --timeout=60s
kubectl logs load | sort | uniq -c
What each line does
seq 300— 300 requests.wget -qO- who— Each request opens a new connection, so kube-proxy may pick a new Pod.sort | uniq -c— Counts answers per Pod.
Green output
97 who-6b9f7c8d5-4kx2m
104 who-6b9f7c8d5-9tqz7
99 who-6b9f7c8d5-m2vn8
Expected result. Roughly a third each. In iptables mode the choice is random per connection, which evens out.
Example 3: An unready Pod leaves the Service
Stack. EndpointSlice API
Goal. An unready Pod leaves the Service.
terminal
BAD=$(kubectl get pod -l app=who -o name | head -1)
kubectl label $BAD app=who-off --overwrite
kubectl get endpointslice -l kubernetes.io/service-name=who
kubectl get pods -L app
kubectl scale deploy who --replicas=5
kubectl get endpointslice -l kubernetes.io/service-name=who
What each line does
label app=who-off— The Pod no longer matches the selector. The same happens when readiness fails.endpointslice— Now lists only matching, ready Pods.New Pod— The Deployment also makes a replacement.scale to 5— New Pods join the slice as soon as they are ready.
Green output
pod/who-6b9f7c8d5-4kx2m labeled
NAME ADDRESSTYPE PORTS ENDPOINTS
who-8x2kq IPv4 5678 10.244.0.12,10.244.0.13,10.244.0.14
NAME READY STATUS APP
who-6b9f7c8d5-4kx2m 1/1 Running who-off
who-6b9f7c8d5-9tqz7 1/1 Running who
who-6b9f7c8d5-m2vn8 1/1 Running who
who-6b9f7c8d5-x7k2p 1/1 Running who
deployment.apps/who scaled
who-8x2kq IPv4 5678 10.244.0.12,10.244.0.13,10.244.0.14,10.244.0.15,10.244.0.16
Expected result. The Service list follows labels and readiness in seconds. You never edit IPs by hand.
Verify: did it work?
-
Check the Service has endpoints
kubectl describe svc who | grep -E "Selector|Endpoints"What it does: Empty Endpoints means no Pod matches or none is ready.
Output
Selector: app=who Endpoints: 10.244.0.12:5678,10.244.0.13:5678,10.244.0.14:5678 + 2 more... -
Try NodePort
kubectl patch svc who -p '{"spec":{"type":"NodePort"}}' && kubectl get svc who && docker exec lab-control-plane curl -s localhost:$(kubectl get svc who -o jsonpath={.spec.ports[0].nodePort})What it does: Opens a port on the node. Lesson 19 covers all Service types.
Output
NAME TYPE CLUSTER-IP PORT(S) who NodePort 10.96.81.200 80:31544/TCP who-6b9f7c8d5-9tqz7
When it is red 1
Service has no endpoints (Endpoints: none)
Cause. The selector does not match the Pod labels, or no Pod is ready.
Fix, step by step
- Compare kubectl get svc who -o yaml selector with kubectl get pods --show-labels.
- Check readiness with kubectl describe pod.
When it is red 2
wget: can't connect to remote host (10.96.81.200): Connection refused
Cause. targetPort does not match the port the container listens on.
Fix, step by step
- Check the containerPort and the app listen port.
- Fix targetPort.
When it is red 3
wget: bad address 'who'
Cause. DNS lookup failed: wrong namespace or CoreDNS is down.
Fix, step by step
- Use who.practice.svc.cluster.local.
- Run kubectl get pods -n kube-system -l k8s-app=kube-dns.
Cleanup
-
Delete the lesson objects
kubectl delete deploy who kubectl delete svc who kubectl delete pod load --ignore-not-found kubectl delete pod -l app=who-offWhat it does: Removes the Deployment, Service and leftover Pods.
Output
deployment.apps "who" deleted service "who" deleted -
Delete the practice namespace
kubectl delete namespace practiceWhat it does: Deletes every object you made in this lesson in one go.
Output
namespace "practice" deleted
Ravindra Bagale's Tip
Service la ek kayamcha naav ani IP asto. Fakt ready Pods la traffic jaata. Lakshat theva!
Practice tasks
Easy Read a Service
Why: Know the fields.
What to do: Run kubectl get svc who -o yaml and explain each spec field.
- Look at selector, ports, type, clusterIP.
Check: You can explain every line.
Medium Session affinity
Why: Some apps need sticky users.
What to do: Set sessionAffinity: ClientIP and send 50 requests from one Pod.
- kubectl patch svc who -p spec.sessionAffinity ClientIP
- Run the loop.
Check: All answers come from one Pod.
Hard IPVS mode
Why: Big clusters use IPVS.
What to do: Read how kind can set kubeProxyMode: ipvs and try it.
- Add networking.kubeProxyMode: ipvs to the kind config.
- Run ipvsadm -Ln inside the node.
Check: You see the Service virtual server.
Easy Say how a Service spreads traffic in two lines
Why: If you can say it simply, you understand it. Interviewers want the short version first.
What to do: Write two short lines about how a Service spreads traffic for a fresher who knows only Docker basics.
- Write what problem it solves.
- Write the one kubectl command you use for it.
- Read it aloud to a friend.
Check: A fresher understands it without asking a question.
Easy Start a fresh lab cluster
Why: Practice needs a throwaway cluster. If you break it, you delete it and start again.
What to do: Create a kind cluster, or a minikube cluster, and check the node is Ready.
- kind create cluster --name lab
- kubectl get nodes
- kubectl get pods -A
Check: One node shows STATUS Ready and the kube-system Pods are Running.
Medium Break it on purpose
Why: You must know the red message by face. During an outage there is no time to read slowly.
What to do: Cause one error from the "When it is red" part of this lesson and fix it.
- Follow the error section.
- Copy the exact message and the kubectl describe Events into your notes.
- Write the fix in one line.
Check: You fixed it using only the steps, without searching the web.
Medium Write a one-page runbook
Why: At 2 a.m. nobody remembers the right command. A short page saves the on-call person.
What to do: Write a runbook page for this lesson.
- Title, when to use, exact kubectl command, how to check, how to undo.
Check: A teammate can follow it alone.
Medium Repeat it on a cloud cluster
Why: kind is a laptop toy. Real clusters run on EKS, GKE, AKS or k3s on a VM.
What to do: Do the main steps of this lesson again on a small k3s VM or a free-tier managed cluster.
- Install k3s on one Ubuntu 22.04 VM: curl -sfL https://get.k3s.io | sh -
- Copy the kubeconfig.
- Apply the same YAML and compare the output.
Check: Same output on the cloud cluster as on kind.
Hard Explain it like an interview
Why: Interviewers want real stories, not definitions.
What to do: Record a 60 second answer about this lesson.
- Say the problem, the YAML field you used, the kubectl command you ran, and one error you hit.
Check: Your answer has a real command and a real error message.
Hard Draw the flow top to bottom
Why: A drawing shows who talks to whom and in what order.
What to do: Draw this lesson as boxes from top to bottom.
- Start at kubectl apply, go through the API server, controller and scheduler, end at the running container.
Check: Each box matches a real thing you saw in kubectl output.
Homework: try at home
Try at home
At home, run 4 Pods that print their name behind a Service. Send 400 requests and count answers per Pod. Then make one Pod fail its readiness probe and count again.
Recap
- A Service gives Pods a stable IP and DNS name.
- It sends traffic only to ready Pods that match its selector.
- EndpointSlices hold the real Pod IPs.
- kube-proxy spreads connections across them.
- port-forward and NodePort reach it from outside.
FAQ
Is a Service a real process?
No. ClusterIP is virtual; kube-proxy rules on each node do the forwarding.
Does a Service retry a failed request?
No. Retries are the client or a service mesh job.
Why did one long connection always hit one Pod?
Balancing is per connection, not per request. HTTP keep-alive reuses one connection.
Endpoints or EndpointSlice?
EndpointSlice is the newer, scalable API. Endpoints still exists for older tools.
What is a headless Service?
clusterIP: None. DNS returns Pod IPs directly; used with StatefulSets.
Interview questions
-
How does a Service load balance?
kube-proxy programs iptables or IPVS rules so each new connection to the ClusterIP goes to one ready endpoint.
-
What happens when a Pod fails readiness?
It is removed from the EndpointSlice and gets no new traffic until ready again.
-
Service has no endpoints. What do you check?
Selector versus Pod labels, readiness, and targetPort.
-
How does DNS work for Services?
CoreDNS returns the ClusterIP for NAME.NAMESPACE.svc.cluster.local.
-
Why not use Pod IPs directly?
Pod IPs change on every restart and scale event.
Quick quiz
Q1. What does a Service give a set of Pods?
A stable IP and DNS name.
Q2. What decides which Pods are behind a Service?
The label selector and readiness.
Q3. Where are the real Pod IPs listed?
In the EndpointSlice.
Q4. Which component programs the rules on each node?
kube-proxy.
Q5. How do you reach a ClusterIP Service from your laptop?
kubectl port-forward.
Samajla ka?
Revision card
Interview card