03 - Services & Cluster Networking Basics
Why this matters
Pods are ephemeral and get new IPs constantly. Services give you a stable way to reach a set of pods. This is the #1 source of "why can't my app talk to X" debugging.
Read this first — Definitions & Explanations
Why Services exist
Pods are mortal — they die, get rescheduled, change IPs. A Service gives a stable virtual IP (ClusterIP) and DNS name that fronts a set of Pods selected by labels.
ClusterIP
Default Service type. Reachable inside the cluster only. Other Pods call my-svc.my-ns.svc.cluster.local.
NodePort
Exposes the Service on a static port on each node’s IP. Useful for labs/demos; less common as a public production entry by itself.
LoadBalancer
Cloud providers allocate an external load balancer that fronts the Service. On bare minikube this may be emulated (e.g. tunnel/metallb patterns depending on setup).
ExternalName
Maps a Service name to an external DNS name (CNAME-style). No proxying of Pods — just DNS aliasing.
Headless Service (clusterIP: None)
No virtual ClusterIP. DNS returns Pod IPs directly. Common with StatefulSets when clients need to address individual Pods.
Endpoints / EndpointSlices
Track which Pod IPs/ports are currently valid backends for a Service. Ready Pods appear here; not-ready Pods usually do not receive traffic.
kube-proxy’s role
Implements Service routing on nodes so packets to a Service IP get forwarded to an endpoint Pod. Modes: iptables (common) or IPVS (better at huge Service scale).
Label selectors
Services find Pods with matching labels (app=web). If labels don’t match, the Service has no endpoints — traffic goes nowhere.
Official docs (read for detail)
Key Concepts
- Every pod gets its own IP (flat network, no NAT between pods — the CNI's job)
- Service types: ClusterIP (default), NodePort, LoadBalancer, ExternalName, headless
- kube-proxy modes: iptables vs IPVS — how Service IPs get translated to pod IPs
- Endpoints / EndpointSlices: the actual pod IPs backing a Service
- CoreDNS: how
myservice.mynamespace.svc.cluster.localresolves - Service selectors — how a Service finds "its" pods via labels
- Headless services for StatefulSets (direct pod DNS, no load balancing)
YouTube search terms
- "Kubernetes Services explained ClusterIP NodePort LoadBalancer"
- "Kubernetes networking model explained kube-proxy iptables"
- "Kubernetes CoreDNS explained"
- "Kubernetes headless service StatefulSet DNS"
Hands-on lab (on prod-sim)
kubectl create deployment web --image=nginx --replicas=3
kubectl expose deployment web --port=80 --type=ClusterIP
kubectl get svc web
kubectl get endpointslices -l kubernetes.io/service-name=web
# Prove DNS resolution works cluster-internally
kubectl run tmp --rm -it --image=busybox --restart=Never -- \
sh -c "nslookup web.default.svc.cluster.local; wget -qO- web"
# Watch iptables rules kube-proxy generated for this Service (on any node)
minikube ssh -p prod-sim
sudo iptables -t nat -L | grep -A5 web
exit
# NodePort — reach the service from outside the cluster network via node IP
kubectl expose deployment web --port=80 --type=NodePort --name=web-nodeport
kubectl get svc web-nodeport # note the NodePort e.g. 30xxx
minikube ip -p prod-sim
curl http://$(minikube ip -p prod-sim):<nodeport>
# Break the selector on purpose and watch endpoints go empty
kubectl patch svc web -p '{"spec":{"selector":{"app":"doesnotexist"}}}'
kubectl get endpointslices -l kubernetes.io/service-name=web # empty now
kubectl patch svc web -p '{"spec":{"selector":{"app":"web"}}}' # fix it back
Notes
(fill in your own words after watching + labbing)