Free lesson · CKA Prep Program · Module 4: Services and networking
Expose Workloads with the Right Service Type
Services turn shifting pod IPs into one stable address. The type decides who can reach it; the endpoints prove it works. This lesson makes both choices boring on purpose.
11 min reading
Objectives
- Pick ClusterIP, NodePort or LoadBalancer for the traffic at hand
- Read Endpoints and EndpointSlices to see exactly where traffic goes
- Explain why a Service has no external IP yet and fix it
- Use headless Services where clients need pod addresses directly
Why this matters
Every connection problem in Kubernetes ends at one of two questions: does an endpoint exist for this Service, and can the client reach the Service address. Half of all networking tickets dissolve once you read endpoints first, because no endpoints means the selector matches nothing and the rest of the debugging is theater.
Concepts
ClusterIP is the default: a stable virtual IP reachable only inside the cluster, backed by kube-proxy rules on every node. Use it for everything internal. NodePort opens the same Service on one port of every node for outside access without a cloud load balancer: fine for labs, crude for production. LoadBalancer provisions an external balancer (on supported clouds) and feeds the Service: the production front door. ExternalName aliases an outside DNS name instead of proxying: no endpoints, just a CNAME.
Endpoints are the membership list. kubectl get endpoints <name> shows ready pod addresses; EndpointSlices shard the same data for large fleets. Empty endpoints with a healthy-looking Service means the selector matches zero pods: compare the Service selector against pod labels character by character. A headless Service (clusterIP: None) skips the virtual IP and returns pod addresses from DNS directly, which stateful clients and service discovery need.
Worked example
One deployment, internal plus debug access, receipts at each step:
kubectl create deployment web --image=nginx --replicas=3
kubectl expose deployment web --port=80 # ClusterIP
kubectl get svc web # CLUSTER-IP set, EXTERNAL-IP <none>
kubectl get endpoints web # three ready addresses
# lab-only outside access on every node port 30080
kubectl expose deployment web --port=80 --type=NodePort --name=web-debug
kubectl get svc web-debug # PORT(S) 80:30080/TCP
curl http://<any-node-ip>:30080/ # nginx welcome pageEndpoints with three addresses is the proof of the internal path; the curl against the node port proves the external one. If endpoints are empty, fix the selector before touching anything else.
Exam notes
NodePort range is 30000 to 32767: a port outside it is rejected, and questions love that boundary. LoadBalancer on bare metal stays Pending without an implementation like MetalLB: know what Pending means there. And ExternalName has no endpoints by design, so an empty endpoints list for it is correct, not broken.
Quick check
A 4-question self-check. Your answers stay on your device, are never stored or sent. Only a passing score (for example 3 of 4) is saved, and only to unlock lesson progress: at least 3 correct is required before a lesson counts as complete. It never counts toward any certification assessment.
Lesson feedback
No published feedback yet.
Log in and complete the lesson to leave feedback.
Exercise
Expose a fictional 2-replica app three ways (ClusterIP, NodePort, headless) and record the endpoints output for each. Then break one by changing a pod label and show the exact endpoints plus event output that identifies the mismatch.
Pass criteria
All three types created with correct expectations; endpoints outputs recorded; the broken case names the selector mismatch with the proving output quoted.