Deployments: the one you will use
Managing pods the normal way.
A Deployment is what you use to run almost everything on Kubernetes. You tell it what should be running and how many copies, and it keeps reality matching that. If a copy crashes at 3 a.m., you do not get paged; it brings up a new one.
A few words you will see everywhere. A container is your app plus what it needs to run, so it behaves the same on a laptop or a server. An image is the read-only template a container is built from; nginx:1.25 is the name and version. A Pod is the smallest thing Kubernetes runs; for now, a thin wrapper around one container. A cluster is the machines Kubernetes manages. A node is one of those machines, where pods run.
One file, and it manages the rest
Underneath, a Deployment creates a ReplicaSet. You tell the ReplicaSet to keep three copies alive; it replaces any copy that dies so the count does not drift. You almost never build a ReplicaSet by hand. The Deployment adds what a bare ReplicaSet cannot: a rollout of a new version without taking the whole app offline, and a rollback in seconds if the new one misbehaves. You write this in YAML (YAML Ain't Markup Language): indented plain text. Keep the file in version control (Git records every change), review it, and you can stand the same app up on any cluster.
apiVersion: apps/v1kind: Deploymentmetadata:name: hellospec:replicas: 3selector:matchLabels:app: hellotemplate:metadata:labels:app: hellospec:containers:- name: webimage: nginx:1.25ports:- containerPort: 80
Hand the file to the cluster with kubectl apply. kubectl is the command-line tool you use to talk to a Kubernetes cluster; you'll type it a hundred times a day. The apply part means 'make the cluster match this file.' Then ask what you got.
$ kubectl apply -f hello.yamldeployment.apps/hello created$ kubectl get deployment helloNAME READY UP-TO-DATE AVAILABLE AGEhello 3/3 3 3 18s$ kubectl get pods -l app=helloNAME READY STATUS RESTARTS AGEhello-6d8f4c9b7d-2mkzq 1/1 Running 0 18shello-6d8f4c9b7d-9wp4t 1/1 Running 0 18shello-6d8f4c9b7d-lr7xn 1/1 Running 0 18s
That READY column showing 3/3 means all three copies are up and serving requests. UP-TO-DATE tells you how many pods are running the version you last asked for, and AVAILABLE is how many have stayed healthy long enough to take real users. The odd-looking tails on the pod names, like 6d8f4c9b7d-2mkzq, are normal: Kubernetes gives every pod a unique name so it can tell them apart, and you don't pick them or need to memorize them. That number you set, replicas: 3, is also your dial for scaling. Bump it to 5, apply again, and two more pods show up; drop it back and the extras go away. That's all scaling means at this level.
Changing it without downtime
Say a newer version is ready. Deleting everything would take the app dark while new copies boot. A Deployment swaps pods a few at a time, waiting for each new one to report healthy before retiring an old one. Watch it with rollout status.
$ kubectl set image deployment/hello web=nginx:1.26deployment.apps/hello image updated$ kubectl rollout status deployment/helloWaiting for deployment "hello" rollout to finish: 1 out of 3 new replicas have been updated...Waiting for deployment "hello" rollout to finish: 2 out of 3 new replicas have been updated...deployment "hello" successfully rolled out
When a rollout goes wrong
Not every new version is a good one. Maybe you fat-finger the image tag, or the build you pushed is broken. This is the moment beginners dread, so let's cause it on purpose. Here we point the Deployment at nginx:1.99, a tag that was never published, then ask for the rollout status.
$ kubectl set image deployment/hello web=nginx:1.99deployment.apps/hello image updated$ kubectl rollout status deployment/helloWaiting for deployment "hello" rollout to finish: 1 out of 3 new replicas have been updated...^C
It just sits there, and that stall is the Deployment protecting you. It started one new pod, never saw it turn healthy, and so refused to touch the three old ones. Your app keeps serving throughout. Press Ctrl+C and look at the pods to see the split.
$ kubectl get pods -l app=helloNAME READY STATUS RESTARTS AGEhello-6d8f4c9b7d-2mkzq 1/1 Running 0 6mhello-6d8f4c9b7d-9wp4t 1/1 Running 0 6mhello-6d8f4c9b7d-lr7xn 1/1 Running 0 6mhello-7c4d9f8b5c-x2p9q 0/1 ImagePullBackOff 0 40s
Three old pods still Running, one new pod stuck. ImagePullBackOff means the node tried to download the image, failed, and now waits a little longer between each retry. To find out why, describe the broken pod and read the Events list at the bottom, where Kubernetes narrates, line by line, what it tried to do with this pod.
$ kubectl describe pod hello-7c4d9f8b5c-x2p9q...Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 50s default-scheduler Successfully assigned default/hello-7c4d9f8b5c-x2p9q to node-1Normal Pulling 35s (x2 over 49s) kubelet Pulling image "nginx:1.99"Warning Failed 33s (x2 over 47s) kubelet Failed to pull image "nginx:1.99": manifest for nginx:1.99 not foundWarning Failed 33s (x2 over 47s) kubelet Error: ErrImagePullNormal BackOff 19s (x3 over 46s) kubelet Back-off pulling image "nginx:1.99"Warning Failed 19s (x3 over 46s) kubelet Error: ImagePullBackOff
There it is, on the Failed line: manifest for nginx:1.99 not found. The tag doesn't exist, so the node has nothing to run. Those events also name the two parts that touched the pod: the scheduler picked a node for it, and the kubelet (the agent on that node) tried and failed to pull the image. A misspelled private image or a missing pull secret looks the same. You don't repair this pod; you put the last known-good version back.
$ kubectl rollout undo deployment/hellodeployment.apps/hello rolled back$ kubectl rollout status deployment/hellodeployment "hello" successfully rolled out$ kubectl get pods -l app=helloNAME READY STATUS RESTARTS AGEhello-6d8f4c9b7d-2mkzq 1/1 Running 0 8mhello-6d8f4c9b7d-9wp4t 1/1 Running 0 8mhello-6d8f4c9b7d-lr7xn 1/1 Running 0 8m
The broken pod is gone and the three good ones are untouched. Because the old ReplicaSet was still sitting there, the rollback points traffic back at pods that were already built, so it lands in seconds. The safety net worth memorizing: a bad image stalls a rollout, it doesn't take your running app down with it. kubectl rollout status catches the stall; kubectl rollout undo gets you out of it.
A Deployment is the object you will use day to day. It owns ReplicaSets, rolls out new pod templates, and keeps revision history so you can undo. It scales the new ReplicaSet up and the old one down.
Rolling updates trade speed for safety. maxUnavailable and maxSurge decide how many pods can be down or extra during a change. Prefer a slower rollout with readiness probes over a blast that takes the Service to zero endpoints.
In production, record the image (digest) you rolled and who approved it. kubectl rollout history is your friend after an incident when people argue about what changed.
Try this
Apply a Deployment, watch Ready replicas, then change the image and observe the rollout status.
$ kubectl create deployment hello --image=nginx:1.26 --replicas=3deployment.apps/hello created$ kubectl get deploy helloNAME READY UP-TO-DATE AVAILABLE AGEhello 3/3 3 3 9s$ kubectl set image deployment/hello nginx=nginx:1.27deployment.apps/hello image updated$ kubectl rollout status deployment/helloWaiting for deployment "hello" rollout to finish: 1 out of 3 new replicas have been updated...deployment "hello" successfully rolled out$ kubectl rollout history deployment/hellodeployment.apps/helloREVISION CHANGE-CAUSE1 <none>2 <none>$ kubectl delete deployment hellodeployment.apps "hello" deleted
Takeaway
Deployments manage ReplicaSets and rollouts. Ship with readiness-aware rolling updates, keep history, and verify with rollout status instead of hoping the Service stayed healthy.