What Kubernetes Is
Why you need a container orchestrator. Starting from a reader who has used Docker / docker-compose, this chapter lays out the five limits of single-container tooling, the declarative desired state + reconcile loop model, the big picture of control plane / worker node, and the scope of the book.
This is the opening chapter of the book. From Chapter 2 on we get into commands you can actually run. Before writing any code, this chapter lays out why Kubernetes matters, how this book differs from other introductions, and the path from Chapter 1 through Chapter 31.
Everything that follows — Part 4 EKS in Production, Part 5 Operations · Debugging · Cost, Part 6 the fullstack capstone — also sits inside this chapter’s final section, “What this book covers.” So this chapter is a guide that goes beyond a simple “Part 1, Chapter 1.” Take it slowly and follow along.
For the reader who has come as far as Docker #
This book starts from a reader who is comfortable with Docker / docker-compose. If the flow of building a single container, running it, and persisting its data is familiar to you, this chapter starts naturally from the next question.
I can run one container. So how do I run a hundred containers — automatically reviving them when they die, and scaling them up and down with traffic?
The de facto standard answer to that question is Kubernetes (K8s). This chapter lays out why the question is natural, and what model K8s uses to answer it.
If you’ve come in knowing only Docker, after finishing this chapter it helps to open Appendix A, From docker-compose to k8s first. Appendix A organizes, as a mapping table, which K8s resource each key of docker-compose.yml corresponds to. You’ll find yourself coming back to it often as the body proceeds.
The moment docker run alone falls short
#
The single-container operations command at the end of the Docker stage looks like this.
docker run -d --name myapp \
--restart unless-stopped \
-p 127.0.0.1:8000:8000 \
-v myapp-data:/app/data \
ghcr.io/curtis/myapp:1.0.0One container on one host, or bundling web + db + cache on the same host with Compose — this is enough up to that point. But once traffic grows, zero-downtime deploys are required, and the service must stay alive even when one server dies, you hit problems the same tool can’t solve.
1) When one node dies, the service dies with it. A container started with docker run is bound to that host. If the host reboots or its disk fails, there is no standard way to move the container to another machine. A person has to SSH into a new machine and retype the same command.
2) Automatic recovery when a container dies is weak. --restart unless-stopped only works within the same host. If the host itself is alive, the docker daemon restarts the container, but if the host dies, that’s the end of it. And it can’t catch partial failures like “the process is alive but can’t respond.”
3) Adjusting the instance count to match traffic is all manual. To grow an API server that normally runs fine on 2 instances to 10 during a campaign, someone has to provision new machines, pull the same image and run the same command 10 times, then change the front load balancer config by hand. When it’s over, scale back down. A lot of manual work is required.
4) You have to write zero-downtime deploys (rolling updates) yourself. Moving from v1.0.0 to v1.1.0 — replacing one of the 10 instances at a time with the new version while routing traffic only to containers that pass a health check — is awkward to script by hand. Add automatic rollback on failure and it gets more complex.
5) There’s no standard for config, secret values, and service discovery. You need to pass DB connection details as environment variables, but docker’s basic tooling has no standard way to inject different values per environment (staging / prod) safely. You also have to decide yourself by what name container A finds container B (service discovery).
These five are the limits of a per-host, per-container tool. And resolving these limits is the job of a container orchestrator.
What a container orchestrator solves #
Abstracting the five items above into one line gives this.
Bundle several machines into one block (a cluster), declare only “what shape it should run in,” and the system maintains that shape on its own.
The names for this are declarative desired state and the reconcile loop.
- Declarative — you write only “what it should be (what)”, not “how to make it (how).” Example: “3 myapp containers must always be up.”
- Reconcile loop — the system continuously compares the actual state with the desired state, and when there’s a difference, it moves to shrink that difference. If one container dies and you’re down to 2, the system brings up one more on its own to get back to 3.
┌──────────────────┐ ┌──────────────────┐
│ desired state │ │ actual state │
│ "myapp 3 replicas"│ ◀───▶ │ 2 currently up │
└──────────────────┘ compare └──────────────────┘
│ │
└────────┬──────────────┘
▼
┌─────────────┐
│ controller │ → diff → "bring up one more"
└─────────────┘This model is the core idea of K8s. A person declares only “it should be like this,” and keeping that declaration is the system’s job. A node dies, you roll out a new version gradually, you grow 5 instances to 8 with traffic — all of it is solved inside the same model.
In this book we see directly how the reconcile loop works on real resources in Chapter 4 Deployment and ReplicaSet, and we cover how to extend the same model to custom resources in Chapter 18 the CRD and Operator pattern.
Where Kubernetes came from #
K8s didn’t invent this model. Its root is Borg, a cluster management system Google ran internally for over 10 years. Google services like Search, Gmail, and Maps ran on Borg, and out of that experience came the next-generation system, Omega. Reimplementing that idea as open source later produced Kubernetes.
The rough timeline is as follows.
- June 2014 — Google open-sourced an early version of Kubernetes.
- July 2015 — Kubernetes 1.0 was released, and at the same time the CNCF (Cloud Native Computing Foundation) was launched, with K8s donated as its first project.
- After that — all the major cloud providers (AWS EKS, GCP GKE, Azure AKS) offered managed K8s, making it the de facto standard for container orchestration.
“Kubernetes” is Greek for helmsman. The abbreviation K8s shortens the 8 letters between K and s. The two notations are often used interchangeably and refer to the same thing.
This book takes Kubernetes 1.32 as its standard, as of writing. Whenever certain deprecated APIs or newly GA features come up, the relevant chapter calls them out explicitly.
The big picture of a K8s cluster #
A K8s cluster is made up of two kinds of nodes — the control plane and worker nodes.
┌───────────────────────────────────┐
│ Control Plane │
│ │
kubectl ───▶ │ kube-apiserver ◀──────┐ │
│ │ │ │
│ ▼ │ │
│ etcd │ │
│ │ │
│ kube-scheduler ───────┤ │
│ kube-controller-manager ─┤ │
│ cloud-controller-manager ┘ │
└────────────┬───────────────────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ Worker Node 1 │ │ Worker Node 2 │ │ Worker Node 3 │
│ │ │ │ │ │
│ kubelet │ │ kubelet │ │ kubelet │
│ kube-proxy │ │ kube-proxy │ │ kube-proxy │
│ containerd │ │ containerd │ │ containerd │
│ │ │ │ │ │
│ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │
│ │ Pod │ ... │ │ │ Pod │ ... │ │ │ Pod │ ... │
│ └──────┘ │ │ └──────┘ │ │ └──────┘ │
└────────────────┘ └────────────────┘ └────────────────┘Control plane — the brain of the cluster #
The control plane is the part that receives and stores the cluster’s desired state and makes decisions so that state is actually maintained.
- kube-apiserver — the cluster’s front door. Every tool, including
kubectl, sends REST requests to this API server. After authentication, authorization, and validation, it records the result in etcd. - etcd — a distributed key-value store that holds all cluster state (which Pod should be where, which Service is defined, and so on). It is the cluster’s single source of truth.
- kube-scheduler — decides “which worker node it goes to” for a newly created Pod. It decides by looking at a node’s available resources, labels, and constraints.
- kube-controller-manager — bundles many controllers into one process and runs them. It’s the actual agent that performs the reconcile loop we saw above. Example: the ReplicaSet controller continuously compares the declaration “3 Pods must be up” with the actual count.
- cloud-controller-manager — on a cluster running atop a cloud (AWS / GCP / Azure, etc.), it connects that cloud’s resources — load balancers, disks, nodes — to K8s resources. A cluster running in your own data center may not have it.
Worker node — where the work happens #
A worker node is the machine where containers actually run. Typically a node has the following three things installed.
- kubelet — the node’s agent. It receives the instruction “run this Pod on this node” from the apiserver and asks the container runtime to actually run it. It also watches the containers’ health and reports the state back to the apiserver.
- kube-proxy — manages the node’s network rules (iptables / IPVS, etc.) so that a Service’s (Chapter 5) virtual IP forwards to the actual Pods.
- container runtime — the layer that actually runs containers. The current standard is containerd, and CRI-O is also common. The old adapter (dockershim) by which K8s used the docker daemon directly was removed starting in Kubernetes 1.24 (2022). So the phrase “K8s uses docker” is no longer accurate. K8s uses an OCI-compatible runtime, and images built by docker (OCI images) work directly.
The flow, all at once #
Compressing what happens the moment you type kubectl apply -f deployment.yaml gives this.
kubectlsends the apiserver a request that “this Deployment must exist.”- After authentication and validation, the apiserver records that fact in etcd.
- The Deployment controller inside the controller-manager decides “3 more Pods are needed” and creates 3 Pod objects.
- The scheduler assigns a node to each of those Pods.
- The kubelet on that node sees the information and instructs containerd to run the containers.
- The kubelet reports the result back to the apiserver, and you can check the state with
kubectl get pods.
The whole thing flows in one pattern — each component reconciles, within its own area, the desired state recorded in etcd through the apiserver. In this book we follow this flow by hand in Chapter 3 kubectl and your first Pod.
The core resources in one table #
In K8s the unit a person handles is not a container but a resource (object). Names like Pod, Deployment, Service. We cover each in depth from the next chapter, but let’s fix their shape in one table up front.
| Resource | One-line definition | Covered in |
|---|---|---|
| Pod | K8s’s smallest execution unit, bundling 1 container or a few that work together. Shares an IP | Chapter 3 |
| ReplicaSet | Guarantees “N of this Pod template must be up” | Chapter 4 |
| Deployment | The higher-level resource that handles rolling updates and rollbacks on top of ReplicaSet. The unit a person touches most often | Chapter 4 |
| Service | A network abstraction that puts a stable virtual IP / DNS name in front of several Pods | Chapter 5 |
| ConfigMap | Injects config values (environment variables, config files) separated from code | Chapter 6 |
| Secret | Injects secret values (passwords, tokens, certificates) separately. base64 encoding + access control | Chapter 6 |
| Namespace | A logical partition inside the cluster. Separates permissions, resource limits, and name collisions | Chapter 7 |
Once these seven are second nature, you can handle a substantial part of day-to-day work. Deeper resources (StatefulSet, PV / PVC, Ingress, RBAC, CRD, etc.) follow in Parts 2 ~ 3.
How it differs from Docker Compose #
For a reader who has come as far as Docker, the first comparison that comes to mind is Docker Compose. Compose also resembles K8s in that it declaratively defines several containers in one YAML file. But the two solve problems of different scope.
| Docker Compose | Kubernetes | |
|---|---|---|
| Scope | Multi-container within one host | A bundle of containers across several nodes (a cluster) |
| Self-healing | Restart on the same host, roughly | Auto-migrates to another node even if a node dies |
| Scaling | Manual via --scale | Declared as desired state → maintained automatically, autoscaling possible |
| Zero-downtime deploy | Must write it yourself | A Deployment’s rolling update by default |
| Config / secrets | About .env, secrets | ConfigMap / Secret are first-class resources |
| Where it fits | Local development, single-server operation | Operation across several machines / serious SLA environments |
Compose is a tool to simplify development and operation on one host, and K8s is used in operational environments that go beyond that limit. The two aren’t so much rivals as solvers of problems at different stages. Local development is still lighter with Compose, and production is natural with K8s or its managed services.
A detailed mapping of how each row of this table moves to a K8s resource is organized in Appendix A, From docker-compose to k8s. If you’re comfortable with the mental model of Docker / docker-compose, keeping Appendix A beside you as you read the body lets you quickly grasp which Compose key each chapter’s new resource corresponds to.
What this book covers #
K8s is a wide subject. Let’s decide up front how far the 32 chapters of this book go.
| Part | Content | Chapters |
|---|---|---|
| Part 1 | Getting Started with Kubernetes — Pod · Deployment · Service · ConfigMap / Secret · Namespace | 1 ~ 7 |
| Part 2 | Workloads and Operations — StatefulSet · PV / PVC · Ingress · resources · health checks · autoscaling · RBAC | 8 ~ 14 |
| Part 3 | Depth — CNI · RBAC in depth · Admission Controller · CRD / Operator · observability · GitOps | 15 ~ 20 |
| Part 4 | EKS in Production — cluster setup · app deployment skeleton · RDS integration · CI / CD · monitoring · operations checklist | 21 ~ 26 |
| Part 5 | Operations · Debugging · Cost — kubectl debugging · cost optimization · secret operations · upgrade strategy | 27 ~ 30 |
| Part 6 | Capstone — fullstack app EKS deployment (Next.js + FastAPI + RDS + GitOps + observability) | 31 |
| Appendix | From docker-compose to k8s — mapping table + 7 differences that commonly trip you up | A |
Once you finish this whole book, the following are yours.
- You can deploy a small app the K8s way with Pod / Deployment / Service / ConfigMap / Secret / Namespace.
- You can learn the workload operation flow, all the way through StatefulSet · PV / PVC · Ingress · health checks · autoscaling · RBAC.
- You can adopt observability with Prometheus / Grafana / Loki / OpenTelemetry, and GitOps with ArgoCD / Flux.
- You can operate a production workload on AWS EKS end to end (cluster setup · app deployment · DB integration · CI / CD · monitoring · operations checklist).
- You can diagnose common failures like CrashLoopBackOff · OOMKilled · ImagePullBackOff · Pending.
- You can optimize cluster cost with Karpenter + Spot + bin packing.
- You can operate secrets with the sealed-secrets · external-secrets · IRSA patterns.
- You can follow the control plane and node upgrade cycle safely.
- You can deploy modern-react’s Next.js app and modern-python’s FastAPI app on one EKS cluster.
The parts not covered are organized as follows.
| Topic | Where it belongs |
|---|---|
| The depth of writing Helm charts · ArgoCD ApplicationSet patterns · the Cilium eBPF data plane | a later K8s deep-dive book |
| Multi-cluster federation · service mesh (Istio / Linkerd) | a later K8s deep-dive book |
| Differences per managed offering other than EKS (GKE / AKS) | a separate appendix or volume |
| Kubernetes contribution · kubelet internals · the depth of etcd operation | a separate book |
This book’s scope goes as far as “how to use.” The realm of “how to build” is covered in a later K8s deep-dive book.
Exercises #
- From a Docker / docker-compose environment you currently operate or know, write a paragraph about which of the five limits in §“The moment
docker runalone falls short” you’re most likely to hit first. After finishing the book, you’ll come back to it and check which K8s resource or pattern resolves that item. - Write out the 6 steps of the
kubectl apply -f deployment.yamlflow without looking. If a step gives you trouble, also note which of the 5 control plane components (apiserver · etcd · scheduler · controller-manager · cloud-controller-manager) is responsible for that step. In Chapter 4 Deployment and ReplicaSet you’ll follow the same flow by hand. - Pick one docker-compose setup you already know (a small web + db, say) and note in one line for each row how the table in §“How it differs from Docker Compose” applies to that setup. It becomes the starting point when you read Appendix A, From docker-compose to k8s.
In one line: Kubernetes is the de facto standard tool for bundling several machines into one cluster and operating containers with a desired-state + reconcile-loop model. A cluster splits into control plane + worker nodes, and the unit a person handles is resources like Pod / Deployment / Service / ConfigMap / Secret / Namespace. Docker Compose handles one host; Kubernetes handles several nodes.
Next chapter #
In Chapter 2 Local Environment we cover which of the three local cluster options — minikube / kind / Docker Desktop k8s — to pick in which case, installing kubectl and switching your first context, and the procedure of confirming your first cluster with kubectl get nodes. After preparing a local cluster, we move on to Chapter 3 kubectl and your first Pod.