Files
rootnode-academy/content/root/root-07-kubernetes-networking-config/m03-services-and-service-discovery/02-demonstration-zero-endpoints.md
T
ced4568andClaude Sonnet 5 dbcc210889 feat(root): author networking and configuration curriculum
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]>
2026-08-26 09:06:19 -05:00

83 lines
3.3 KiB
Markdown

# 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).