feat(root02): author Linux Administration & Networking
Adds all 6 ROOT 02 modules (Linux system administration, storage & filesystems, the Networking Mental Model, Linux network inspection, DNS & HTTP, Linux troubleshooting) — 37 activities, each with a real lesson, demonstration/guided exercise, knowledge check, hands-on Lab, and reflection. The Networking Mental Model module (M3) gets deliberately extra depth per its "critical module" designation, with no lab by design — conceptual mastery via knowledge check and guided exercise, matching root-05-m00's own precedent. Adds 6 new hands-on Linux/networking scenarios plus 2 MCQs: linux-service-inspection (inspects the platform's own real nginx without touching it — a genuinely safe, real service-inspection target that avoids ever risking the Lab's own connectivity), linux-service-lifecycle (start/ verify/stop a fully controllable service), linux-storage-inspection, linux-network-inspection (ip/ss/getent/curl, each framed as answering one specific diagnostic question), linux-dns-http-diagnosis (a genuine DNS-resolves-but-wrong-port failure, diagnosed and fixed), and linux-troubleshooting-framework (a real missing-config-file failure, resolved via the RootNode Observe/Evidence/Hypothesis/Test/Repair/Validate/Prevent framework). All tested end-to-end via the real scenario-validation API, including negative tests confirming they fail when unsolved. Wires rootnode-linux-networking-assessment (2 required + 4 random-draw scenarios) using the existing Phase 3B assessment engine. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
+64
@@ -0,0 +1,64 @@
|
||||
# Linux System Administration
|
||||
|
||||
## Five questions every administrator asks
|
||||
|
||||
Whenever you're handed an unfamiliar running system, you're really always answering the same
|
||||
five questions: **What is running? Who started it? Where is its configuration? Where are its
|
||||
logs? How do I verify its state?** This module builds the vocabulary and tools for all five.
|
||||
|
||||
## Packages: how software gets installed
|
||||
|
||||
A **package manager** (`apt` on Debian/Ubuntu, `dnf`/`yum` on Fedora/RHEL) installs, updates, and
|
||||
removes software along with tracking what depends on what — the alternative (manually downloading
|
||||
and compiling everything) is exactly the kind of unreproducible mess package managers exist to
|
||||
prevent. You won't install packages by hand often in this Lab, but recognizing `apt install` /
|
||||
`apt list --installed` matters for reading real systems.
|
||||
|
||||
## Services: long-running background programs
|
||||
|
||||
A **service** is a program meant to run continuously in the background, outliving any single
|
||||
terminal session — a web server, a database, a log collector. Unlike a command you run and wait
|
||||
for, a service starts, keeps running, and is expected to still be there when you come back.
|
||||
|
||||
## systemd and systemctl (the concept — not available in this Lab)
|
||||
|
||||
On most real Linux servers, **systemd** is the process manager (PID 1) responsible for starting,
|
||||
stopping, and supervising services, and `systemctl` is the command you'd use to control it
|
||||
(`systemctl status nginx`, `systemctl restart nginx`, `journalctl -u nginx` for its logs).
|
||||
**This disposable Lab container does not run systemd** — its PID 1 is a boot script that starts
|
||||
k3s directly, the same way a real container almost never runs a full init system. This is worth
|
||||
knowing honestly rather than pretending otherwise: the CONCEPTS below (a running process, its
|
||||
config file, its logs, verifying its state) are identical whether the supervisor is systemd or
|
||||
something else — this module's Labs teach those concepts against a service you CAN directly
|
||||
inspect and control in this environment.
|
||||
|
||||
## Configuration files and environment variables
|
||||
|
||||
Most services read their settings from either a config file (a specific path, usually under
|
||||
`/etc/`) or **environment variables** (`env` lists every variable in your current shell;
|
||||
`export NAME=value` sets one). Knowing WHERE a service's configuration lives is exactly the
|
||||
"where is its configuration" question from this lesson's opening — you'll answer it for real in
|
||||
this module's first Lab.
|
||||
|
||||
## Logs: where a service tells you what happened
|
||||
|
||||
A well-behaved service writes what it's doing to a log — usually a file under `/var/log/`.
|
||||
Reading logs (with `cat`, `tail`, `tail -f` — from ROOT 01) is almost always the first real step
|
||||
in figuring out why something isn't working, which Module 6 builds into a full framework.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Establish the five-question mental model (running/started-by/config/logs/
|
||||
verify) as the organizing frame for the rest of this module, and be explicit and honest that
|
||||
systemd isn't available in this specific Lab environment rather than silently working around it.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live.
|
||||
|
||||
**Common misconception to watch for:** students who've used systemd elsewhere may expect
|
||||
`systemctl` to exist here — address this directly and early rather than letting them discover
|
||||
the error on their own.
|
||||
|
||||
**Discussion question:** "If you SSH into an unfamiliar production server and need to figure out
|
||||
what's running and whether it's healthy, what's the very first command you'd run?" (There's no
|
||||
single right answer — the point is getting students to articulate their own process before this
|
||||
module hands them formal tools for it.)
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# Demonstration: Answering the Five Questions For Real
|
||||
|
||||
## OBSERVE
|
||||
|
||||
This Lab's own terminal is served by a real running service: nginx.
|
||||
|
||||
```
|
||||
$ pgrep -x nginx
|
||||
906
|
||||
```
|
||||
|
||||
Something with PID 906 is running.
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Predict: where would nginx's configuration file be, and where would its logs be?
|
||||
|
||||
## INSPECT
|
||||
|
||||
```
|
||||
$ cat /etc/nginx/nginx.conf | head -5
|
||||
worker_processes 1;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /run/nginx.pid;
|
||||
...
|
||||
$ ls /var/log/nginx/
|
||||
access.log error.log
|
||||
```
|
||||
|
||||
## CHANGE
|
||||
|
||||
Confirm it's actually listening on the port you'd expect:
|
||||
|
||||
```
|
||||
$ ss -lnt | grep :80
|
||||
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
|
||||
```
|
||||
|
||||
## VERIFY
|
||||
|
||||
All five questions answered with real evidence, not assumptions: it's running (PID 906),
|
||||
listening on port 80, configured at `/etc/nginx/nginx.conf`, logging to `/var/log/nginx/`. This
|
||||
exact sequence — process, port, config, logs — is what you'll repeat for almost any unfamiliar
|
||||
service on any real system.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Show the five-question checklist answered end-to-end against a real, already-
|
||||
running service the students are themselves using right now — more convincing than an
|
||||
abstract example.
|
||||
|
||||
**Approximate timing:** 8 minutes live.
|
||||
|
||||
**Common mistake to watch for:** stopping here at "PID exists" without also confirming it's
|
||||
actually listening on the expected port — a process existing and a service actually being
|
||||
reachable are two different facts, both worth checking.
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
# Knowledge Check: Linux System Administration
|
||||
|
||||
---
|
||||
|
||||
**1. What are the five questions this module opened with?**
|
||||
|
||||
*Answer:* What is running? Who started it? Where is its configuration? Where are its logs? How
|
||||
do I verify its state?
|
||||
|
||||
---
|
||||
|
||||
**2. Why doesn't this Lab environment have `systemctl`?**
|
||||
|
||||
*Answer:* This Lab's container doesn't run systemd as PID 1 — its boot script starts k3s
|
||||
directly instead, the same way most containers don't run a full init system. The concepts
|
||||
(inspecting a running service's process/config/logs/state) are the same regardless of which
|
||||
supervisor manages it.
|
||||
|
||||
---
|
||||
|
||||
**3. A process exists (`pgrep` finds a PID) but the service seems unreachable. What's the next
|
||||
thing to check?**
|
||||
|
||||
*Answer:* Whether it's actually listening on the expected port (`ss -lnt`) — a running process
|
||||
and a reachable service are two separate facts; the process could have started but failed to
|
||||
bind its port.
|
||||
|
||||
---
|
||||
|
||||
**4. Where does a well-behaved Linux service typically write what it's doing?**
|
||||
|
||||
*Answer:* A log file, usually under `/var/log/`.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 2 is worth confirming explicitly if any student has prior systemd
|
||||
experience — set the expectation before the Lab, not after a confusing error.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
# Guided Exercise: Plan Your Inspection Before You Type Anything
|
||||
|
||||
This is a paper exercise — commit to a plan before running commands in the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
You're told "the payments service seems slow." Before touching a keyboard, write down, in
|
||||
order, which of these five questions you'd check FIRST, SECOND, and so on, and the one command
|
||||
you'd use for each:
|
||||
|
||||
- Is it running at all?
|
||||
- Is it listening on its expected port?
|
||||
- What does its configuration say?
|
||||
- What do its recent logs say?
|
||||
- Has anything changed recently?
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
A reasonable order: (1) is it running — `pgrep`/`ps`; (2) is it listening — `ss -lnt`; (3) recent
|
||||
logs — `tail -n 50 <logfile>` (recent activity is usually more informative than config for a
|
||||
sudden "slow" complaint); (4) configuration — only if logs point at a config-driven cause; (5)
|
||||
recent changes — depends on context (deployment history, `ls -la` timestamps on config files).
|
||||
There's no single correct order, but "running, then listening, then logs" before jumping to
|
||||
config is a defensible default — you're checking the cheapest, fastest-answering questions
|
||||
first.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** There's genuine room for disagreement on ordering here — the value is in
|
||||
students defending their reasoning, not converging on one "correct" sequence.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# Reflection: You Now Have a Repeatable Checklist
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Before this module, if someone said "check whether the service is healthy," what would you
|
||||
have actually done? How does that compare to the five-question checklist now?
|
||||
2. You started and stopped a service you fully controlled this module. What's different about
|
||||
doing the same thing to a service you DIDN'T start, on a machine you don't fully understand
|
||||
yet?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**The five-question checklist works on ANY unfamiliar Linux system, not just this Lab.** Running
|
||||
process, listening port, config location, logs, and current state — this is genuinely how
|
||||
experienced engineers approach a machine they've never touched before. You'll use this exact
|
||||
checklist again in ROOT 04 when a container "doesn't seem to be working," and again in
|
||||
Kubernetes when a Pod isn't behaving as expected — the tools change, the checklist doesn't.
|
||||
|
||||
This module is complete. Continue to the next module.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 2 is worth real discussion time — the
|
||||
honest answer is "you should be more careful and inspect more before acting," a good early
|
||||
seed for the least-privilege/caution habits ROOT 09 formalizes much later.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
# Storage and Filesystems
|
||||
|
||||
## Block devices and partitions, conceptually
|
||||
|
||||
A **block device** is the kernel's view of a storage device (a disk, an SSD, a cloud volume) —
|
||||
`lsblk` lists them. A device can be split into **partitions**, separate sections each formatted
|
||||
with their own filesystem, though a single-partition disk is common too. You won't partition
|
||||
anything in this Lab, but recognizing `lsblk`'s output (`sda`, `sda1`, `nvme0n1`...) matters for
|
||||
reading real systems.
|
||||
|
||||
## Filesystems and mount points
|
||||
|
||||
A **filesystem** is the format that organizes raw blocks into the files/directories you actually
|
||||
interact with (ext4, xfs, and — inside this container — overlay, a filesystem UNIQUE to how
|
||||
containers layer their storage). A filesystem becomes usable by being **mounted** at some
|
||||
directory path — `/` itself is a mount point, and additional filesystems can be mounted at other
|
||||
paths (a separate disk mounted at `/data`, for example).
|
||||
|
||||
## Persistent vs. temporary storage — a distinction that matters immediately
|
||||
|
||||
Not everything written to disk is meant to last. `/tmp` is conventionally temporary (ROOT 01);
|
||||
a container's own writable layer is, by convention and by ROOT 04's design, meant to be
|
||||
DISPOSABLE — data written inside a container that isn't explicitly persisted elsewhere disappears
|
||||
when that container is destroyed. This distinction — is this data supposed to survive, or not —
|
||||
is the single most important storage question you'll carry forward into ROOT 04's volumes and
|
||||
eventually Kubernetes' PersistentVolumes.
|
||||
|
||||
## Measuring usage: `df` vs. `du`
|
||||
|
||||
These answer two DIFFERENT questions, a common point of confusion:
|
||||
|
||||
- **`df -h`** — how much space is used/free on an entire mounted FILESYSTEM.
|
||||
- **`du -sh <path>`** — how much space one specific DIRECTORY's contents actually use.
|
||||
|
||||
A filesystem can show plenty of free space overall (`df`) while one specific directory has
|
||||
unexpectedly ballooned (`du`) — you need both questions answered to diagnose "why is disk full."
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Build the persistent-vs-temporary distinction explicitly, since it's the direct
|
||||
conceptual root of ROOT 04's bind-mount/volume distinction and, later, Kubernetes storage.
|
||||
|
||||
**Approximate timing:** 12 minutes read; 8 minutes live.
|
||||
|
||||
**Common misconception to watch for:** conflating `df` and `du` — a student surprised that
|
||||
`df` doesn't show them "which directory is huge" is a good moment to reinforce they answer
|
||||
different questions.
|
||||
|
||||
**Demo idea:** `df -h /` then `du -sh /var/log/*` side by side, narrating the different
|
||||
question each answers.
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
# Demonstration: Two Different Questions, Two Different Commands
|
||||
|
||||
## OBSERVE
|
||||
|
||||
```
|
||||
$ df -h /
|
||||
Filesystem Size Used Avail Use% Mounted on
|
||||
overlay 937G 199G 691G 23% /
|
||||
```
|
||||
|
||||
The root filesystem overall: 23% used, plenty free.
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Predict: does this tell you anything about which specific directory is using the most space?
|
||||
|
||||
## INSPECT
|
||||
|
||||
```
|
||||
$ du -sh /var/log/* 2>/dev/null
|
||||
1.2M /var/log/nginx
|
||||
850K /var/log/k3s.log
|
||||
```
|
||||
|
||||
No — `df` only ever answers "how full is this filesystem overall." Finding a SPECIFIC large
|
||||
consumer requires `du`, pointed at the directories you suspect.
|
||||
|
||||
## CHANGE
|
||||
|
||||
Narrow further into a single suspicious directory:
|
||||
|
||||
```
|
||||
$ du -sh /var/log/nginx/*
|
||||
600K /var/log/nginx/access.log
|
||||
600K /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
## VERIFY
|
||||
|
||||
You now know both the overall filesystem picture (`df`) AND exactly which files are contributing
|
||||
to it (`du`, narrowed progressively) — two genuinely different tools for two genuinely different
|
||||
questions, not interchangeable.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Make the df/du distinction concrete by actually needing BOTH to answer a
|
||||
realistic "what's using my disk" question.
|
||||
|
||||
**Approximate timing:** 8 minutes live.
|
||||
|
||||
**Common mistake to watch for:** running `du -sh /` directly (extremely slow on a large
|
||||
filesystem, since it recurses everything) instead of narrowing progressively into suspected
|
||||
directories first.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# Knowledge Check: Storage and Filesystems
|
||||
|
||||
---
|
||||
|
||||
**1. `df -h /` shows only 23% used, but you suspect one directory is unusually large. Does
|
||||
`df` alone answer that?**
|
||||
|
||||
*Answer:* No — `df` reports overall filesystem usage, not per-directory. You need `du -sh <path>`
|
||||
pointed at the suspect directory (or directories) to answer that.
|
||||
|
||||
---
|
||||
|
||||
**2. What makes data in a container's writable layer different from data in `/home`?**
|
||||
|
||||
*Answer:* By convention and design, a container's own writable layer is meant to be disposable —
|
||||
data there disappears when the container is destroyed, unless explicitly persisted elsewhere
|
||||
(a topic ROOT 04 covers directly). `/home` data is expected to persist normally.
|
||||
|
||||
---
|
||||
|
||||
**3. What does "mounting" a filesystem actually mean?**
|
||||
|
||||
*Answer:* Attaching a filesystem so it becomes accessible at a specific directory path — `/`
|
||||
itself is a mount point, and additional filesystems can be mounted at other paths.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 2 previews ROOT 04's volume/bind-mount content directly — worth
|
||||
naming that connection explicitly.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# Guided Exercise: Narrow Down a Disk Problem on Paper
|
||||
|
||||
This is a paper exercise — commit to a plan before running commands in the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
`df -h /` shows the root filesystem is 95% full. You have three suspect directories:
|
||||
`/var/log`, `/srv/cache`, and `/home`. Write the exact sequence of `du` commands you'd run to
|
||||
find the actual culprit, narrowing your search efficiently rather than checking everything with
|
||||
maximum depth immediately.
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
```
|
||||
du -sh /var/log /srv/cache /home
|
||||
```
|
||||
|
||||
— one command, comparing all three top-level suspects at once. Whichever is largest, THEN
|
||||
narrow further into it specifically (e.g. `du -sh /srv/cache/*` if that one's the culprit) rather
|
||||
than deep-diving into all three from the start — this mirrors Module 1's "cheapest, fastest-
|
||||
answering question first" principle.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Point out that running `du -sh` separately three times versus once with all
|
||||
three paths produces the same information — the point isn't syntax, it's the "compare broadly,
|
||||
then narrow" strategy.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# Reflection: "It's Probably the Logs" Is a Guess, Not a Diagnosis
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Before measuring, which of the three directories in this module's Lab did you guess would
|
||||
be largest? Were you right?
|
||||
2. Why does "persistent vs. temporary" matter enough to be worth memorizing as a question you
|
||||
ask about EVERY piece of data, not just during a storage module?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**Guessing which directory is consuming disk space, without measuring, is exactly the kind of
|
||||
assumption that wastes real incident-response time.** `du`, applied progressively (broad, then
|
||||
narrow), replaces guessing with evidence in seconds. And the persistent-vs-temporary question
|
||||
you just practiced is the exact same question you'll ask again in ROOT 04 about container data,
|
||||
and again much later about Kubernetes storage — a question worth internalizing now, cheaply,
|
||||
rather than learning the hard way after data you assumed would persist didn't.
|
||||
|
||||
This module is complete. Continue to the next module.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 1 works well as a quick poll before
|
||||
revealing the real answer — the mismatch between guesses and reality reinforces the lesson
|
||||
better than stating it directly.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
# Networking Mental Model
|
||||
|
||||
## Why this module gets extra attention
|
||||
|
||||
Nearly everything you'll do for the rest of ROOT_ — container networking, Kubernetes Services,
|
||||
Ingress, NetworkPolicy, DNS, cloud infrastructure — is a variation on the SAME small set of
|
||||
ideas this module introduces. Get this model solid now, and every later networking topic becomes
|
||||
"the same thing again, one layer up." Skip it, and every later module will feel like memorizing
|
||||
unrelated facts. The goal here is operational REASONING, not subnet arithmetic — you will not be
|
||||
asked to calculate a subnet mask by hand.
|
||||
|
||||
## The chain, layer by layer
|
||||
|
||||
Think of reaching another machine as passing through a fixed sequence of layers, each of which
|
||||
can independently succeed or fail:
|
||||
|
||||
```
|
||||
Host
|
||||
↓
|
||||
Network interface (this machine's connection to a network)
|
||||
↓
|
||||
IP address (this machine's address on that network)
|
||||
↓
|
||||
Subnet (which other addresses are "local" to reach directly)
|
||||
↓
|
||||
Default gateway (where to send anything NOT local)
|
||||
↓
|
||||
Routing (the actual path traffic takes)
|
||||
↓
|
||||
DNS (translating a name into an IP address)
|
||||
↓
|
||||
Port (which specific service on that IP)
|
||||
↓
|
||||
TCP / UDP (how the connection itself behaves)
|
||||
↓
|
||||
Application protocol (HTTP, etc. — the actual conversation)
|
||||
```
|
||||
|
||||
## Each layer, briefly
|
||||
|
||||
- **Network interface** — your machine's actual network connection (`eth0`, etc.). No interface,
|
||||
no networking, full stop.
|
||||
- **IP address** — this machine's address on a given network. IPv4 addresses look like
|
||||
`172.17.0.2`. `127.0.0.1` (aka `localhost`) is special: it always means "this same machine,"
|
||||
never anywhere else. `0.0.0.0` is different again — when a SERVICE binds to it, it means
|
||||
"listen on every interface this machine has," not a real destination address.
|
||||
- **Subnet** — a range of addresses considered "local" to each other, expressed as CIDR
|
||||
(`172.17.0.0/16` means every address starting `172.17.`). You don't need to calculate subnet
|
||||
math by hand — you need to recognize that two addresses in the same subnet can talk directly,
|
||||
while addresses outside it need a gateway.
|
||||
- **Default gateway** — the address your machine sends traffic to when the destination ISN'T in
|
||||
its own subnet — effectively "ask this router to figure out the rest."
|
||||
- **Routing** — the actual decision-making about which path traffic takes; `ip route` shows your
|
||||
machine's routing table (Module 4 makes this hands-on).
|
||||
- **DNS** — translates a human-readable name (`example.com`) into an IP address, since IP
|
||||
addresses aren't what humans want to remember or type.
|
||||
- **Port** — a single IP address can host MANY services simultaneously; the port number is what
|
||||
distinguishes "which one." Port 80 conventionally means HTTP, 443 HTTPS, 22 SSH — conventions,
|
||||
not hard rules.
|
||||
- **TCP vs. UDP** — TCP is connection-oriented and guarantees delivery/order (most web traffic,
|
||||
SSH); UDP is connectionless and faster but delivers no guarantees (DNS queries themselves,
|
||||
video streaming). Client/server: one side (the server) listens on a fixed port waiting for
|
||||
connections; the other (the client) initiates them.
|
||||
|
||||
## The reasoning skill: "where is the failure?"
|
||||
|
||||
Given this chain, a real diagnostic question decomposes into a sequence of yes/no checks, each
|
||||
isolating ONE layer:
|
||||
|
||||
- Does the host have an IP address at all?
|
||||
- Is the destination local (same subnet) or remote?
|
||||
- Is there a valid route to it?
|
||||
- Can the name resolve to an IP?
|
||||
- Can the destination PORT actually be reached?
|
||||
- Is a service actually listening there?
|
||||
- Does the application itself respond correctly?
|
||||
|
||||
Module 4 makes each of these a real command. Module 5 walks the complete request path end to
|
||||
end. Module 6 formalizes this into RootNode's troubleshooting framework, applied to networking
|
||||
specifically.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** This module gets outsized weight for a reason — it's the single highest-
|
||||
leverage conceptual investment in ROOT 02. Do not rush it to "cover the topics"; depth here pays
|
||||
off across ROOT 04, ROOT 05-10, and beyond.
|
||||
|
||||
**Approximate timing:** 25 minutes read; 15-20 minutes live with discussion — longer than a
|
||||
typical lesson, deliberately.
|
||||
|
||||
**Common misconception to watch for:** treating `0.0.0.0` as a real destination address rather
|
||||
than a "listen on every interface" bind convention — this genuinely confuses students later when
|
||||
they see it in a container's own listening-port output.
|
||||
|
||||
**Discussion question:** Walk through a real failure narrative live: "You `curl` a URL and get
|
||||
'Connection refused.' Which layers does that rule OUT, and which remain suspects?" (Answer:
|
||||
rules out DNS entirely — you got FAR enough to attempt a TCP connection — and rules out "nothing
|
||||
is listening at all" being the ENTIRE problem elsewhere; "refused" specifically means something
|
||||
actively rejected the connection, most often nothing listening on that exact port.)
|
||||
|
||||
**Optional extension:** contrast "Connection refused" vs. "Connection timed out" vs. "Could not
|
||||
resolve host" as three DIFFERENT failure signatures pointing at different layers — worth a full
|
||||
discussion pass if time allows, since Module 5 depends on students already sensing this
|
||||
distinction.
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# Demonstration: Three Failures That Sound Similar But Aren't
|
||||
|
||||
## OBSERVE
|
||||
|
||||
All three of these LOOK like "it didn't work" — but each one rules out and rules in completely
|
||||
different layers.
|
||||
|
||||
```
|
||||
$ curl http://this-name-does-not-resolve.invalid/
|
||||
curl: (6) Could not resolve host: this-name-does-not-resolve.invalid
|
||||
```
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Which layer does THIS failure point at — DNS, routing, the port, or the application itself?
|
||||
|
||||
## INSPECT
|
||||
|
||||
"Could not resolve host" happens BEFORE any network connection is even attempted — it's purely a
|
||||
DNS failure. No IP was ever found to connect to.
|
||||
|
||||
## CHANGE
|
||||
|
||||
Now a different failure, against a name that DOES resolve:
|
||||
|
||||
```
|
||||
$ curl http://localhost:9999/
|
||||
curl: (7) Failed to connect to localhost port 9999: Connection refused
|
||||
```
|
||||
|
||||
DNS/name resolution already succeeded here (`localhost` always resolves). "Connection refused"
|
||||
means a TCP packet reached the destination and was ACTIVELY rejected — almost always because
|
||||
nothing is listening on that port.
|
||||
|
||||
## VERIFY
|
||||
|
||||
A third signature, distinct from both:
|
||||
|
||||
```
|
||||
$ curl http://10.255.255.1:80/
|
||||
curl: (28) Failed to connect to 10.255.255.1 port 80: Connection timed out
|
||||
```
|
||||
|
||||
No response at all, not even a rejection — usually means the destination is unreachable
|
||||
(no route, or blocked silently), not that something is running but declining you.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Get students to viscerally distinguish these three error MESSAGES as three
|
||||
different diagnostic starting points, not interchangeable "it's broken" signals.
|
||||
|
||||
**Approximate timing:** 10 minutes live.
|
||||
|
||||
**Common mistake to watch for:** treating every connection failure as "the network is down" —
|
||||
each of these three messages should immediately narrow which layer to investigate next, per
|
||||
Module 5's troubleshooting exercise.
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# Knowledge Check: Networking Mental Model
|
||||
|
||||
---
|
||||
|
||||
**1. What's the actual difference between an IP address and a port?**
|
||||
|
||||
*Answer:* An IP address identifies a specific machine on a network; a port identifies WHICH
|
||||
service on that machine, since one IP can host many services simultaneously.
|
||||
|
||||
---
|
||||
|
||||
**2. `curl` reports "Could not resolve host." Which layer failed?**
|
||||
|
||||
*Answer:* DNS — the name never got translated into an IP address, so no connection attempt was
|
||||
even made.
|
||||
|
||||
---
|
||||
|
||||
**3. `curl` reports "Connection refused" instead. What does that specifically tell you?**
|
||||
|
||||
*Answer:* The name resolved fine and the destination was reachable, but nothing is listening on
|
||||
the port you tried — the connection attempt was actively rejected, not silently ignored.
|
||||
|
||||
---
|
||||
|
||||
**4. What is a default gateway for?**
|
||||
|
||||
*Answer:* It's where your machine sends traffic whose destination ISN'T in its own local subnet
|
||||
— effectively "ask this router to figure out how to get there."
|
||||
|
||||
---
|
||||
|
||||
**5. True or false: `0.0.0.0` is a real destination address you could `curl`.**
|
||||
|
||||
*Answer:* False (in the way most students initially assume) — when a SERVICE binds to
|
||||
`0.0.0.0`, it means "listen on every interface," a bind convention, not a real host you'd
|
||||
connect to as a destination.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Questions 2-3 are worth spending real time on together — the point isn't
|
||||
memorizing the exact error text, it's the underlying "which layer does this rule out" reasoning.
|
||||
|
||||
**Approximate timing:** 8 minutes (slightly longer than a typical knowledge check, given this
|
||||
module's weight).
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
# Guided Exercise: Diagnose From the Symptom Alone
|
||||
|
||||
This is a paper exercise — reason through each case using only the mental model, before any
|
||||
hands-on Lab work.
|
||||
|
||||
## Your task
|
||||
|
||||
For each symptom, name which layer(s) it rules OUT, and which remain genuine suspects:
|
||||
|
||||
1. `ping 8.8.8.8` succeeds, but `curl https://example.com` fails with "Could not resolve host."
|
||||
2. `dig example.com` returns a correct-looking IP address, but `curl https://example.com` times
|
||||
out with no response at all.
|
||||
3. `curl http://internal-service:8080/` returns "Connection refused," but
|
||||
`curl http://internal-service:9090/` (a different port, same host) succeeds.
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
1. Rules out: basic connectivity/routing to the internet (ping succeeded). Remaining suspect:
|
||||
DNS specifically — something is preventing name resolution even though raw IP connectivity
|
||||
works.
|
||||
2. Rules out: DNS (resolution clearly succeeded). Remaining suspects: routing/reachability to
|
||||
that specific IP, or a firewall silently dropping the connection — "no response at all" is the
|
||||
timeout signature, not the "actively rejected" signature.
|
||||
3. Rules out: DNS AND general reachability to that host (port 9090 worked). Remaining suspect:
|
||||
narrowed all the way down to "nothing is listening on port 8080 specifically" — the most
|
||||
precisely isolated diagnosis of the three.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Item 3 is the strongest example of "isolate one variable" reasoning (same
|
||||
host, different port) — worth highlighting explicitly as the gold-standard diagnostic pattern.
|
||||
|
||||
**Approximate timing:** 10-12 minutes live discussion; 8 minutes self-paced.
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# Reflection: One Model, Reused Everywhere From Here On
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Before this module, how would you have described "the network is broken" to a colleague?
|
||||
How would you describe it now, more precisely?
|
||||
2. This module deliberately avoided subnet math. Do you feel that cost you anything real, given
|
||||
the module's actual goal (reasoning about WHERE a failure is, not calculating addresses)?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**Every networking topic for the rest of ROOT_ is this same chain, viewed from a different
|
||||
altitude.** A container's published port is still "port," a Kubernetes Service is still "how do
|
||||
I reach the right destination via a stable address," Ingress is still "routing decisions," and
|
||||
NetworkPolicy is still "which subnets/ports are allowed to reach which." You are not about to
|
||||
learn a new mental model in ROOT 04 or ROOT 05 — you're about to see this SAME one applied one
|
||||
layer up, repeatedly. That's why this module got extra weight: the investment compounds.
|
||||
|
||||
This module is complete. Continue to Module 4, where these ideas become real commands.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 1 is worth collecting several answers
|
||||
before and after — the change in precision is itself the evidence this module worked.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced writing.
|
||||
|
||||
**Milestone note:** this is the "critical module" this course was built around — consider
|
||||
confirming as a group that everyone can walk the full chain (host→interface→IP→subnet→gateway→
|
||||
routing→DNS→port→TCP/UDP→application) from memory before moving on.
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
# Linux Network Inspection
|
||||
|
||||
## Every command below is really a question
|
||||
|
||||
Module 3 gave you the model. This module gives you the commands — but treat each one as
|
||||
ANSWERING a specific question, not as syntax to memorize.
|
||||
|
||||
## "What addresses does this machine have?" — `ip addr`
|
||||
|
||||
```
|
||||
$ ip -4 addr show eth0
|
||||
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
|
||||
```
|
||||
|
||||
This machine's IPv4 address is `172.17.0.2`, on a `/16` subnet.
|
||||
|
||||
## "Where will this machine send traffic?" — `ip route`
|
||||
|
||||
```
|
||||
$ ip route
|
||||
default via 172.17.0.1 dev eth0
|
||||
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2
|
||||
```
|
||||
|
||||
The second line says: anything in `172.17.0.0/16` is reachable directly. The first line (the
|
||||
`default` route) says: everything else goes through `172.17.0.1` — the default gateway from
|
||||
Module 3, now a real line in a real table.
|
||||
|
||||
## "What TCP ports are listening?" — `ss -lnt`
|
||||
|
||||
```
|
||||
$ ss -lnt
|
||||
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
|
||||
```
|
||||
|
||||
Something is listening on port 80, on every interface (`0.0.0.0` as a BIND address, exactly the
|
||||
convention Module 3 flagged). `ss` replaced the older `netstat` on modern Linux — same idea.
|
||||
|
||||
## "What does DNS say this name resolves to?" — `getent hosts` / `dig`
|
||||
|
||||
```
|
||||
$ getent hosts example.test
|
||||
10.0.0.5 example.test
|
||||
```
|
||||
|
||||
`getent hosts` checks the same resolution path the rest of the system uses (local `/etc/hosts`
|
||||
entries first, then DNS) — a reliable first choice. `dig <name>` queries DNS directly and shows
|
||||
more detail, useful once you specifically suspect a real DNS server issue rather than a local
|
||||
override.
|
||||
|
||||
## "Can the application actually respond?" — `curl`
|
||||
|
||||
```
|
||||
$ curl -sf http://localhost:80/ -o /dev/null -w '%{http_code}\n'
|
||||
200
|
||||
```
|
||||
|
||||
Everything before this point (address, route, port, DNS) can be perfect, and the APPLICATION
|
||||
itself can still be broken — `curl` is the only one of these tools that actually tests the full
|
||||
chain end to end, including the application's own response.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Convert Module 3's mental model into muscle-memory commands, each explicitly
|
||||
framed as answering one specific diagnostic question.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live.
|
||||
|
||||
**Common misconception to watch for:** treating all five commands as redundant ways to "check
|
||||
the network" — reinforce that each answers a DIFFERENT layer's question, and a real diagnosis
|
||||
usually needs more than one.
|
||||
|
||||
**Demo idea:** run all five in sequence against this Lab's own nginx, narrating which Module 3
|
||||
layer each one answers.
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# Demonstration: The Same Five Questions, Answered Live
|
||||
|
||||
## OBSERVE
|
||||
|
||||
```
|
||||
$ ip -4 addr show eth0
|
||||
inet 172.17.0.2/16 ...
|
||||
```
|
||||
|
||||
"What address do I have?" — answered.
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Predict what `ip route` will show before running it — specifically, predict whether there will
|
||||
be a `default` line.
|
||||
|
||||
## INSPECT
|
||||
|
||||
```
|
||||
$ ip route
|
||||
default via 172.17.0.1 dev eth0
|
||||
172.17.0.0/16 dev eth0 ...
|
||||
```
|
||||
|
||||
Confirmed — a `default` route exists, meaning traffic to anywhere outside `172.17.0.0/16` has
|
||||
somewhere to go.
|
||||
|
||||
## CHANGE
|
||||
|
||||
```
|
||||
$ ss -lnt
|
||||
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
|
||||
```
|
||||
|
||||
"What's listening?" — port 80, confirmed.
|
||||
|
||||
## VERIFY
|
||||
|
||||
```
|
||||
$ curl -sf http://localhost/ -o /dev/null -w '%{http_code}\n'
|
||||
200
|
||||
```
|
||||
|
||||
The full chain, verified end to end with real evidence at each layer — not "the network seems
|
||||
fine," but five specific, falsifiable answers.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Chain all five commands together as ONE continuous diagnostic narrative, not
|
||||
five disconnected syntax examples.
|
||||
|
||||
**Approximate timing:** 8 minutes live.
|
||||
|
||||
**Common mistake to watch for:** running `curl` first and stopping at "it worked" without ever
|
||||
having separately confirmed address/route/port — fine when things work, but the WRONG habit for
|
||||
when they don't (Module 5's Lab requires the full sequence).
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
# Knowledge Check: Linux Network Inspection
|
||||
|
||||
---
|
||||
|
||||
**1. Which command answers "what TCP ports are listening on this machine"?**
|
||||
|
||||
*Answer:* `ss -lnt`.
|
||||
|
||||
---
|
||||
|
||||
**2. Which command answers "where will this machine send traffic that isn't local"?**
|
||||
|
||||
*Answer:* `ip route` — specifically, the `default` line in its output.
|
||||
|
||||
---
|
||||
|
||||
**3. Why is `curl` the only tool in this module that can confirm the APPLICATION itself is
|
||||
working, not just the network path to it?**
|
||||
|
||||
*Answer:* Address, route, port, and DNS can all be perfectly correct while the application
|
||||
behind them still returns an error or wrong content — `curl` is the only one of these tools that
|
||||
actually completes an HTTP request and inspects the real response.
|
||||
|
||||
---
|
||||
|
||||
**4. `getent hosts` and `dig` both resolve names. When would you reach for `dig` specifically
|
||||
instead of `getent hosts`?**
|
||||
|
||||
*Answer:* When you specifically suspect a DNS server issue and want to query DNS directly with
|
||||
more detail — `getent hosts` checks the same path the rest of the system uses (including local
|
||||
`/etc/hosts` overrides first), which is usually the right first choice, but doesn't show you as
|
||||
much about what the actual DNS server itself said.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 3 reinforces Module 3's "layers can each independently succeed or
|
||||
fail" idea with a very concrete example.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# Guided Exercise: Which Command Answers This Question?
|
||||
|
||||
This is a paper exercise — commit to an answer before checking it in the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
For each question, name the ONE command you'd reach for first:
|
||||
|
||||
1. "Does this machine have a working IP address at all?"
|
||||
2. "Is anything listening on port 8080 right now?"
|
||||
3. "Does `payments.internal` resolve to an address?"
|
||||
4. "Is the web server actually returning a valid response, not just accepting connections?"
|
||||
5. "Where does traffic to an address outside my local subnet actually go?"
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
1. `ip -4 addr show`
|
||||
2. `ss -lnt | grep 8080`
|
||||
3. `getent hosts payments.internal`
|
||||
4. `curl -sf http://.../ -o /dev/null -w '%{http_code}'`
|
||||
5. `ip route`
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Have students justify WHY their chosen command answers the specific question
|
||||
asked, not just that it's "a networking command" — the goal is precision, not familiarity.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# Reflection: Five Commands, Five Answers, One Diagnosis
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Which of these five commands did you already know before this module? Which was
|
||||
genuinely new?
|
||||
2. You now have a real toolkit for Module 3's "where is the failure" question. What's still
|
||||
missing before you'd feel fully confident diagnosing a real production networking issue?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**Five specific commands, each answering one specific question, beat one vague instinct that
|
||||
"something's wrong with the network."** This is the exact toolkit real engineers reach for
|
||||
first, in this order, when handed an unfamiliar connectivity problem. Module 5 puts all five
|
||||
into one continuous diagnostic story; Module 6 formalizes the discipline around using them.
|
||||
|
||||
This module is complete. Continue to the next module.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 2's honest answer is usually
|
||||
"experience with real, varied failures" — a good moment to set expectations that fluency
|
||||
compounds with practice, not from one module.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
# DNS and HTTP
|
||||
|
||||
## The complete request path
|
||||
|
||||
Every time a browser or `curl` fetches a URL, it walks the entire Module 3 chain in order:
|
||||
|
||||
```
|
||||
Application (curl / browser)
|
||||
↓
|
||||
DNS lookup — translate the name to an IP
|
||||
↓
|
||||
IP address obtained
|
||||
↓
|
||||
Routing — figure out the path to that IP
|
||||
↓
|
||||
TCP connection established — a three-way handshake, underneath curl
|
||||
↓
|
||||
HTTP request sent
|
||||
↓
|
||||
Server processes it
|
||||
↓
|
||||
HTTP response returned
|
||||
```
|
||||
|
||||
Nothing here is new — this is Module 3's chain and Module 4's commands, just walked start to
|
||||
finish as ONE continuous story instead of five separate checks.
|
||||
|
||||
## Four distinct failure categories, not one
|
||||
|
||||
Given this path, "it doesn't work" always decomposes into exactly one of four categories:
|
||||
|
||||
1. **DNS failure** — the name never resolved to an IP at all (`curl`'s "Could not resolve
|
||||
host").
|
||||
2. **Routing/connection failure** — the IP was found, but no TCP connection could be established
|
||||
— either refused (something's there but nothing's listening) or timed out (no response at
|
||||
all, usually unreachable or silently blocked).
|
||||
3. **TCP succeeded, but the application never responds correctly** — a connection was
|
||||
established, but the server hangs, drops the connection, or the request never completes.
|
||||
4. **HTTP/application failure** — a full HTTP response comes back, but it's WRONG (a 500 error,
|
||||
a 404, unexpected content) — everything below the application layer worked fine.
|
||||
|
||||
These are genuinely different problems requiring genuinely different fixes — conflating them
|
||||
(treating a 404 the same as a DNS failure) wastes real diagnostic time.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Make the four-category failure taxonomy explicit and distinct — this is the
|
||||
direct foundation for this module's diagnostic Lab and for Module 6's formal framework.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live.
|
||||
|
||||
**Common misconception to watch for:** treating an HTTP 500 error the same category as "the
|
||||
network is down" — a 500 means the ENTIRE network path worked; the failure is purely in the
|
||||
application's own logic.
|
||||
|
||||
**Discussion question:** "A request returns HTTP 404. Which of the four categories is that, and
|
||||
what does it rule out?" (Answer: category 4, application/HTTP — it rules out DNS, routing, AND
|
||||
TCP connectivity entirely, since a real HTTP response came back at all.)
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# Demonstration: Walking the Complete Path, Layer by Layer
|
||||
|
||||
## OBSERVE
|
||||
|
||||
```
|
||||
$ getent hosts lab-internal.test
|
||||
10.99.99.42 lab-internal.test
|
||||
```
|
||||
|
||||
DNS: succeeded.
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Predict: if the next step (TCP connection) fails, what would that specific error look like?
|
||||
|
||||
## INSPECT
|
||||
|
||||
```
|
||||
$ curl http://lab-internal.test:9091/
|
||||
curl: (7) Failed to connect to lab-internal.test port 9091: Connection refused
|
||||
```
|
||||
|
||||
"Connection refused" — DNS already succeeded (ruled out), the failure is specifically that
|
||||
nothing is listening on port 9091 at that address.
|
||||
|
||||
## CHANGE
|
||||
|
||||
Find what IS actually listening, on the correct address:
|
||||
|
||||
```
|
||||
$ ss -lnt | grep LISTEN
|
||||
LISTEN 0 5 0.0.0.0:9095 0.0.0.0:*
|
||||
```
|
||||
|
||||
The real service is on 9095, not 9091 — a configuration/expectation mismatch, not a DNS or
|
||||
routing problem.
|
||||
|
||||
## VERIFY
|
||||
|
||||
```
|
||||
$ curl http://lab-internal.test:9095/
|
||||
app running
|
||||
```
|
||||
|
||||
The full path, once pointed at the CORRECT layer that was actually broken (the port, not DNS),
|
||||
succeeds end to end.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Show that a real, plausible-sounding complaint ("DNS must be broken") is
|
||||
disproven by evidence in seconds, and the REAL cause is one layer downstream.
|
||||
|
||||
**Approximate timing:** 10 minutes live.
|
||||
|
||||
**Common mistake to watch for:** jumping straight to "restart everything" instead of walking
|
||||
the chain layer by layer to find the SPECIFIC broken step — the whole point of this module's
|
||||
Lab is resisting that shortcut.
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# Knowledge Check: DNS and HTTP
|
||||
|
||||
---
|
||||
|
||||
**1. Name the four failure categories this module introduced.**
|
||||
|
||||
*Answer:* DNS failure, routing/connection failure, TCP-succeeds-but-application-never-responds,
|
||||
and HTTP/application failure (a full response comes back but it's wrong).
|
||||
|
||||
---
|
||||
|
||||
**2. A request returns HTTP 500. What does that tell you about DNS and TCP connectivity?**
|
||||
|
||||
*Answer:* Both worked completely — a 500 means a full HTTP response was received, so the failure
|
||||
is purely inside the application's own logic, not the network path to reach it.
|
||||
|
||||
---
|
||||
|
||||
**3. Why is "connection refused" a MORE specific diagnosis than "timed out"?**
|
||||
|
||||
*Answer:* "Refused" means a packet reached the destination and was actively rejected (almost
|
||||
always: nothing listening on that port) — you know the destination is reachable. "Timed out"
|
||||
means no response at all, which could mean unreachable, no route, or silently blocked —
|
||||
genuinely less specific.
|
||||
|
||||
---
|
||||
|
||||
**4. A teammate says "DNS must be broken" because a request fails. What should you check
|
||||
BEFORE agreeing?**
|
||||
|
||||
*Answer:* Whether the name actually resolves at all (`getent hosts` or `dig`) — many failures
|
||||
that "feel like DNS" are actually one layer downstream (routing, port, or application), and
|
||||
DNS itself may be working perfectly.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 4 mirrors this module's own Lab almost exactly — a good moment to
|
||||
preview it if not already started.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
# Guided Exercise: Categorize the Failure Before You Fix It
|
||||
|
||||
This is a paper exercise — commit to a category before checking it in the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
For each symptom, name which of the four failure categories (DNS / routing-connection /
|
||||
TCP-hangs / HTTP-application) it belongs to:
|
||||
|
||||
1. `curl: (6) Could not resolve host: api.internal`
|
||||
2. `curl: (7) Failed to connect to api.internal port 443: Connection refused`
|
||||
3. A request eventually returns, but with HTTP 502 Bad Gateway
|
||||
4. A request just hangs for 30 seconds with no response, then finally times out
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
1. DNS — the name never resolved.
|
||||
2. Routing/connection — DNS worked, but nothing accepted the connection on that port.
|
||||
3. HTTP/application — a full HTTP response came back (502 is a real, complete response), so
|
||||
DNS/routing/TCP all worked; the failure is in the application layer (often a proxy unable to
|
||||
reach ITS OWN backend, which is itself worth investigating further).
|
||||
4. TCP-hangs / ambiguous-timeout — this is the least specific signature; it could mean
|
||||
unreachable, silently dropped by a firewall, or an application that accepted the connection
|
||||
but never responds — worth narrowing further with `ss`/`ip route` before concluding.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Item 3 (502) is a good one to slow down on — many students initially assume
|
||||
any 5xx code means "the network is broken," when it actually proves the OPPOSITE for every
|
||||
layer below the application.
|
||||
|
||||
**Approximate timing:** 10-12 minutes live discussion; 8 minutes self-paced.
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# Reflection: You Fixed the Real Problem, Not the Suspected One
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Before running any commands in this module's Lab, what would you have guessed was broken?
|
||||
Was your guess right?
|
||||
2. Why does correctly categorizing a failure (DNS vs. routing vs. TCP vs. application) matter
|
||||
more than just "trying things until something works"?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**"DNS must be broken" is one of the most common wrong first guesses in real incident response
|
||||
— and disproving or confirming it takes seconds, not minutes.** The habit this module builds —
|
||||
categorize the failure precisely before attempting a fix — is what separates a five-minute
|
||||
diagnosis from an hour of trying unrelated things. This exact reasoning, applied to Kubernetes
|
||||
Services/Ingress/DNS later, is one of the most valuable transferable skills in this entire
|
||||
program.
|
||||
|
||||
This module is complete. Continue to the final module of this course.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 1 works well as a live poll before
|
||||
revealing the Lab's actual root cause — the gap between instinct and evidence is the lesson.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
# Linux Troubleshooting
|
||||
|
||||
## The RootNode troubleshooting framework
|
||||
|
||||
Every module in ROOT 02 has been building toward this: a repeatable, disciplined process for
|
||||
diagnosing ANY Linux-level failure, not just the specific ones you've already seen.
|
||||
|
||||
```
|
||||
OBSERVE
|
||||
↓
|
||||
GATHER EVIDENCE
|
||||
↓
|
||||
FORM HYPOTHESIS
|
||||
↓
|
||||
TEST
|
||||
↓
|
||||
REPAIR
|
||||
↓
|
||||
VALIDATE
|
||||
↓
|
||||
PREVENT
|
||||
```
|
||||
|
||||
## Each step, and why skipping it costs you later
|
||||
|
||||
- **Observe** — what's the actual, specific symptom? Not "it's broken" — the exact error, the
|
||||
exact command that failed.
|
||||
- **Gather evidence** — logs, process state, listening ports, config content — the raw facts,
|
||||
collected BEFORE guessing at a cause.
|
||||
- **Form hypothesis** — based on the evidence (not a guess made before looking), what's the most
|
||||
likely cause?
|
||||
- **Test** — check whether your hypothesis is actually true, ideally without yet making changes
|
||||
that could mask the real problem.
|
||||
- **Repair** — make the minimal change that addresses the CONFIRMED cause, not a broad "try
|
||||
everything" fix.
|
||||
- **Validate** — confirm the original symptom is actually gone, with real evidence, not
|
||||
assumption.
|
||||
- **Prevent recurrence** — what would stop this from happening again? Skipping this step is how
|
||||
teams end up fixing the exact same incident repeatedly.
|
||||
|
||||
## Why "skip straight to fixing" costs you
|
||||
|
||||
Jumping straight from OBSERVE to REPAIR (skipping evidence and hypothesis) means you're guessing
|
||||
— sometimes you'll get lucky, but you won't have LEARNED anything transferable, and you risk
|
||||
"fixing" a symptom while leaving the actual cause untouched, guaranteeing a repeat. Every command
|
||||
from Modules 1, 2, and 4 IS the "gather evidence" step, made concrete.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Consolidate every command this course taught into ONE disciplined process,
|
||||
directly matching the identical framework `root-10-m03-troubleshooting-framework` teaches at the
|
||||
Kubernetes level — introduced here first, at the Linux level, so it already feels familiar by
|
||||
then.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live.
|
||||
|
||||
**Common misconception to watch for:** treating this as a rigid checklist to recite rather than
|
||||
an actual habit — the value only shows up when it's applied under a real, unfamiliar failure
|
||||
(this module's Lab).
|
||||
|
||||
**Discussion question:** "Describe a time you fixed something without understanding WHY it
|
||||
broke. Did it come back?" (Real personal examples land this lesson better than abstract
|
||||
description.)
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
# Demonstration: Every Step, In Order, On a Real Failure
|
||||
|
||||
## OBSERVE
|
||||
|
||||
A startup script fails: `/srv/demo/start.sh` exits with an error. That's the specific, concrete
|
||||
symptom — not "the service is down."
|
||||
|
||||
## GATHER EVIDENCE
|
||||
|
||||
```
|
||||
$ /srv/demo/start.sh
|
||||
FATAL: config file not found: /srv/demo/config/settings.conf
|
||||
```
|
||||
|
||||
The script's own error message IS the evidence — no guessing required.
|
||||
|
||||
## FORM HYPOTHESIS
|
||||
|
||||
The config file at that exact path doesn't exist. That's the specific, testable hypothesis —
|
||||
not "something's misconfigured."
|
||||
|
||||
## TEST
|
||||
|
||||
```
|
||||
$ ls /srv/demo/config/
|
||||
ls: cannot access '/srv/demo/config/': No such file or directory
|
||||
```
|
||||
|
||||
Confirmed — the directory itself doesn't exist yet, not just the file.
|
||||
|
||||
## REPAIR
|
||||
|
||||
```
|
||||
$ mkdir -p /srv/demo/config
|
||||
$ echo "mode=production" > /srv/demo/config/settings.conf
|
||||
```
|
||||
|
||||
The minimal fix for the CONFIRMED cause — not a broader "reinstall everything" overcorrection.
|
||||
|
||||
## VALIDATE
|
||||
|
||||
```
|
||||
$ /srv/demo/start.sh
|
||||
Started with config: mode=production
|
||||
```
|
||||
|
||||
The original symptom is gone, confirmed with real output, not assumption.
|
||||
|
||||
## PREVENT RECURRENCE
|
||||
|
||||
A real prevention step here: the script should probably check for and create its config
|
||||
directory on first run, or the deployment process should ensure the config exists before the
|
||||
service ever starts — naming this explicitly is what separates a real fix from a lucky one.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Show all seven steps applied in strict order to one concrete, realistic failure
|
||||
— the exact structure this module's Lab requires students to repeat themselves.
|
||||
|
||||
**Approximate timing:** 10 minutes live.
|
||||
|
||||
**Common mistake to watch for:** skipping straight to REPAIR (creating the file) without first
|
||||
articulating the hypothesis and testing it — even when the fix seems obvious, naming the
|
||||
hypothesis explicitly is the habit this module is building.
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# Knowledge Check: Linux Troubleshooting
|
||||
|
||||
---
|
||||
|
||||
**1. What's the difference between OBSERVE and GATHER EVIDENCE?**
|
||||
|
||||
*Answer:* OBSERVE names the specific symptom (what actually failed, precisely). GATHER EVIDENCE
|
||||
collects the raw facts (logs, config, process/port state) needed to understand WHY, before
|
||||
guessing at a cause.
|
||||
|
||||
---
|
||||
|
||||
**2. Why does FORM HYPOTHESIS come before TEST, rather than jumping straight to REPAIR?**
|
||||
|
||||
*Answer:* A hypothesis is a specific, falsifiable guess at the cause based on evidence already
|
||||
gathered — testing it BEFORE repairing confirms you're about to fix the right thing, rather than
|
||||
making a change that might not address the actual cause at all.
|
||||
|
||||
---
|
||||
|
||||
**3. A fix works, but the same failure happens again a week later. Which framework step was
|
||||
most likely skipped?**
|
||||
|
||||
*Answer:* PREVENT RECURRENCE — the immediate symptom was resolved, but whatever allowed the
|
||||
underlying cause to occur in the first place was never addressed.
|
||||
|
||||
---
|
||||
|
||||
**4. Why is "repair the minimal confirmed cause" better than "try several things until it
|
||||
works"?**
|
||||
|
||||
*Answer:* A minimal, confirmed repair means you actually know what fixed it and can prevent
|
||||
recurrence deliberately. Trying multiple things at once means even success leaves you unable to
|
||||
say WHICH change mattered — and unable to reliably prevent the same failure next time.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 3 is a good real-world check-in — ask if anyone has a personal
|
||||
example of a fix that didn't actually stick.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# Guided Exercise: Write Your Hypothesis Before You Look at the Answer
|
||||
|
||||
This is a paper exercise — commit to a full framework pass before touching the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
You're told: "A script that's supposed to write a `ready` marker file isn't creating it, and
|
||||
exits with a non-zero code." Before running anything, write out, specifically:
|
||||
|
||||
1. What's your OBSERVE statement (the precise symptom, not "it's broken")?
|
||||
2. What EVIDENCE would you gather first, and with what command?
|
||||
3. What's ONE specific, testable hypothesis (not "something's wrong with it")?
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
1. Observe: "Script X exits non-zero and does not create file Y."
|
||||
2. Evidence: redirect both stdout and stderr to a file and read it —
|
||||
`script.sh > log 2>&1; cat log` — the script's own error output is almost always the fastest
|
||||
evidence source for a startup failure.
|
||||
3. Hypothesis (once you've actually read the error): something SPECIFIC the error names —
|
||||
e.g. "the script requires a file at path X that doesn't currently exist" — not a vague
|
||||
restatement of the symptom.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** The value here is entirely in getting students to write a hypothesis
|
||||
SPECIFIC and FALSIFIABLE enough that they could be proven wrong — "something's misconfigured"
|
||||
fails that bar; "the config file at this exact path is missing" passes it.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# Reflection: This Is the Same Framework You'll Use for the Rest of ROOT_
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Which framework step did you personally find easiest to skip or rush through in this
|
||||
module's Lab? Why do you think that step specifically tempts shortcuts?
|
||||
2. You've now used this exact seven-step framework — Observe, Gather Evidence, Form Hypothesis,
|
||||
Test, Repair, Validate, Prevent Recurrence — at the Linux level. Where do you expect to use
|
||||
it again?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**This entire course, from Module 1's five-question service checklist through Module 5's
|
||||
failure-category taxonomy, was building toward this one repeatable discipline.** You'll meet
|
||||
this exact framework again, formally named, in ROOT 10 — applied to Kubernetes instead of raw
|
||||
Linux. Having already practiced it here means ROOT 10 won't be teaching you a new skill, just
|
||||
retargeting a skill you already have.
|
||||
|
||||
This module — and ROOT 02 — is complete. Continue to ROOT 03: Git & Collaborative Development.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 1 tends to surface "Prevent Recurrence"
|
||||
and "Form Hypothesis" (jumping straight to a guessed fix) as the two most commonly admitted
|
||||
shortcuts — worth normalizing as a real, common temptation rather than a personal failing.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced writing.
|
||||
|
||||
**Milestone note:** this is the last module of ROOT 02 — consider a short recap connecting all
|
||||
six modules (services → storage → networking model → inspection tools → DNS/HTTP → framework)
|
||||
before moving to ROOT 03.
|
||||
Reference in New Issue
Block a user