root-07-m03-services-and-service-discovery — a new, full gold-standard- pattern module: the Service/Endpoints/DNS request path, choosing the right Service type. Lesson, demonstration (a Service with zero Endpoints — the single fastest diagnostic check for this failure class), knowledge check, guided exercise (ClusterIP vs. NodePort vs. LoadBalancer for three real components), a real Lab (expose-service-basics), and a reflection. root-07-m04-ingress-networkpolicy-foundations — a new PARTIAL module: Ingress and NetworkPolicy at the architectural level (the security- control framing of NetworkPolicy is deliberately deferred to ROOT 09). Lesson + knowledge check + real Labs (ingress-task, network-policy). Co-Authored-By: Claude Sonnet 5 <[email protected]>
3.3 KiB
Demonstration: A Service With Zero Endpoints
This is one of the most common "it just doesn't work" incidents in real Kubernetes operation — and it produces no error message at all at the Service level.
The setup
A Service api-svc selects Pods labeled app: api. A Deployment was recently updated and its
Pods now carry the label app: api-v2 instead (a rename during a refactor, deliberately or
accidentally).
What you'd see from the client side
$ curl http://api-svc:8080/health
curl: (7) Failed to connect to api-svc port 8080: Connection refused
Just a generic connection failure — nothing here says "your Service's selector doesn't match any Pods."
What the Service object itself shows
$ kubectl get service api-svc
NAME TYPE CLUSTER-IP PORT(S) AGE
api-svc ClusterIP 10.96.34.201 8080/TCP 2h
Completely normal-looking. The Service exists, has a stable IP, is not in any kind of "error" state — because from the Service's own perspective, nothing is wrong. It's doing exactly what it's configured to do: route to whatever matches its selector, which currently happens to be nothing.
Where the real evidence is: Endpoints
$ kubectl get endpoints api-svc
NAME ENDPOINTS AGE
api-svc <none> 2h
This is the actual signal. <none> means the Service's selector currently matches zero Pods —
this is the single fastest check for "is this a Service-to-Pod mismatch" any time something
reachable-in-principle looks like a connection failure.
Confirming the mismatch
$ kubectl get pods --show-labels
NAME READY STATUS LABELS
api-abc123 1/1 Running app=api-v2
The Pod is healthy and running — app=api-v2, not app=api. The Service's selector
(app: api) matches nothing, and nothing about the Pod itself is broken.
The general diagnostic shape
curl/application-level connection failure tells you that something's wrong, but nothing
about where. kubectl get endpoints <service> is the fastest way to distinguish "the Service
has no matching Pods" from "the Pods exist and are ready but something else is wrong" —
reach for it early whenever a Service-fronted connection fails for no obvious reason.
Instructor Notes
Demo script: Live-create this exact mismatch (a Service with one selector, a Deployment
with a slightly different label) and walk through kubectl get service -> kubectl get endpoints -> kubectl get pods --show-labels in front of the class, narrating what each
command is and isn't telling you.
Approximate timing: 8-10 minutes live; 5-7 minutes self-paced.
Common misconception to preempt: students often check the Service object first and, seeing
nothing obviously wrong, assume the problem must be elsewhere entirely (DNS, firewall, the
application itself) — reinforce that kubectl get endpoints is the specific, fast check for
this exact failure mode, before reaching for anything more complex.
Optional extension: ask what kubectl describe service api-svc would additionally show
compared to kubectl get service (answer: the same selector the Service is using, which lets
you directly compare it against kubectl get pods --show-labels's actual Pod labels side by
side, rather than inferring the mismatch).