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]>
88 lines
4.2 KiB
Markdown
88 lines
4.2 KiB
Markdown
# The Service/Endpoints/DNS Request Path
|
|
|
|
## The problem: Pod IPs are not stable
|
|
|
|
Every Pod gets its own IP address, but that IP is NOT durable — when a Pod is replaced (a
|
|
rollout, a crash recovery, a reschedule), the new Pod gets a brand new IP. If another part of
|
|
your application had that old IP hardcoded, it would silently start talking to nothing. This is
|
|
the exact problem in `expose-service-basics`'s scenario: "other pods cannot reliably connect
|
|
because pod IPs change when they restart."
|
|
|
|
## A Service: a stable address in front of a moving set of Pods
|
|
|
|
A Service is a stable virtual IP and DNS name that automatically routes to whichever Pods
|
|
currently match its label selector — regardless of how many there are or how often they change.
|
|
You never talk to a Pod's IP directly in normal operation; you talk to the Service, and the
|
|
Service's Endpoints (a live, continuously-updated list of the matching Pods' IPs) determines
|
|
where traffic actually goes.
|
|
|
|
```yaml
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: redis-svc
|
|
spec:
|
|
selector:
|
|
app: redis # <- matches Pods by label, same mechanism from ROOT 05
|
|
ports:
|
|
- port: 6379
|
|
```
|
|
|
|
This is the same label-selector mechanism you learned in ROOT 05 — a Service doesn't know or
|
|
care about specific Pod names, only which Pods currently carry a matching label.
|
|
|
|
## The three Service types you'll use most
|
|
|
|
- **ClusterIP** (the default) — reachable only from inside the cluster. Use this for
|
|
internal-only communication, like one microservice talking to another or to a database.
|
|
- **NodePort** — additionally exposes the Service on a static port on every node's own IP,
|
|
reachable from outside the cluster. Mostly used for development/testing or when there's no
|
|
cloud load balancer available.
|
|
- **LoadBalancer** — asks the cloud provider to provision a real external load balancer pointed
|
|
at the Service. This is the standard way to expose something to the public internet on a
|
|
cloud-hosted cluster.
|
|
|
|
Choosing wrong isn't just a style preference — a ClusterIP Service is architecturally
|
|
unreachable from outside the cluster no matter how correctly everything else is configured.
|
|
|
|
## DNS: how Pods actually find a Service by name
|
|
|
|
Every Service automatically gets a DNS name inside the cluster
|
|
(`<service-name>.<namespace>.svc.cluster.local`, or usually just `<service-name>` from within
|
|
the same Namespace) — this is why application code can use a plain hostname like `redis-svc`
|
|
instead of an IP address at all. Kubernetes' internal DNS resolves that name to the Service's
|
|
stable virtual IP, which then routes to whichever Pods are currently healthy.
|
|
|
|
## The full request path, end to end
|
|
|
|
```
|
|
Client -> DNS lookup "redis-svc" -> Service's stable virtual IP
|
|
-> Service consults its Endpoints (live list of matching, ready Pods)
|
|
-> traffic forwarded to one of those Pod IPs
|
|
```
|
|
|
|
Every one of these steps can fail independently, which is exactly why "the app can't reach
|
|
redis" requires checking each layer rather than guessing — the next activity shows you what
|
|
that looks like when the Endpoints layer is empty.
|
|
|
|
## Instructor Notes
|
|
|
|
**Approximate timing:** 15 minutes read, 10-12 minutes live with the request-path diagram.
|
|
|
|
**Demo plan:** Before the next activity, it's worth live-running `kubectl get endpoints
|
|
<service-name>` against a healthy Service so students see a populated Endpoints list before
|
|
they see an empty one in the demonstration.
|
|
|
|
**Likely misconceptions to watch for:**
|
|
- Students often think a Service "is" a load balancer process running somewhere. It's closer to
|
|
a routing rule maintained by `kube-proxy` on every node — worth clarifying that there's no
|
|
single "Service Pod" to go find and debug.
|
|
- ClusterIP vs. NodePort vs. LoadBalancer is sometimes memorized without the "why" — the
|
|
knowledge check's Service-type question is designed to test the underlying reasoning, not
|
|
just recall.
|
|
|
|
**Discussion question:** "If a Service's selector matches zero Pods, does the Service itself
|
|
fail to exist?" (Answer: no — the Service object exists fine; its Endpoints list is just empty,
|
|
which looks like connection failure from the client's side without any error at the Service
|
|
level itself. This is exactly the next activity's topic.)
|