Kubernetes homelab — beginner's guide
All concepts below appeared in your homelab conversation. Read them in order — each one builds on the previous.
1. What is Kubernetes?
Kubernetes (also called k8s) is a system that manages containerised applications across a group of computers. Think of it as an orchestra conductor — it decides where to run each part of your app, restarts things that crash, and keeps everything coordinated.
Analogy: If Docker is like shipping containers, Kubernetes is the port that decides which ship carries which container and where it goes.
In your homelab, you have a small cluster: one control-plane machine (k8s-cp-01) and two worker machines (k8s-worker-01, k8s-worker-02). That's a real, functional Kubernetes setup.
2. Nodes — the machines in your cluster
A node is just a computer (physical or virtual) that Kubernetes manages. There are two kinds:
| Type | Your machine | Role |
|---|---|---|
| Control plane | k8s-cp-01 |
The brain — manages the cluster, schedules work, stores config |
| Worker | k8s-worker-01 |
The muscle — actually runs your app containers |
| Worker | k8s-worker-02 |
Second worker — adds capacity and redundancy |
In your terminal output, both showed NotReady. This means the nodes are online but can't yet communicate with each other — they're waiting for a CNI (covered next).
Once Flannel is installed, they'll flip to Ready.
3. Pods — the smallest unit of work
A pod is the smallest thing Kubernetes can run. It wraps one (or sometimes a few) containers and gives them a shared network and storage.
Analogy: If a container is a single app process, a pod is the little apartment it lives in — with its own address and shared utilities.
This command from your conversation creates a bare pod running nginx:
--image=nginxtells k8s to pull the nginx container image.--restart=Nevermeans "just run it once, don't keep restarting" — useful for quick tests.
4. CNI & Flannel — the cluster's network layer
A CNI (Container Network Interface) is a plugin that gives every pod its own IP address and lets pods talk to each other across nodes. Without one, your nodes stay NotReady and nothing can be scheduled.
Flannel is one of the simplest CNI plugins. It creates a virtual network overlay so every pod gets an IP — even when they're on different physical machines. It's a great choice for homelabs because it's lightweight (important with your 8 GB RAM constraint).
You install it with one command:
The kubectl apply -f pattern means "apply this configuration file to my cluster." You'll use it constantly in Kubernetes.
The CRI-to-CNI Handoff
While CNI stands for Container Network Interface, it is not merely a theoretical API — it consists of physical, executable binary files (like flannel, bridge, portmap).
When a pod is scheduled to a node, the Container Runtime Interface (CRI) (like containerd) creates the initial Linux network namespace. However, the runtime does not know how to assign IPs or route traffic. Instead, the runtime executes the CNI binary on the host OS and hands off the networking responsibilities.
Because of this direct execution model, the CRI is hardcoded to look for these CNI binaries in a specific directory (usually /opt/cni/bin/). If the OS package manager compiled the CRI to look in /usr/lib/cni/ instead, the handoff fails, and the pod will be permanently stuck in ContainerCreating.
5. Pod CIDR — giving pods their IP addresses
A CIDR (Classless Inter-Domain Routing) block is a range of IP addresses written in compact notation. 10.244.0.0/16 means "all addresses from 10.244.0.0 to 10.244.255.255" — that's 65,536 possible IPs.
Kubernetes assigns each pod a unique IP from this range so they can communicate. 10.244.0.0/16 is Flannel's default range — which is exactly why it works out of the box with your cluster config.
These are private IPs (invisible to the internet), used only internally inside your cluster.
6. kubectl — your command line for Kubernetes
kubectl (pronounced "kube control" or "kube cuttle") is the CLI tool you use to interact with your cluster. Every command in the conversation used it.
Almost every kubectl command follows this pattern:
Essential commands
# List all nodes and their status
kubectl get nodes
# Create or update resources from a YAML config file
kubectl apply -f file.yml
# Quickly launch a pod from a container image
kubectl run test-nginx --image=nginx --restart=Never
# List all pods and their current state
kubectl get pods
# Get detailed info about a specific pod
kubectl describe pod test-nginx
# Stream logs from a pod
kubectl logs test-nginx
Node maintenance commands
# Mark a node as unschedulable (no new pods will be placed on it)
kubectl cordon k8s-worker-01
# Evict all pods from a node (also cordons it automatically)
kubectl drain k8s-worker-01 --ignore-daemonsets --delete-emptydir-data
# Mark a node as schedulable again (allow new pods after maintenance)
kubectl uncordon k8s-worker-01 k8s-worker-02
- cordon = "put up the tape" — the node stays running, but the scheduler won't place new pods on it.
- drain = cordon + evict all existing pods. Used before maintenance or shutdown.
- uncordon = "take the tape down" — the node is open for business again. You can uncordon multiple nodes at once.
Quick reference — concepts at a glance
| Term | What it is | Your example |
|---|---|---|
k8s |
Short for Kubernetes | Your whole homelab cluster |
| Node | A machine in the cluster | k8s-cp-01, k8s-worker-01, k8s-worker-02 |
| Control plane | The node that manages everything | k8s-cp-01 |
| Worker | The node that runs workloads | k8s-worker-01, k8s-worker-02 |
| Pod | Smallest runnable unit (wraps containers) | test-nginx |
| CNI | Network plugin — gives pods IPs | Flannel |
| CIDR | A range of IP addresses | 10.244.0.0/16 |
kubectl |
CLI tool to control the cluster | All commands above |
NotReady |
Node online but no CNI yet | Your initial state |
Ready |
Node fully operational | After Flannel installs |
| Cordon | Mark a node as unschedulable | kubectl cordon k8s-worker-01 |
| Uncordon | Mark a node as schedulable again | kubectl uncordon k8s-worker-01 |
| Drain | Evict all pods from a node (+ cordon) | kubectl drain k8s-worker-01 |