Deployment and lifecycle
From Pod to Deployment
In the previous module you learned something uncomfortable: if a Pod dies and nobody recreates it, that is the end of the story. In production nobody deploys bare Pods. You deploy Deployments: an object that declares how many replicas of a Pod must exist and which template they follow, and that takes care of creating them, replacing them when the version changes and going back when something goes wrong.
Underneath, the Deployment does not manage the Pods directly: it creates a ReplicaSet for each version of the template, and it is the ReplicaSet that keeps the number of replicas. You will see it with your own eyes in this lesson. Open the dev-machine tab and, if you want to watch the object tree live, the Explorer tab.
Step 1: Create the Deployment
Create a file deployment.yaml with this content:
cat << 'EOF' > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: ghcr.io/iximiuz/labs/nginx:alpine
ports:
- containerPort: 80
EOF
The YAML, explained in questions and answers
You already know metadata, labels and the contents of containers from the first lesson, so the questions focus on what is new.
Why is apiVersion now apps/v1 and not v1?
Deployments do not belong to the core API group but to the apps group, which gathers the workload objects (Deployments, ReplicaSets, StatefulSets, DaemonSets). The format is <group>/<version>.
What does replicas declare?
The number of identical Pods that must exist at all times. If a Pod dies, the ReplicaSet creates another; if there are too many, it deletes some. It is the perfect example of desired state versus current state.
What is selector.matchLabels for?
It is the Deployment's glue: it defines which Pods it considers its own. It must match the template's labels, and since apps/v1 it is mandatory and immutable. If it did not match template.metadata.labels, the API would reject the object.
What exactly is template?
A complete embedded Pod, with its metadata and its spec. Compare it with the pod.yaml of the first lesson: it is the same thing, only without a name, because Pod names are generated by the ReplicaSet (you will see random suffixes like web-7d4b9c8f6d-x2klp).
So there are three layers of labels in this file?
Yes, and it helps to tell them apart: the labels of the Deployment itself (organizational), the selector (functional, it picks Pods) and the template's labels (the ones the created Pods will carry). The last two must match.
Apply it and watch what appears:
kubectl apply -f deployment.yaml
kubectl get deployments,replicasets,pods -l app=web
Notice the chain: the Deployment web has created a ReplicaSet with a suffix, and that ReplicaSet has created 3 Pods with yet another suffix.
Before moving on, check the self-healing promise: delete one of the Pods with kubectl delete pod <name> and run kubectl get pods -l app=web right away. You will see a replacement appear in seconds. This is what a bare Pod could not give you.
Step 2: Scale to 5 replicas
You can do it imperatively:
kubectl scale deployment web --replicas=5
Or declaratively, changing replicas: 5 in the file and applying again. Both are valid; the declarative one leaves the change recorded in your YAML, which is what you will want in a repository.
Step 3: Deploy a new version (rollout)
Any change to the Deployment's template triggers a rollout: the Deployment creates a new ReplicaSet with the new template and gradually moves replicas from the old one to the new one (RollingUpdate strategy). You are going to simulate a new version by adding an environment variable to the template:
kubectl set env deployment/web VERSION=2
And now, quickly, watch the dance live:
kubectl rollout status deployment/web
kubectl get rs -l app=web
You will see two ReplicaSets: the old one shrinking to 0 and the new one growing to 5. The old one is not deleted: it is the history that makes the rollback possible.
💡 In a real case the change would be the image (kubectl set image deployment/web nginx=registry/app:v2). We use an environment variable because it triggers exactly the same mechanism and does not depend on a second tag existing in the registry.
Step 4: Undo the change (rollback)
Imagine version 2 has turned out to be a disaster. Check the history and go back:
kubectl rollout history deployment/web
kubectl rollout undo deployment/web
The rollback deletes nothing: it creates a new revision whose template is that of the previous revision. Check it in the history when it finishes.
Summary
- A Deployment declares replicas and a template; one ReplicaSet per version keeps them.
- Scaling is changing a number; Kubernetes does the rest.
- Every template change triggers a gradual rollout that does not take the service down.
- The ReplicaSet history makes the rollback instant and safe.
What is left is to put it to the test without a safety net: a real stuck rollout is waiting for you in the next unit.
Challenge: recover a Deployment with a rollback
You have just practiced the rollout and the rollback under lab conditions. This challenge puts them in their natural context: an incident.
A production Deployment has had its rollout stuck for half an hour because the image of the new version does not exist in the registry. The RollingUpdate strategy has half protected the service, with the old Pods holding the traffic, but the previous state has to be recovered. Diagnosis and rollout undo, as in real life.
Passing criterion: the Deployment tienda goes back to the good image through a rollback, with 3/3 replicas ready and no broken Pod. Once you complete it, the lesson is passed.
- Previous lesson
- Challenge: stabilize a Pod with resources and probes
- Next lesson
- StatefulSet