All modules in this track (20)
In this module, you'll learn to:
- Explain why pods are disposable, and why you create Deployments rather than pods
- Set resource requests and limits, and readiness and liveness probes
- Follow a rolling update step by step, and roll back
Before you start: read How a Cluster Works first. This module assumes the idea of desired state and controllers, and that you know how to build and run a container image.
A Pod is a wrapper around your container, and it's disposable
You never run a container on Kubernetes directly. You run a Pod: the smallest thing the cluster schedules. A pod holds one container (occasionally a few that must live together, such as an app plus a log-shipping helper), and everything in it shares one IP address and can share storage.
The key fact about pods is that they're disposable. When a node fails, a pod is evicted, or you deploy a new version, the old pod is deleted and a new one is created, with a new name and a new IP address. Nothing ever repairs a pod in place. So you almost never create pods yourself. You create a Deployment, and let it create and replace pods for you.
A Deployment you can actually run in production
This is a complete, realistic Deployment. Every field below the image is there for a reason, and each is explained after the listing.
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-api
namespace: orders
spec:
replicas: 3
selector:
matchLabels:
app: orders-api # which pods this Deployment owns
strategy:
rollingUpdate:
maxSurge: 1 # at most 1 extra pod during an update
maxUnavailable: 0 # never drop below 3 ready pods
template: # the pod template: what each pod looks like
metadata:
labels:
app: orders-api
spec:
containers:
- name: orders-api
image: <account>.dkr.ecr.<region>.amazonaws.com/orders-api@sha256:<digest>
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: orders-api-config
resources:
requests: { cpu: 250m, memory: 256Mi }
limits: { memory: 512Mi }
readinessProbe:
httpGet: { path: /healthz/ready, port: 8080 }
periodSeconds: 5
livenessProbe:
httpGet: { path: /healthz/live, port: 8080 }
initialDelaySeconds: 15
periodSeconds: 10selectorandlabelsare how Kubernetes connects objects. The Deployment owns every pod labelledapp: orders-api, and a Service will find the pods by the same label in the next module.resourcessays how much CPU and memory each container needs, and the most it may use. The next section explains it.- The image is pinned by digest, so every pod runs exactly the same bytes. Container Delivery explains why.
Requests and limits
Every container should say how much CPU and memory it needs. Kubernetes uses two numbers:
- Requests are a reservation. The scheduler only places a pod on a node with that much unreserved CPU and memory left. If no node has room, the pod waits in
Pending. - Limits are a ceiling. A container that uses more CPU than its limit is slowed down (throttled). A container that uses more memory than its limit is killed and restarted, shown as
OOMKilled.
CPU is measured in cores, so 250m (250 millicores) is a quarter of a CPU. Memory uses binary units: 256Mi is 256 mebibytes, 1Gi is one gibibyte.
| Setting | What happens | If it's wrong |
|---|---|---|
| CPU request | Reserves CPU on a node for scheduling | Too high wastes nodes; too low packs pods onto busy nodes |
| Memory request | Reserves memory on a node for scheduling | Too low lets nodes run out of memory and evict pods |
| CPU limit | Throttles the container above it | Too low makes the app slow even when the node is idle |
| Memory limit | Kills the container above it | Too low causes restarts under normal load |
A good starting point is the one in the Deployment above: CPU and memory requests based on real usage, a memory limit, and often no CPU limit, so a busy pod can borrow idle CPU. Requests also drive autoscaling and most of the cloud bill, which is why Scaling & Cost comes back to them in depth.
# Real usage per pod (needs metrics-server; see the add-ons module)
kubectl top pods
# Why is my pod Pending? The Events section names the missing resource
kubectl describe pod <pod-name>How Kubernetes knows your app is ready, and still alive
A container process being started doesn't mean the app is ready. It may still be loading config, warming a cache, or connecting to a database. Probes let the app tell Kubernetes the truth.
| Probe | Question it answers | What happens if it fails |
|---|---|---|
| Readiness | "Can this pod take traffic right now?" | The pod is taken out of its Service's endpoints, so it gets no requests. It isn't restarted. |
| Liveness | "Is this container stuck beyond repair?" | The kubelet kills and restarts the container. |
| Startup | "Has a slow app finished starting yet?" | Holds off the liveness probe until startup succeeds, so slow starters aren't killed mid-boot. |
The most common mistake is making the liveness probe check a dependency, such as the database. When the database has a blip, every pod fails liveness at once, and Kubernetes restarts your whole app, which makes the outage worse. Liveness should only check the process itself. Put dependency checks in readiness.
A rolling update, step by step
Change the image in the Deployment, and the Deployment controller replaces pods gradually, never dropping below the number of ready pods you asked for. With maxSurge: 1 and maxUnavailable: 0, it looks like this:
If a new pod never becomes ready, the rollout simply stops at that step: old pods keep serving and users aren't affected. That's the safety net readiness probes give you, and it's why a Deployment without a readiness probe can take a working service down with a bad release.
# Watch a rollout until it finishes, or report that it failed
kubectl rollout status deployment/orders-api -n orders
# See the ReplicaSets: one per version, the old one scaled to 0
kubectl get replicasets -n orders -l app=orders-api
# Go back to the previous version, reusing the kept ReplicaSet
kubectl rollout undo deployment/orders-api -n ordersConfigMaps and Secrets
Build one image and configure it per environment, rather than baking settings into the image. A ConfigMap holds plain settings (log level, feature flags, URLs) and can be injected as environment variables, as in the Deployment above, or mounted as files.
apiVersion: v1
kind: ConfigMap
metadata:
name: orders-api-config
namespace: orders
data:
LOG_LEVEL: info
PAYMENTS_URL: http://payments.payments.svc.cluster.localA Kubernetes Secret looks similar, but its values are only base64-encoded, not encrypted, so anyone who can read Secrets in that namespace can read the values. For real credentials, the better pattern is to keep them in AWS Secrets Manager and sync them in, covered in Workload Identity & Secrets. One gotcha: pods read environment variables once, at start-up. Changing a ConfigMap doesn't change running pods until they restart (kubectl rollout restart deployment/orders-api -n orders).
Deployments suit apps whose pods are interchangeable and keep no data of their own. Databases, node agents, and one-off or scheduled tasks need other controllers (StatefulSets, DaemonSets, Jobs, and CronJobs), covered in Storage & Other Workload Types.
Implementation notes
- Always set a readiness probe on anything that receives traffic. Without one, a pod counts as ready the moment its process starts.
- Always set memory requests, and a memory limit. A container that goes over its memory limit is killed (
OOMKilled). Without a limit, one leaking pod can starve every other pod on the node. - Run at least two replicas of anything users depend on. A single pod means every deploy, node replacement, or crash is an outage.
- Use
kubectl explainto learn any field. For example,kubectl explain deployment.spec.strategyprints the documentation for that field, straight from your cluster's version.
Key terms
- Pod: the smallest unit Kubernetes runs; one container, occasionally a few, sharing one IP address.
- Deployment: keeps a set number of identical pods running, and replaces them safely.
- ReplicaSet: the object a Deployment creates for each version, to hold the pod count.
- Request and limit: the CPU and memory reserved for scheduling, and the most a container may use.
- Readiness probe: "can this pod take traffic?" Failing removes it from its Services.
- Liveness probe: "is this container stuck?" Failing restarts it.
- ConfigMap: plain settings injected into pods as environment variables or files.
Pop quiz
Five questions: three on the ideas in this module, and two scenarios where you apply them. The order changes every time you take it, and 4 out of 5 passes.
5 questions. Decide on your answer to each, then check it.
-
Scenario
Your liveness probe checks the database, and the database has a 30-second blip. What happens?
Show answer
Every pod fails liveness and is restarted, making the outage worse
Liveness failures restart containers, so a shared dependency failing restarts everything at once. Put dependency checks in readiness, and keep liveness to the process itself.
-
Scenario
During a rolling update with
maxUnavailable: 0, the new version's pods never pass readiness. What do users see?Show answer
Nothing changes: the old pods keep serving and the rollout stops
New pods only replace old ones once they're ready. If they never become ready, the rollout stalls and the old version keeps serving. That's the safety net probes give you.
-
Concept
A container goes over its memory limit. What happens?
Show answer
It's killed and restarted, shown as
OOMKilledGoing over a memory limit kills the container; going over a CPU limit only throttles it. That difference is why memory limits need care.
-
Concept
What does the scheduler use to decide whether a pod fits on a node?
Show answer
The pod's requests
Requests are a reservation. The scheduler places a pod only where its requested CPU and memory are still unreserved, whatever the pod actually uses.
-
Concept
You change a ConfigMap value that pods read as an environment variable. When do running pods see it?
Show answer
Only after they restart, for example with
kubectl rollout restartEnvironment variables are read once, when the container starts. Restart the Deployment to pick up the new value.