Kubernetes From Scratch
Build a complete Kubernetes cluster by hand — no kubeadm, no scripts — from the first certificate to a real HA cluster, then use that cluster as a lab to deep-dive every Kubernetes concept. Part one: PKI/TLS, etcd, the control plane, workers, pod networking, CoreDNS. Part two: Pods, workload controllers, scheduling, storage, advanced networking (Cilium eBPF), security, extending the API, operations. Each component is both explained from the inside and stood up/configured by hand. Tested for real on AWS EC2 with Kubernetes v1.36; manifests/scripts at github.com/nghiadaulau/kubernetes-from-scratch. Grounded in the official docs at kubernetes.io.
kube-proxy: Turning a ClusterIP Into a Real Destination
A Kubernetes Service is a virtual IP — it isn't bound to any machine. kube-proxy is the component that turns that virtual IP into forwarding rules on each node, so traffic to a ClusterIP lands on a real pod behind it. This article installs kube-proxy in iptables mode on two workers, dissects the NAT chains it generates, then curls a ClusterIP directly to watch it work — all without pod networking yet.
The Kubernetes Network Model
Before wiring up pod networking in the next article, you need to understand what kind of network model Kubernetes demands: one IP per pod, every pod talking directly with no NAT. This article walks through the model's four foundational requirements, the four kinds of in-cluster communication, why pod-to-pod across nodes is the hard part, the two families of solutions (overlay and native routing), and where CNI fits — laying the groundwork for the hands-on wiring next.
Wiring Up Pod Networking by Hand: CNI bridge and VPC Routes
The previous theory article laid the groundwork; this one does the assembly. We write the CNI bridge + host-local config for each worker, add routes in the VPC route table so pod-to-pod across nodes works, then watch the two nodes finally flip to Ready. At the end we create two real pods on two different nodes and ping between them — each pod gets an IP from its node's range, and packets cross nodes without NAT.
CoreDNS: Calling Each Other by Name in the Cluster
Pods can already talk by IP, but a pod's IP changes every time it's reborn, so no one hard-codes them. CoreDNS fills that gap: it runs as a workload right in the cluster, sits behind a Service at exactly the 10.32.0.10 that kubelet has been pointing pods at, and resolves Service names into ClusterIPs. This article deploys CoreDNS by hand — RBAC, ConfigMap, Deployment, Service — then tests a pod resolving both internal and external names.
Smoke Test: The Whole Cluster Running Together
Every component is present; this article tests that they work together. We deploy a real application via a Deployment, expose it with a Service, call it by name and watch traffic spread evenly across replicas, use logs/exec/port-forward, then delete a pod to see the cluster self-heal. Each test shines a light on one piece we built throughout the series.
The Lifecycle of a Request: From kubectl apply to a Running Pod
The previous smoke test showed the cluster runs; this article traces a single apply command through each component we built, in chronological order, to see how they hand off to one another. More important than the sequence of steps is the model behind it: there is no conductor giving orders, just many independent loops all looking at one source of truth and pulling reality toward the desired state.
The Lifecycle of a Pod: Phase, Condition and restartPolicy
Opens the deep-dive Pods section with something you read every day in kubectl get pods but rarely read closely: a pod's status. This article separates the three layers of status — the coarse phase, the detailed container state, and conditions as a checklist — then shows that phase is actually derived from container state plus restartPolicy. Four real pods illustrate Running, Succeeded, Failed and CrashLoopBackOff.
Init Containers and Sidecar Containers
A pod isn't just its main container. An init container runs preparation work to completion before handing off to the app; a sidecar container runs alongside the app for the lifetime of the pod. This article distinguishes the two, digs into the precise semantics from the docs — startup order, error handling, shutdown order — and verifies it with real pods on a v1.36 cluster.
Probes: liveness, readiness and startup
Article 18 left the Ready condition unexplained. Behind it sits the probe — how the kubelet asks a container three different questions: are you alive, are you ready for traffic, have you finished starting. This article separates the three kinds of probe per the docs, then verifies each with real pods: liveness kills and restarts, readiness removes the pod from a Service's endpoints, startup disables the other two until the app has time to start.
Ephemeral containers and kubectl debug
Good production containers often have no shell — the leaner a distroless image, the fewer debugging tools, so kubectl exec is stuck. This article uses ephemeral containers: slip a tooling container temporarily into a running pod without restarting it or modifying its image. It digs into the semantics per the docs, then verifies all three modes of kubectl debug — attach to a running pod, copy the pod, and debug a node directly — on a real cluster.
Requests, limits, QoS and the Downward API
Declaring requests and limits for a container isn't just about picking numbers. requests guide the scheduler, limits are kernel-enforced fences — CPU gets throttled, exceeding memory is an OOM kill. From those numbers Kubernetes sorts pods into three QoS classes that decide who gets killed first when the node runs out of RAM. This article tests all three QoS classes for real, an OOMKilled, and the Downward API for a pod to read information about itself.
Disruptions and the PodDisruptionBudget
Pods vanish in two very different ways: involuntary (the node dies, runs out of RAM — no one can stop it) and voluntary (draining a node for maintenance, upgrade — deliberate). A PodDisruptionBudget only guards the second kind: it tells the cluster not to take down too many replicas at once. This article distinguishes the two kinds of disruption then verifies a PDB with the real Eviction API — seeing an evict blocked with HTTP 429 firsthand.