Introduction
Running a container in Kubernetes is only the starting point. Production workloads also need mechanisms for maintaining the desired number of replicas, recovering from Pod failures, scaling with demand, and updating application versions without unnecessary disruption.
In this mission, I used a Kubernetes Deployment to manage an NGINX workload on Azure Kubernetes Service (AKS). I configured three replicas, added health probes and resource constraints, verified the rollout, deleted a Pod to observe self-healing, scaled the Deployment, updated the container image, inspected rollout history, and rolled back to the previous version.
What This Part Covers
- Defining a Deployment with three NGINX replicas.
- Configuring resource requests, limits, and health probes.
- Verifying the rollout and associated ReplicaSet.
- Observing automatic Pod replacement after deletion.
- Scaling the Deployment from three replicas to five and back.
- Updating the NGINX image.
- Inspecting rollout history and recording the change cause.
- Rolling back to the previous Deployment revision.
1. Define the Deployment Manifest
I created the Deployment manifest at manifests/02-deployment.yaml.
cat << 'EOF' > manifests/02-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 10
EOF
The manifest defines a Deployment named web, using the apps/v1 API and the app: web label to identify its Pods.
Replica management
replicas: 3
selector:
matchLabels:
app: web
The Deployment declares three desired replicas. Its selector matches Pods labelled app: web, while the Pod template applies that label to each replica it creates.
This relationship allows the Deployment controller to manage the workload through a ReplicaSet, which maintains the required number of Pods.
Rolling update strategy
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
The update strategy controls how Kubernetes replaces old Pods during an update.
-
maxSurge: 1allows one additional Pod above the desired replica count during a rollout. -
maxUnavailable: 0prevents the Deployment from intentionally reducing the number of available replicas below the desired count during the rolling update.
These settings prioritize availability during updates, provided the cluster has sufficient capacity and the replacement Pods can become ready.
Container resources and health checks
The NGINX container uses the nginx:1.27 image and exposes container port 80.
Its resource settings are:
| Resource | Request | Limit |
|---|---|---|
| CPU | 50m |
200m |
| Memory | 64Mi |
128Mi |
The manifest also defines a readiness probe that sends an HTTP request to / on port 80. It begins checking after three seconds and repeats every ten seconds.
A readiness probe helps Kubernetes determine whether a container is ready to receive traffic. The configuration also establishes resource boundaries for each replica, making resource expectations explicit.
2. Apply the Deployment and Verify the Rollout
I applied the manifest to the cluster:
kubectl apply -f manifests/02-deployment.yaml
Kubernetes returned:
deployment.apps/web created
I then monitored the rollout:
kubectl rollout status deployment/web
The command confirmed:
deployment "web" successfully rolled out
To inspect the Deployment, ReplicaSet, and Pods together, I ran:
kubectl get deployment,replicaset,pods -l app=web
The results showed:
NAME READY UP-TO-DATE AVAILABLE
deployment.apps/web 3/3 3 3
NAME DESIRED CURRENT READY
replicaset.apps/web-5b77b547f8 3 3 3
NAME READY STATUS
pod/web-5b77b547f8-5jwm5 1/1 Running
pod/web-5b77b547f8-5tzd h 1/1 Running
pod/web-5b77b547f8-qpzqh 1/1 Running
The important result was that the Deployment had three available replicas, the ReplicaSet maintained three Pods, and all three Pods were running.
The Deployment and ReplicaSet provide different levels of workload management. The Deployment manages rollout and revision history, while the ReplicaSet maintains the required number of matching Pods.
Together, they provide more control over application availability than a standalone Pod.
3. Verify Self-Healing by Deleting a Pod
To test the Deployment controller's reconciliation behavior, I deleted one of the Pods selected by the app=web label.
I used the following command to identify a Pod and pass its name to kubectl delete:
kubectl delete pod $(kubectl get pods -l app=web -o jsonpath='{.items[0].metadata.name}')
Kubernetes confirmed that the selected Pod had been deleted from the k8s-lab namespace.
I then checked the Pods again:
kubectl get pods -l app=web
The output showed three running Pods. A replacement had been created to maintain the Deployment's desired replica count.
This is the Deployment controller's reconciliation behavior in action. The desired state remained three replicas, so Kubernetes worked to restore that state after one Pod was removed.
The replacement Pod did not need to retain the deleted Pod's name. Kubernetes created a new Pod from the Deployment's template through its ReplicaSet.
Operational takeaway: Deleting a Pod managed by a Deployment does not normally reduce the workload's desired replica count. The controller works to maintain that count.
4. Scale the Deployment
The next step was to change the desired replica count.
I scaled the Deployment from three replicas to five:
kubectl scale deployment web --replicas=5
Kubernetes returned:
deployment.apps/web scaled
To verify the result and inspect the Pod placement, I ran:
kubectl get pods -l app=web -o wide
The output showed five running Pods, each with a Pod IP and placement on the AKS node.
I then scaled the Deployment back to three replicas:
kubectl scale deployment web --replicas=3
This returned:
deployment.apps/web scaled
Scaling changes the number of Pod replicas managed by the Deployment. Scaling out from three to five increased the number of running instances, while scaling back to three reduced the desired replica count.
This operation was performed manually with kubectl scale. It was not an automatic response to CPU utilization or incoming traffic; automatic scaling requires additional configuration, such as a Horizontal Pod Autoscaler.
5. Update the NGINX Container Image
After verifying replica management, I updated the Deployment to use the nginx:1.27-alpine image:
kubectl set image deployment/web nginx=nginx:1.27-alpine
Kubernetes confirmed:
deployment.apps/web image updated
The new image uses the Alpine-based NGINX image variant. Changing the image in a Deployment triggers a rollout, allowing Kubernetes to replace Pods according to the configured update strategy.
I then annotated the Deployment with the reason for the change:
kubectl annotate deployment/web kubernetes.io/change-cause="switch to nginx 1.27-alpine"
The output confirmed:
deployment.apps/web annotated
Recording the change cause makes the intention behind a revision easier to identify when reviewing rollout history.
6. Verify the Rollout and Inspect Revision History
I checked the status of the updated Deployment:
kubectl rollout status deployment/web
The rollout completed successfully:
deployment "web" successfully rolled out
I then inspected the revision history:
kubectl rollout history deployment/web
The output showed two revisions:
deployment.apps/web
REVISION CHANGE-CAUSE
1 <none>
2 switch to nginx 1.27-alpine
Revision 1 represented the original Deployment configuration, while revision 2 recorded the change to the Alpine image.
The history provides an operational record of Deployment revisions and their associated change causes. This can help identify which version is currently deployed and which revision to restore if an update needs to be reversed.
7. Roll Back to the Previous Revision
To demonstrate rollback, I reverted the Deployment to its previous revision:
kubectl rollout undo deployment/web
Kubernetes returned:
deployment.apps/web rolled back
The command also displayed a warning explaining that the Deployment had previously been managed with kubectl apply. A rollback does not update the kubectl.kubernetes.io/last-applied-configuration annotation, which can affect the behavior of future kubectl apply operations. Kubernetes recommended applying the previous configuration file when appropriate.
This warning is worth noting when mixing imperative changes, such as kubectl set image, with declarative manifest management. The live Deployment, its rollout history, and the last-applied configuration annotation are related, but they are not interchangeable.
To verify the image configured in the Deployment after the rollback, I ran:
kubectl get deployment web -o jsonpath='{.spec.template.spec.containers[0].image}'; echo
The output was:
nginx:1.27
This confirmed that the Deployment's Pod template had returned to the original image.
The rollback demonstrated how Deployment revision history can be used to reverse a change without manually recreating the entire workload.
8. Operational Takeaways
This mission demonstrated several core capabilities used to manage Kubernetes applications:
- Desired-state management: A Deployment maintains the declared number of replicas through its controller and ReplicaSet.
- Self-healing: When a managed Pod is deleted, Kubernetes creates a replacement to restore the desired replica count.
- Scaling: The replica count can be increased or reduced to change the number of running application instances.
- Health checks: Readiness probes help Kubernetes determine whether a container is ready to receive traffic.
- Rolling updates: A Deployment can replace Pods gradually according to its update strategy.
- Revision history: Rollout history provides a record of Deployment revisions and their recorded change causes.
- Rollback: A previous Deployment revision can be restored when an update needs to be reversed.
-
Configuration management: Mixing imperative updates with declarative
kubectl applyrequires attention to the last-applied configuration and future changes.
These capabilities provide a stronger operational foundation than managing individual Pods directly. They make application changes more controlled and help maintain the intended state of a workload.
Conclusion
The web Deployment was successfully created with three NGINX replicas, and its rollout was verified. I confirmed that Kubernetes replaced a deleted Pod, scaled the Deployment to five replicas and back to three, updated the image to nginx:1.27-alpine, reviewed the rollout history, and restored the original nginx:1.27 image through rollback.
The key distinction is that a Deployment manages the desired state of an application rather than relying on individual Pods to remain unchanged. Replica management, health checks, rolling updates, and rollback together provide essential tools for operating workloads reliably on AKS.
In Part 5, the series will continue with the next mission, building on the workload-management capabilities established here.
This article is Part 4 of the nine-part series, “Kubernetes on AKS: From Zero to Production Workloads.”







Top comments (1)
Reliable deployments are not just about keeping an application running, they’re about maintaining availability while the workload changes. This mission demonstrates how Kubernetes Deployments bring together self-healing, replica scaling, health checks, rolling updates, and rollback capabilities. The ability to recover from Pod deletion and reverse an image update is especially valuable when building resilient workloads on AKS.