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]>
This commit is contained in:
+87
@@ -0,0 +1,87 @@
|
|||||||
|
# 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.)
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
# 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).
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
# Knowledge Check: Services & Service Discovery
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**1. A Deployment has three replicas but `kubectl get endpoints` shows `<none>` for its Service.
|
||||||
|
Which part of the system should you inspect first, and why?**
|
||||||
|
|
||||||
|
*Answer:* Compare the Service's selector (`kubectl describe service`) against the actual Pods'
|
||||||
|
labels (`kubectl get pods --show-labels`) — a Service with a healthy backend Deployment but zero
|
||||||
|
Endpoints almost always means the selector doesn't match the Pods' current labels, not that the
|
||||||
|
Pods themselves are unhealthy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**2. You need a microservice reachable ONLY from other services inside the same cluster, never
|
||||||
|
from the public internet. Which Service type, and why not LoadBalancer?**
|
||||||
|
|
||||||
|
*Answer:* ClusterIP — it's reachable only from inside the cluster by design, which is exactly
|
||||||
|
the requirement. A LoadBalancer would provision a real external endpoint reachable from the
|
||||||
|
public internet, which is more exposure than the requirement calls for, and depends on cloud
|
||||||
|
provider support this scenario doesn't need.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**3. True or false: application code that connects to `redis-svc` by that plain hostname is
|
||||||
|
relying on Kubernetes' internal DNS to resolve it.**
|
||||||
|
|
||||||
|
*Answer:* True. Every Service automatically gets a DNS name; that's specifically what lets code
|
||||||
|
use a stable hostname instead of hardcoding a Pod IP that could change at any time.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**4. A Service's Endpoints list is non-empty, but the application still can't connect. Does
|
||||||
|
this rule out a Service-level problem entirely?**
|
||||||
|
|
||||||
|
*Answer:* Not entirely — a non-empty Endpoints list means at least one Pod currently matches the
|
||||||
|
selector, but the connection could still fail for other reasons (wrong port number on the
|
||||||
|
Service, a NetworkPolicy blocking the traffic, the application inside the Pod not actually
|
||||||
|
listening). A populated Endpoints list narrows the problem, it doesn't prove the whole path
|
||||||
|
works.
|
||||||
|
|
||||||
|
## Instructor Notes
|
||||||
|
|
||||||
|
**Facilitation:** Question 1 is the single most transferable diagnostic habit from this module —
|
||||||
|
worth explicitly calling out as "the first thing to check" for this entire class of problem.
|
||||||
|
|
||||||
|
**Approximate timing:** 5 minutes.
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
# Guided Exercise: Choosing the Right Service Type
|
||||||
|
|
||||||
|
Paper-and-thinking exercise before the Lab.
|
||||||
|
|
||||||
|
## The scenario
|
||||||
|
|
||||||
|
You're deploying three components for a class project on a cloud-hosted cluster:
|
||||||
|
|
||||||
|
1. A Postgres database that only your own backend application should ever talk to.
|
||||||
|
2. A backend API that the frontend (also running in the cluster) calls internally.
|
||||||
|
3. The frontend web application itself, which needs to be reachable by users on the public
|
||||||
|
internet.
|
||||||
|
|
||||||
|
## Your task
|
||||||
|
|
||||||
|
For each of the three components, decide: ClusterIP, NodePort, or LoadBalancer — and justify
|
||||||
|
each choice in one sentence referencing WHO needs to reach it.
|
||||||
|
|
||||||
|
## A worked answer to check yourself against
|
||||||
|
|
||||||
|
1. **Postgres — ClusterIP.** Only your own backend (running inside the same cluster) needs to
|
||||||
|
reach it; there is no reason for it to be reachable from outside the cluster at all, and
|
||||||
|
exposing a database externally is a real, avoidable security risk.
|
||||||
|
2. **Backend API — ClusterIP.** The frontend calls it from inside the cluster too — same
|
||||||
|
reasoning as the database. External users never talk to the backend directly; they only ever
|
||||||
|
go through the frontend.
|
||||||
|
3. **Frontend — LoadBalancer** (on a real cloud-hosted cluster). Public internet users need to
|
||||||
|
reach it, and a cloud provider's LoadBalancer is the standard mechanism for that. NodePort
|
||||||
|
would technically work but is typically used for development/testing rather than a real
|
||||||
|
public-facing service on a cloud cluster.
|
||||||
|
|
||||||
|
## Self-check
|
||||||
|
|
||||||
|
If you chose LoadBalancer for the database or the backend API, reconsider — "internet-reachable
|
||||||
|
by default" is not the safe default for anything that doesn't need to be, and getting this
|
||||||
|
choice right the first time avoids a real security exposure later, not just a style issue.
|
||||||
|
|
||||||
|
## Instructor Notes
|
||||||
|
|
||||||
|
**Facilitation:** Push on the "why not just LoadBalancer everything, it works" instinct some
|
||||||
|
students will have — this is a good early moment to connect Service-type choice to the security
|
||||||
|
mindset that ROOT 09 builds on more fully (exposure should be intentional, not the path of
|
||||||
|
least resistance).
|
||||||
|
|
||||||
|
**Approximate timing:** 10 minutes live with discussion; 10 minutes self-paced.
|
||||||
|
|
||||||
|
**Optional extension:** ask what would need to be added (not covered in this module) to let
|
||||||
|
public users reach the frontend at a real domain name over HTTPS rather than a bare
|
||||||
|
LoadBalancer IP — a natural forward pointer to Ingress, covered in the next module.
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
# Launch Lab: Exposing an Application
|
||||||
|
|
||||||
|
Time to create a real Service in your disposable Lab and connect it to a real Deployment.
|
||||||
|
|
||||||
|
## What you're doing
|
||||||
|
|
||||||
|
A `redis` Deployment is running, but other Pods can't reliably reach it because Pod IPs change
|
||||||
|
whenever a Pod restarts — exactly the problem the lesson opened with. Your task is to create a
|
||||||
|
stable ClusterIP Service in front of it.
|
||||||
|
|
||||||
|
## Before you start
|
||||||
|
|
||||||
|
Recall the request path from the lesson: your Service's `selector` has to match the Deployment
|
||||||
|
Pods' actual labels, or you'll end up with the empty-Endpoints situation from the
|
||||||
|
demonstration. Check the Deployment's Pod labels before writing your Service's selector, don't
|
||||||
|
guess at them.
|
||||||
|
|
||||||
|
## Your task in this Lab
|
||||||
|
|
||||||
|
Create a ClusterIP Service exposing the `redis` Deployment on the correct port. Once it's
|
||||||
|
created, verify it the same way the demonstration did: `kubectl get service`, then `kubectl get
|
||||||
|
endpoints` to confirm it actually has a non-empty Endpoints list, not just that the Service
|
||||||
|
object exists.
|
||||||
|
|
||||||
|
This Lab has its own in-context hints if you get stuck.
|
||||||
|
|
||||||
|
When you're done, return to this module for a reflection on why service discovery is
|
||||||
|
infrastructure, not magic.
|
||||||
|
|
||||||
|
## Instructor Notes
|
||||||
|
|
||||||
|
**Classroom flow:** Encourage students to run `kubectl get endpoints` as their OWN verification
|
||||||
|
step even though the Lab's automated validation will check this too — building the habit of
|
||||||
|
checking Endpoints, not just "the Service exists," is the actual transferable skill here.
|
||||||
|
|
||||||
|
**Approximate timing:** 10-15 minutes.
|
||||||
|
|
||||||
|
**Common mistake to watch for:** a selector that matches on the wrong label key/value, or a
|
||||||
|
port/targetPort mismatch (the Service's `port` is what clients connect to; `targetPort` is what
|
||||||
|
the container actually listens on — conflating the two is a very common first-time error).
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
# Reflection: Service Discovery Is Infrastructure, Not Magic
|
||||||
|
|
||||||
|
You just created a Service that gives a moving set of Pods a stable address. Take a few minutes
|
||||||
|
to connect this to real operational thinking.
|
||||||
|
|
||||||
|
## Reflection questions
|
||||||
|
|
||||||
|
1. Before this module, "the app can just connect to redis by name" might have seemed obvious or
|
||||||
|
automatic. What specific mechanism actually makes that true, and what would break it?
|
||||||
|
2. If your Service's selector had been wrong (matching zero Pods), what would the SYMPTOM have
|
||||||
|
looked like from the application's side, versus what the actual CAUSE would have been?
|
||||||
|
3. Why is checking `kubectl get endpoints` before assuming a networking/DNS/firewall problem a
|
||||||
|
good default habit, given how common a selector mismatch actually is?
|
||||||
|
|
||||||
|
## Real Ops takeaway
|
||||||
|
|
||||||
|
**"It's probably a networking issue" is often wrong, and Endpoints is how you find out fast.** A
|
||||||
|
huge fraction of real "service X can't reach service Y" incidents in Kubernetes are Service
|
||||||
|
selector mismatches, not actual network problems — checking `kubectl get endpoints` first, before
|
||||||
|
escalating to firewall rules or DNS debugging, saves real time precisely because this failure
|
||||||
|
mode produces zero errors at the layers people instinctively check first.
|
||||||
|
|
||||||
|
**Stable addressing is what makes rolling updates and self-healing actually usable.** Everything
|
||||||
|
you learned in ROOT 06 about Pods being replaced during a rollout or after a crash only works
|
||||||
|
smoothly in practice because Services give callers a stable target that survives those
|
||||||
|
replacements — without this layer, every Pod replacement would require updating every caller's
|
||||||
|
configuration by hand.
|
||||||
|
|
||||||
|
This module is complete. Continue to the next module.
|
||||||
|
|
||||||
|
## Instructor Notes
|
||||||
|
|
||||||
|
**Discussion questions for a live classroom:** use question 2 as a discussion prompt — the
|
||||||
|
symptom/cause gap is exactly why this class of bug is often under-diagnosed in real teams.
|
||||||
|
|
||||||
|
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced writing.
|
||||||
|
|
||||||
|
**Optional extension:** ask students to write the one-line runbook entry they'd want a future
|
||||||
|
on-call engineer to have for "Service X seems unreachable" — a good exercise in turning a
|
||||||
|
lesson into something operationally reusable.
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
# Ingress and NetworkPolicy: What They Architecturally Do
|
||||||
|
|
||||||
|
## Ingress: routing external HTTP(S) traffic by host/path
|
||||||
|
|
||||||
|
You already know a LoadBalancer Service can expose something to the public internet. But a real
|
||||||
|
application usually needs more than "one raw IP to one Service" — you might want
|
||||||
|
`api.example.com` and `app.example.com` to route to different backend Services, all through one
|
||||||
|
entry point, with TLS termination handled in one place instead of per-Service.
|
||||||
|
|
||||||
|
**Ingress** is the Kubernetes object for exactly this: a set of HTTP routing rules (by hostname
|
||||||
|
and/or URL path) that an Ingress controller reads and turns into actual load-balancer/proxy
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: app-ingress
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: app.example.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: frontend-svc
|
||||||
|
port: { number: 80 }
|
||||||
|
```
|
||||||
|
|
||||||
|
An important distinction: Ingress is a set of ROUTING RULES, not a load balancer itself — an
|
||||||
|
Ingress controller (a separate piece of software running in the cluster) is what actually reads
|
||||||
|
these rules and does the routing. Without a controller installed, an Ingress object exists in
|
||||||
|
the API but nothing acts on it.
|
||||||
|
|
||||||
|
## NetworkPolicy: traffic reachability as something you declare, not assume
|
||||||
|
|
||||||
|
By default, every Pod in a Kubernetes cluster can reach every other Pod — there is no built-in
|
||||||
|
network isolation between them at all. This surprises people coming from a "obviously the
|
||||||
|
network is locked down unless configured otherwise" assumption. It is exactly backwards from
|
||||||
|
that assumption: **everything is reachable until you say otherwise.**
|
||||||
|
|
||||||
|
A NetworkPolicy changes this for the Pods it selects, by declaring which traffic IS allowed —
|
||||||
|
everything not explicitly allowed to a Pod covered by at least one NetworkPolicy is then denied.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: allow-frontend-to-backend
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels: { app: backend }
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels: { app: frontend }
|
||||||
|
```
|
||||||
|
|
||||||
|
This example allows traffic to `backend` Pods only from `frontend` Pods — once this policy
|
||||||
|
exists and selects `backend`, every OTHER Pod trying to reach `backend` is now blocked, even
|
||||||
|
though nothing was true about that before the policy existed.
|
||||||
|
|
||||||
|
## Why this matters architecturally, before it matters as a security control
|
||||||
|
|
||||||
|
Right now, think of NetworkPolicy purely as "declaring intended reachability, not assuming it."
|
||||||
|
ROOT 09 comes back to NetworkPolicy specifically as a SECURITY control (default-deny postures,
|
||||||
|
least-privilege network access) — this module is deliberately just the architectural mental
|
||||||
|
model: Kubernetes doesn't isolate network traffic for you by default, and NetworkPolicy is the
|
||||||
|
mechanism for declaring the reachability you actually intend.
|
||||||
|
|
||||||
|
## Instructor Notes
|
||||||
|
|
||||||
|
**Approximate timing:** 15 minutes read; 10 minutes live.
|
||||||
|
|
||||||
|
**Likely misconceptions to watch for:**
|
||||||
|
- Students often assume Ingress by itself "does" the routing. Emphasize the controller
|
||||||
|
requirement — an Ingress object with no controller installed does nothing.
|
||||||
|
- The "everything reachable by default" fact genuinely surprises most students the first time
|
||||||
|
they hear it — it's worth stating plainly and letting it land rather than rushing past it.
|
||||||
|
|
||||||
|
**Discussion question:** "If NO NetworkPolicy exists anywhere in a Namespace, what's the actual
|
||||||
|
reachability between Pods in that Namespace?" (Answer: completely open — every Pod can reach
|
||||||
|
every other Pod, matching the "everything reachable by default" rule.)
|
||||||
|
|
||||||
|
**Optional extension:** ask what would happen if you created a NetworkPolicy that selects
|
||||||
|
`frontend` Pods but declares no `ingress` rules at all (answer: `frontend` Pods would become
|
||||||
|
completely unreachable by anything — an empty ingress list inside a NetworkPolicy that HAS an
|
||||||
|
ingress policyType means "deny all incoming," not "no policy applies").
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
# Knowledge Check: Ingress & NetworkPolicy Foundations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**1. You create an Ingress object with correct routing rules, but no traffic is ever routed
|
||||||
|
according to them. What's the most likely reason?**
|
||||||
|
|
||||||
|
*Answer:* No Ingress controller is installed/running in the cluster. Ingress objects are just
|
||||||
|
routing rules; a separate controller has to actually read and act on them, or nothing happens.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**2. True or false: by default, a Pod in Namespace A cannot reach a Pod in Namespace B unless a
|
||||||
|
NetworkPolicy explicitly allows it.**
|
||||||
|
|
||||||
|
*Answer:* False — the opposite is true by default. With NO NetworkPolicy in place at all,
|
||||||
|
every Pod can reach every other Pod regardless of Namespace. NetworkPolicy is what RESTRICTS
|
||||||
|
this default-open behavior, not what's required to enable connectivity in the first place.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**3. A NetworkPolicy selects Pods labeled `app: backend` and declares one ingress rule allowing
|
||||||
|
traffic from Pods labeled `app: frontend`. What happens to traffic from a Pod labeled
|
||||||
|
`app: admin-tool` trying to reach `backend`?**
|
||||||
|
|
||||||
|
*Answer:* It's blocked. Once a NetworkPolicy selects a Pod, only traffic matching one of its
|
||||||
|
explicit rules is allowed — everything else to that Pod is denied, including traffic that would
|
||||||
|
have worked fine before the policy existed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**4. Why is Ingress the right tool for exposing an HTTP application by hostname, rather than
|
||||||
|
creating a separate LoadBalancer Service per hostname?**
|
||||||
|
|
||||||
|
*Answer:* A LoadBalancer Service typically provisions its own external IP/load balancer per
|
||||||
|
Service — one per hostname would mean paying for and managing N separate external endpoints.
|
||||||
|
Ingress lets one entry point route to many backend Services by hostname/path, which is both
|
||||||
|
cheaper and centralizes concerns like TLS termination.
|
||||||
|
|
||||||
|
## Instructor Notes
|
||||||
|
|
||||||
|
**Facilitation:** Question 2 catches a very common and consequential misconception — worth
|
||||||
|
confirming out loud that everyone actually updated their mental model, not just guessed
|
||||||
|
correctly.
|
||||||
|
|
||||||
|
**Approximate timing:** 5 minutes.
|
||||||
Reference in New Issue
Block a user