βΈοΈ Introduction to Kubernetes
You can already build a Docker image and run a container. But what happens when one server isn't enough, when a container crashes at 3 a.m., or when traffic triples on Black Friday? Running containers by hand doesn't scale. Kubernetes is the system that runs them for you β across many machines, healing failures and scaling on demand.
Week 11 · Day 5 (Friday: Container Orchestration Basics) · Lecture 1
π― Learning Objectives
By the end of this lesson, you will be able to:
- Explain what container orchestration solves β scaling, self-healing, rolling updates, service discovery, and load balancing across many hosts
- Describe a Kubernetes cluster and distinguish the control plane from the worker nodes
- Define the core objects: pod, ReplicaSet, deployment, and service
- Explain desired-state reconciliation and why it makes Kubernetes self-healing
- Read a simple Deployment and Service YAML and predict what it creates
- Run the essential
kubectlcommands to deploy and inspect an app on a local cluster
Estimated Time: 70 minutes
Practice: Write a Deployment + Service manifest and reason through how the control plane keeps it running.
In This Lesson
Why Orchestration?
A single container on your laptop is easy. Production is a different animal. Imagine your app is a hit: you now need ten copies of it spread over several servers, a way to send traffic evenly to all ten, automatic restarts when one dies, and a smooth way to ship a new version without downtime. Doing that by hand β SSHing into boxes, starting containers, editing load-balancer configs β is slow, error-prone, and impossible at 2 a.m.
Container orchestration is software that automates all of it. Think of it as an air-traffic controller for containers: you declare what you want in the sky, and it handles the takeoffs, landings, and re-routing.
What an orchestrator gives you
| Problem | Doing it by hand | What orchestration does |
|---|---|---|
| Scaling | Start/stop containers on each host manually | Change one number; it adds or removes copies |
| Self-healing | You notice a crash and restart it | Detects the crash and replaces it in seconds |
| Rolling updates | Take the app down, swap the image, hope | Replaces copies gradually with zero downtime |
| Service discovery | Hard-code IP addresses that keep changing | Stable DNS names find the right containers |
| Load balancing | Configure a proxy for every new instance | Spreads traffic across all healthy copies automatically |
Kubernetes (abbreviated K8s β a "K", eight letters, then "s") is the most widely used orchestrator. It was born at Google, opened up in 2014, and is now stewarded by the Cloud Native Computing Foundation. This lesson is your conceptual map; you don't need to memorize every feature, just how the pieces click together.
π‘ Is it overkill? For a tiny app on one server, yes β a single container or Docker Compose is simpler. Kubernetes earns its complexity when you have many services, real uptime requirements, and traffic that changes.
What Is Kubernetes?
Kubernetes is a platform that runs your containers across a group of machines and keeps them running the way you asked. You describe the desired state β "I want three copies of this web app, reachable on port 80" β and Kubernetes continuously works to make reality match that description.
The single most important idea is that Kubernetes is declarative. You don't write a script of steps ("start container, then start anotherβ¦"). You hand it a description of the end goal, and it figures out the steps, over and over, forever.
Core capabilities in plain English
- Scaling: add or remove identical copies of a container on demand, manually or automatically.
- Self-healing: restart crashed containers, replace ones on dead machines, and stop routing traffic to unhealthy ones.
- Rolling updates & rollbacks: ship a new version gradually and undo it instantly if something breaks.
- Service discovery & load balancing: give a group of containers one stable name and one IP, and share traffic across them.
- Config & secret management: inject settings and passwords without rebuilding images.
Cluster Architecture
A Kubernetes cluster is a set of machines working as one. Every machine is a node, and nodes come in two roles. The control plane is the brain that makes decisions; the worker nodes are the muscle where your containers actually run.
The restaurant analogy helps: the control plane is the kitchen manager and order system β it decides what gets cooked and where. The worker nodes are the cooks at their stations, doing the actual cooking. You (through kubectl) are the customer placing an order; you never talk to the cooks directly.
Control plane components
- API Server: the front door. Every command and every component talks through it. When you run
kubectl, you're calling this API. - etcd: a reliable key-value store that holds the entire cluster state β the single source of truth for what should exist.
- Scheduler: watches for new pods that have no home yet and picks the best node to run each one, based on available CPU and memory.
- Controller Manager: runs the control loops that notice "reality drifted from desired state" and take corrective action.
Worker node components
- kubelet: the agent on each node. It takes instructions from the API server and makes sure the assigned containers are actually running and healthy.
- kube-proxy: handles the networking rules that let traffic reach the right pods.
- Container runtime: the software that actually runs containers β
containerdis the common choice today (Docker Engine used one under the hood too).
π‘ You manage state, not machines
Notice that you never SSH into a node to start a container. You send your desired state to the API server, and the cluster's own components carry it out. That indirection is exactly what makes automation and self-healing possible.
Core Objects: Pods to Services
Kubernetes gives you a handful of building blocks. They nest: a Deployment manages a ReplicaSet, which manages Pods, which wrap your containers. A Service sits in front to provide a stable address. Let's build up from the smallest piece.
Pod β the smallest unit
A pod is the smallest thing Kubernetes runs. It usually holds a single container plus shared network and storage. Pods are ephemeral β they can be killed and replaced at any time, and each replacement gets a new IP. That's why you never rely on a pod's address directly.
apiVersion: v1
kind: Pod
metadata:
name: web-pod
labels:
app: web # labels are how other objects find this pod
spec:
containers:
- name: web
image: nginx:1.27 # a real, current image tag
ports:
- containerPort: 80
You rarely create bare pods yourself, though β because if that pod dies, nothing brings it back. For that you use a Deployment.
Deployment β the object you actually use
A Deployment describes a desired state for a set of identical pods: which image, how many copies, and how to update them. It creates a ReplicaSet behind the scenes whose only job is to keep exactly that many pods alive. If a pod dies, the ReplicaSet makes a new one β that's self-healing in action.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
spec:
replicas: 3 # desired state: always keep 3 pods
selector:
matchLabels:
app: web # this Deployment owns pods labeled app=web
template: # the pod blueprint to stamp out
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
Service β a stable front door
Pods come and go, so their IPs are unreliable. A Service gives a group of pods (chosen by label) one stable DNS name and virtual IP, and load-balances traffic across them. Your other apps talk to the Service name, never to individual pods.
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web # send traffic to every pod labeled app=web
ports:
- port: 80 # the Service's port
targetPort: 80 # the container's port
type: ClusterIP # reachable inside the cluster (the default)
π Service types, briefly
ClusterIP (default) is internal-only. NodePort opens a port on every node for outside access β handy for local testing. LoadBalancer asks your cloud provider for a real external load balancer. Start with ClusterIP and reach for the others when you need outside traffic.
Desired-State Reconciliation
Everything above rests on one loop. A controller constantly compares the desired state (what your YAML says) with the actual state (what's really running) and acts to close the gap. This is called reconciliation, and it's why Kubernetes feels alive.
Say you asked for 3 replicas and a pod crashes. Here's what unfolds β no human involved:
The same loop powers scaling (you change replicas: 3 to replicas: 5 and the controller creates two more) and rolling updates (you change the image and the Deployment swaps pods a few at a time, keeping the app up).
β Why declarative wins
Because you describe the goal rather than the steps, the same manifest works whether you're starting fresh, recovering from a crash, or scaling up. The cluster always drives toward your description β so your config file is your infrastructure.
Working with kubectl
kubectl ("cube control") is the command-line tool that talks to the API server. The easiest way to get a cluster on your own machine is Minikube or kind (Kubernetes-in-Docker), or the one-click cluster built into Docker Desktop.
Spin up a local cluster
# Start a single-node local cluster with Minikube
minikube start
# Confirm kubectl can reach it
kubectl cluster-info
kubectl get nodes # should list one Ready node
Deploy from a manifest
Save the Deployment and Service YAML from earlier into web.yaml (separated by a line with ---), then apply it:
# Create or update everything described in the file
kubectl apply -f web.yaml
# Watch it happen
kubectl get deployments # see the Deployment and its replica count
kubectl get pods # see the individual pods spin up
kubectl get services # see the Service and its ClusterIP
The commands you'll use daily
# Inspect and debug
kubectl describe pod web-deployment-abc123 # full details and events
kubectl logs web-deployment-abc123 # a pod's stdout logs
kubectl exec -it web-deployment-abc123 -- sh # open a shell inside a pod
# Scale by editing desired state
kubectl scale deployment web-deployment --replicas=5
# Roll out a new image, then roll back if needed
kubectl set image deployment/web-deployment web=nginx:1.28
kubectl rollout status deployment/web-deployment
kubectl rollout undo deployment/web-deployment
# Clean up
kubectl delete -f web.yaml
Output
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
web-deployment-7d9f8c6b4-2xk9p 1/1 Running 0 20s
web-deployment-7d9f8c6b4-8mzq7 1/1 Running 0 20s
web-deployment-7d9f8c6b4-q4vlt 1/1 Running 0 20s
π‘ apply, not create: Preferkubectl apply -foverkubectl create -f.applyis idempotent β run it again after editing the file and Kubernetes computes just the difference, which is the whole declarative point.
Practice & Quiz
ποΈ Exercise 1: Write a Deployment and Service
Goal: Draft a manifest that runs 4 replicas of the image ghcr.io/acme/api:2.1 listening on container port 3000, plus a ClusterIP Service that exposes it on port 80 and forwards to 3000. Use the label app: api to tie them together.
π‘ Hint
Two objects in one file, separated by a line containing only ---. The Deployment's selector.matchLabels, its pod template.metadata.labels, and the Service's selector must all use the same label. The Service's port is what clients hit; targetPort must match the container's port.
β Solution
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
replicas: 4
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: ghcr.io/acme/api:2.1
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
selector:
app: api
ports:
- port: 80
targetPort: 3000
type: ClusterIP
ποΈ Exercise 2: Reason about self-healing
Goal: Your Deployment has replicas: 3. A worker node hosting one of the pods loses power. In plain words, describe what the cluster does and roughly how it decides where the replacement pod runs.
β Solution
The kubelet on the dead node stops reporting, so the control plane marks its pod as lost. The ReplicaSet controller sees only 2 of the desired 3 pods and asks the API server for one more. The scheduler picks a healthy node with enough free CPU and memory, and that node's kubelet starts the replacement β bringing the count back to 3. No human action is required; the reconciliation loop did it.
π― Quick Quiz
Question 1: Which component is the "front door" that every command and component talks through?
Question 2: Why do you put a Service in front of your pods?
Question 3: What does "desired-state reconciliation" mean?
Best Practices & Pitfalls
β Do
- Manage apps with Deployments, not bare pods, so self-healing works
- Keep manifests in version control and deploy with
kubectl apply -f - Pin real image tags (e.g.
nginx:1.27), neverlatest, so rollouts are predictable - Use labels consistently β they're the glue between Deployments, Services, and pods
- Start local with Minikube or kind before touching a cloud cluster
β Don't
- Don't SSH into nodes to start containers by hand β you bypass the whole system
- Don't hard-code pod IP addresses; they change every restart
- Don't reach for Kubernetes on a one-container hobby app β Compose is simpler
- Don't forget that
type: ClusterIPis internal only; you need NodePort or LoadBalancer for outside traffic
β οΈ Mismatched labels are the classic first bug
If your Service's selector doesn't exactly match your pods' labels, the Service finds zero pods and traffic goes nowhere β with no error. When "it's running but unreachable," check that the labels line up first.
Summary
π Key Takeaways
- Orchestration automates scaling, self-healing, rolling updates, service discovery, and load balancing across many hosts
- A cluster splits into a control plane (the brain: API server, etcd, scheduler, controllers) and worker nodes (where pods run)
- The objects nest: Deployment β ReplicaSet β Pods β containers, with a Service giving a stable address in front
- Reconciliation β controllers looping to match actual state to desired state β is what makes it self-healing and declarative
kubectl apply -fis your main verb; you change desired state and the cluster does the rest
π Additional Resources
- Kubernetes β Overview and core concepts
- Kubernetes Basics β interactive tutorial
- kubectl reference documentation
- Minikube β get started locally
π What's Next?
Kubernetes is the industry standard, but it isn't the only orchestrator β and it can be a lot to run. Next we look at Docker Swarm, Docker's simpler, built-in orchestration mode, and weigh when its lighter approach is the better fit.
π Big milestone!
You now have a mental model of how containers run at scale β clusters, pods, deployments, services, and the reconciliation loop that ties them together.