Explain how Kubernetes runs containers with pods, deployments and services, and when a team actually needs it.
Why it matters
Compose runs containers on one machine. Production systems run hundreds of containers across many machines, and they must keep running when a machine dies, scale when traffic grows and update without downtime. Kubernetes (often written K8s) is the most widely used system for that job. Cloud engineer job postings mention it constantly, and managed versions exist on every major cloud: Amazon EKS, Azure AKS and Google GKE.
You do not need to run a cluster to understand it. This lesson builds the mental model: what the main objects are, how they relate and what happens when something fails.
The core idea: desired state
Kubernetes is declarative. You do not tell it "start three containers". You describe the state you want, in YAML, and submit it to the cluster's API. Controllers then work continuously to make the real state match: if a container crashes or a machine disappears, they create a replacement. This loop of "compare desired with actual, then fix the difference" is called reconciliation, and it is the single most important idea in Kubernetes.
The cluster
- The control plane runs the API server (everything talks to it), a database of desired state (etcd), the scheduler (decides which machine runs each workload) and controllers. Managed services run the control plane for you.
- Nodes are the worker machines (VMs) that actually run your containers. Each node runs an agent called the kubelet and a container runtime.
You interact with the cluster through kubectl, the command-line client.
Pods
A pod is the smallest thing Kubernetes runs: one or more containers that share a network address and can share storage. Most pods hold a single app container. Pods are disposable: they get a new IP address when recreated and are never repaired in place. You almost never create pods directly.
Deployments
A Deployment says "keep N identical pods of this template running". It manages a ReplicaSet, which does the counting. When you change the image tag, the Deployment performs a rolling update: it starts new pods and stops old ones gradually, so the app stays up, and you can roll back to the previous revision.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
replicas: 3
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: web
image: ghcr.io/example/hello-devops:3f9c2a1
ports:
- containerPort: 8000
readinessProbe:
httpGet:
path: /
port: 8000
The readiness probe tells Kubernetes when a pod can receive traffic. A pod that fails its readiness probe is taken out of rotation; a failing liveness probe makes Kubernetes restart the container.
Services
Pods come and go, so you need a stable address. A Service gives a set of pods one stable name and virtual IP and load-balances across them. It finds its pods with a label selector, the same app: hello label as above.
apiVersion: v1
kind: Service
metadata:
name: hello
spec:
selector:
app: hello
ports:
- port: 80
targetPort: 8000
type: ClusterIP
Service types:
| Type | Reachable from |
|---|---|
ClusterIP (default) | Inside the cluster only, at hello or hello.<namespace>.svc |
NodePort | A fixed port on every node |
LoadBalancer | A cloud load balancer with an external address (this costs money on a cloud) |
For HTTP routing by hostname or path, clusters usually add an Ingress or the newer Gateway API in front of services.
Other objects you will meet
- Namespaces split a cluster into separate areas, for example per team or per environment.
- ConfigMaps hold configuration; Secrets hold sensitive values. By default Secrets are only base64-encoded, not encrypted, so clusters enable encryption at rest and limit who can read them.
- Labels are key-value tags on every object, and selectors use them to connect objects.
The everyday workflow is kubectl apply -f deployment.yaml, then kubectl get pods, kubectl describe pod <name> and kubectl logs <pod>.
Do you need it?
Kubernetes is powerful but complex, and a managed cluster has ongoing costs for the control plane, nodes and load balancers. Many small teams are better served by a simpler managed container service (Cloud Run, Azure Container Apps, ECS with Fargate). To practise for free, run a local cluster with kind or minikube on Docker, and delete it afterwards.
Common mistakes
- Creating bare pods instead of Deployments, so nothing replaces them when they die.
- Mismatched labels between a Service selector and the pod template, so the Service has no endpoints.
- No readiness probe, so traffic reaches pods before the app is ready.
- Treating Secrets as encrypted by default.
Quick recap
- Kubernetes reconciles desired state (your YAML) with actual state.
- Pods run containers and are disposable; Deployments keep N pods running and roll out updates.
- Services give pods a stable name and load-balance across them using labels.
- Practise locally with kind or minikube; managed clusters cost money.
Try it
This lesson has no hands-on activity. Take the quiz in the Do tab to check your mental model.