1. Pods, the Atomic Unit
A Pod is the smallest deployable unit in Kubernetes — one or more containers that always run together, on the same node, sharing the same network namespace and (optionally) storage. Most Pods run exactly one container; multiple containers in a Pod is the exception, reserved for tightly-coupled helper ("sidecar") patterns.
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
labels:
app: my-app
spec:
containers:
- name: my-app
image: myrepo/my-app:1.4.0
ports:
- containerPort: 3000
kubectl apply -f pod.yaml
kubectl get pods
kubectl describe pod my-app-pod
kubectl logs my-app-pod
kubectl delete -f pod.yaml
Here's the crucial gap: if this Pod's node crashes, or the container inside it exits unexpectedly, nothing recreates it. A bare Pod has no controller watching over it — it's a one-shot declaration, not a self-healing promise. That gap is exactly what ReplicaSets exist to close.
The containers block maps almost directly to concepts from Weeks 1-6 — image, ports, and (as you'll see in Week 10) env. Kubernetes doesn't reinvent container configuration, it wraps the same ideas in a declarative, cluster-aware layer.
2. ReplicaSets
A ReplicaSet declares "I want exactly N Pods matching this template, running at all times" and continuously reconciles toward that — if a Pod disappears, it creates a replacement; if there are too many, it deletes the excess.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-app-rs
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template: # this IS a Pod template, embedded
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: myrepo/my-app:1.4.0
ports:
- containerPort: 3000
kubectl apply -f replicaset.yaml
kubectl get pods -l app=my-app # 3 Pods
kubectl delete pod <one-of-the-pod-names>
kubectl get pods -l app=my-app # still 3 — a replacement appeared automatically
The selector is how the ReplicaSet knows which Pods belong to it — it
matches Pods by label, not by name or creation order. This label-based matching
pattern is used everywhere in Kubernetes, and you'll see it again for Services in
Week 9.
ReplicaSets have one glaring gap: no built-in way to roll out a new image version without downtime — change the image and you'd have to manually delete Pods to force replacement, all at once. That's precisely the problem Deployments solve, and it's why you'll write Deployments, not ReplicaSets, in practice.
3. Deployments
A Deployment manages ReplicaSets, the way a ReplicaSet manages Pods. You declare the desired state; the Deployment controller creates and manages the ReplicaSets underneath, and specifically knows how to transition between them safely when your Pod template changes.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: myrepo/my-app:1.4.0
ports:
- containerPort: 3000
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
kubectl apply -f deployment.yaml
kubectl get deployments # 1 Deployment
kubectl get replicasets # 1 ReplicaSet, created and owned by the Deployment
kubectl get pods # 3 Pods, created and owned by that ReplicaSet
This is worth internalizing as a chain: Deployment → ReplicaSet → Pods, each layer owning and reconciling the one below it. You'll basically always interact with the Deployment; the ReplicaSet is an implementation detail the Deployment manages for you, most visible when a rolling update creates a second one temporarily.
Without them, the scheduler has no idea how much of a node's capacity your Pod actually needs, and a runaway container has nothing stopping it from starving its neighbors. This block previews Week 14's deep dive on resource management — start the habit of setting it now.
4. Rolling Updates & Rollbacks
Change the image in a Deployment's Pod template, and the Deployment controller rolls it out gradually — spinning up new Pods on the new version, waiting for them to be ready, then retiring old ones, a few at a time, never taking the whole app down at once.
kubectl set image deployment/my-app my-app=myrepo/my-app:1.5.0
kubectl rollout status deployment/my-app
# Waiting for deployment "my-app" rollout to finish: 2 out of 3 new replicas...
# deployment "my-app" successfully rolled out
kubectl get replicasets
# TWO ReplicaSets now exist — the old one scaled to 0, the new one scaled to 3
kubectl rollout history deployment/my-app # see every past revision
kubectl rollout undo deployment/my-app # roll back to the previous revision
kubectl rollout undo deployment/my-app --to-revision=2 # or a specific one
This is why the old ReplicaSet doesn't get deleted after a rollout — it's kept around (scaled to zero) specifically so a rollback is instant: scale the old one back up, scale the new one down, no rebuild or redeploy required.
A rollout is only as safe as the image tag it points to. Deploying "latest" here means kubectl rollout undo has nothing meaningful to roll back to — the "old" ReplicaSet's image reference is still "latest," which may since have changed underneath you. Immutable version tags make rollbacks actually reliable.
5. Hands-on Exercise
Deploy your own image, break it with a bad rollout, and roll it back
Take an image you pushed to a registry in Week 6 and run it as a real Deployment, then deliberately trigger a failed rollout to practice recovering from one.
Part 1 — Deploy for real:
- Write a Deployment manifest for your Week 6 image, 3 replicas, with resource requests and limits set.
kubectl apply -fit, then confirm 1 Deployment, 1 ReplicaSet, and 3 Pods exist and are Running.- Delete one Pod manually and confirm a replacement appears within a few seconds.
- Scale it to 5 replicas with
kubectl scale deployment/my-app --replicas=5and confirm the count updates.
If your image is in a private registry, you'll need an imagePullSecret configured for kubectl to pull it — a quick search for "kubectl create secret docker-registry" will get you unblocked if you hit an ImagePullBackOff error.
Part 2 — Break a rollout on purpose, and recover:
- Tag and push a new, deliberately broken version of your image (e.g. one with a typo'd CMD that crashes immediately), then
kubectl set imageyour Deployment to point at it. - Watch
kubectl rollout status— it should hang or fail as new Pods crash-loop instead of becoming ready. - Run
kubectl get podsandkubectl describe pod <a-crashing-one>to see the real failure evidence (CrashLoopBackOff status, relevant Events). - Roll back with
kubectl rollout undo, confirm the Deployment returns to a healthy state serving the previous version, and checkkubectl rollout historyto see both revisions recorded.
By default, a Deployment's rolling update strategy keeps at least some old, working Pods available while new ones are rolled out — this is exactly why a bad rollout usually doesn't take your whole app offline immediately, giving you time to notice and roll back.
6. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
Why is a bare Pod (with no ReplicaSet/Deployment) rarely used directly?
Why is a bare Pod (with no ReplicaSet/Deployment) rarely used directly?
A bare Pod has no controller watching over it — if its node fails or its container crashes, nothing recreates it. It's a one-time declaration, not a self-healing guarantee, which is why almost every real workload is managed through a Deployment instead.
Q2
What's the ownership chain from a Deployment down to a running container?
What's the ownership chain from a Deployment down to a running container?
Deployment → ReplicaSet → Pod → container. The Deployment manages ReplicaSets (handling rollouts between them); each ReplicaSet manages a set of Pods matching its label selector; each Pod runs one or more containers.
Q3
Why does the old ReplicaSet stick around (scaled to 0) after a successful rolling update?
Why does the old ReplicaSet stick around (scaled to 0) after a successful rolling update?
It's kept so a rollback can be instant — kubectl rollout undo just scales the old ReplicaSet back up and the new one back down, with no rebuild or redeploy needed. This is the whole mechanism behind fast, reliable rollbacks.
Q4
Why does deploying "latest" undermine the reliability of kubectl rollout undo?
Why does deploying "latest" undermine the reliability of kubectl rollout undo?
The rollback re-references the OLD ReplicaSet's image tag — if that tag is "latest," it's a moving pointer that may no longer refer to the image that was actually running before. An immutable version tag guarantees the rollback restores the exact previous image, not whatever "latest" currently happens to mean.