Workload Controllers
Creating and operating Deployment, StatefulSet, DaemonSet, Job, CronJob
Overview
Workload controllers manage Pod lifecycle, replica count, update strategies, and more. The CKA exam focuses on Deployment, DaemonSet, Job, and CronJob.
1. Deployment
1.1 Create Deployment
kubectl create deployment web --image=nginx --replicas=3
# Generate YAML (--dry-run=client -o yaml is commonly used)
kubectl create deployment web --image=nginx --replicas=3 --dry-run=client -o yaml > deploy.yaml
# Expose Service
kubectl expose deployment web --port=80 --target-port=80 --type=NodePort
1.2 YAML Example
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
1.3 Rolling Update (RollingUpdate)
# Update image
kubectl set image deployment/nginx-deployment nginx=nginx:1.26
# Or edit YAML
kubectl edit deployment/nginx-deployment
# Specify update strategy (Recreate / RollingUpdate)
kubectl patch deployment nginx-deployment -p '{"spec":{"strategy":{"type":"RollingUpdate","rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"}}}}'
# View update status
kubectl rollout status deployment/nginx-deployment
kubectl rollout history deployment/nginx-deployment
Update Strategy Comparison
| Strategy | Description | Use Case |
|---|---|---|
RollingUpdate | Gradually replace Pods, supports maxSurge/maxUnavailable | Default strategy, zero downtime |
Recreate | Delete all old Pods first, then create new Pods | Development environment, database migration |
# RollingUpdate configuration
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Maximum number of Pods allowed beyond the desired replica count (number or percentage)
maxUnavailable: 0 # Maximum number of Pods allowed to be unavailable during the update
1.4 Rollback
# Roll back to the previous revision
kubectl rollout undo deployment/nginx-deployment
# Roll back to a specific revision
kubectl rollout undo deployment/nginx-deployment --to-revision=2
# View revision history
kubectl rollout history deployment/nginx-deployment
# View details of a specific revision
kubectl rollout history deployment/nginx-deployment --revision=2
1.5 Scaling
# Manual scaling
kubectl scale deployment/nginx-deployment --replicas=5
kubectl scale deployment/nginx-deployment --replicas=2
2. ReplicaSet
ReplicaSet ensures a specified number of Pods are always running. Usually managed automatically by Deployments; direct management is not recommended.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
spec:
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: nginx
image: nginx
# View ReplicaSet (RS)
kubectl get replicasets
kubectl get rs
# View the association between RS and Deployment
kubectl describe deployment nginx-deployment | grep ReplicaSet
kubectl get rs -l app=nginx
# Delete RS (also deletes the Pods it manages, but you cannot directly delete an RS managed by a Deployment)
kubectl delete rs <rs-name>
Note: The CKA exam does not require manually creating RS, but you need to understand that RS is the foundation for how Deployments manage Pods.
3. StatefulSet
Used for stateful applications (databases, message queues, etc.), providing stable network identities and persistent storage.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
Key Features
| Feature | Description |
|---|---|
| Ordered Deployment | Pods are created sequentially from 0 to N-1 (web-0, web-1, web-2) |
| Ordered Scale-down | Deleted sequentially from N-1 to 0 |
| Stable Network Identity | Pod names are stable: <statefulset>-<ordinal> |
| Stable Storage | PVCs are retained when decoupled from Pods |
# View StatefulSet Pod names
kubectl get pods -l app=nginx # Output: web-0, web-1, web-2
# Access via stable DNS
# <pod>.<service>.<namespace>.svc.cluster.local
# web-0.nginx.default.svc.cluster.local
Update Strategies
# OnDelete: Update after manually deleting Pods
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"OnDelete"}}}'
# RollingUpdate (default): Update sequentially
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}'
# Partitioned update
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'
4. DaemonSet
Runs one Pod on each node (or on matching nodes).
Typical Use Cases
- Node monitoring (Node Exporter)
- Log collection (Fluentd, Filebeat)
- Network plugins (Calico, Flannel)
- Storage plugins (Ceph, GlusterFS)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
spec:
selector:
matchLabels:
name: fluentd
template:
metadata:
labels:
name: fluentd
spec:
tolerations:
- operator: Exists
containers:
- name: fluentd
image: fluent/fluentd:v1.16
# Create DaemonSet
kubectl apply -f daemonset.yaml
# View DaemonSet
kubectl get daemonsets
kubectl get ds
# View Pods on each node
kubectl get pods -o wide
# Update image
kubectl set image ds/fluentd fluentd=fluent/fluentd:v1.17
# View rolling update status
kubectl rollout status ds/fluentd
# Exam tip: Imperative creation of DaemonSet
kubectl create deployment fluentd --image=fluent/fluentd --dry-run=client -o yaml > ds.yaml
# Then edit ds.yaml to change kind to DaemonSet and remove replicas
5. Job
Executes one-off tasks, Pods automatically terminate upon completion.
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
completions: 5 # Number of Pods that need to complete successfully
parallelism: 2 # Number of parallel executions
backoffLimit: 4 # Failure retry count
activeDeadlineSeconds: 30 # Task timeout
template:
spec:
containers:
- name: pi
image: perl:5.34
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
# Create Job
kubectl create job pi --image=perl:5.34 -- perl -Mbignum=bpi -wle 'print bpi(2000)'
# Create Job (generate YAML from CronJob)
kubectl create job test-job --from=cronjob/backup-cronjob -n default
# View Job status
kubectl get jobs
kubectl describe job pi
# View Job Pod logs
kubectl logs job/pi
kubectl logs -l job-name=pi
# Delete Job
kubectl delete job pi
# Important: restartPolicy can only be Never or OnFailure
Advanced Parameters
| Parameter | Description | Default |
|---|---|---|
.spec.completions | Number of Pods to complete | 1 |
.spec.parallelism | Number of parallel Pods | 1 |
.spec.backoffLimit | Failure retry count | 6 |
.spec.activeDeadlineSeconds | Task timeout duration | Unlimited |
.spec.ttlSecondsAfterFinished | Auto-cleanup time | Unlimited |
6. CronJob
Scheduled execution of Jobs, based on standard Cron syntax.
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *" # Execute every minute
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.28
command: ["echo", "Hello CKA"]
restartPolicy: OnFailure
startingDeadlineSeconds: 100 # Startup deadline
concurrencyPolicy: Forbid # Allow / Forbid / Replace
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
# Create CronJob
kubectl create cronjob hello --image=busybox:1.28 --schedule="*/1 * * * *" -- echo "Hello CKA"
# View CronJob
kubectl get cronjobs
kubectl get cj
# View triggered Jobs
kubectl get jobs
# Suspend CronJob
kubectl patch cronjob hello -p '{"spec":{"suspend":true}}'
# Resume
kubectl patch cronjob hello -p '{"spec":{"suspend":false}}'
# Manually trigger
kubectl create job --from=cronjob/hello manual-job
Cron Syntax Quick Reference
Minute Hour Day Month Weekday
*/1 * * * * Every minute
0 * * * * Every hour
0 8 * * 1 Every Monday 8:00
0 0 1 * * 1st of every month 0:00
*/5 * * * * Every 5 minutes
CronJob Parameters
| Parameter | Description |
|---|---|
concurrencyPolicy: Allow | Allow concurrent execution (default) |
concurrencyPolicy: Forbid | Forbid concurrency, skip if previous is incomplete |
concurrencyPolicy: Replace | Replace the previous running Job |
startingDeadlineSeconds | Maximum wait time after missing a schedule |
successfulJobsHistoryLimit | Number of retained successful Job history |
failedJobsHistoryLimit | Number of retained failed Job history |
7. Useful Exam Commands
# Create Deployment and generate YAML
kubectl create deployment web --image=nginx --replicas=3 --dry-run=client -o yaml
# Update Deployment image
kubectl set image deployment/web nginx=nginx:1.26
# View rollout status
kubectl rollout status deployment/web
kubectl rollout history deployment/web
# Rollback
kubectl rollout undo deployment/web
kubectl rollout undo deployment/web --to-revision=1
# Scaling
kubectl scale deployment/web --replicas=5
# Create Job and CronJob (--dry-run supported)
kubectl create job pi --image=perl:5.34 -- perl -Mbignum=bpi -wle 'print bpi(2000)' --dry-run=client -o yaml
kubectl create cronjob hello --image=busybox:1.28 --schedule="*/1 * * * *" --dry-run=client -o yaml
# JSON method for creating DaemonSet
kubectl run ds-test --image=nginx --restart=Always --dry-run=client -o yaml | sed 's/kind: Deployment/kind: DaemonSet/' | sed '/replicas/d'
🧪 Complete Hands-on Example: Create a Deployment, Perform Rolling Update and Rollback
Scenario
Create an nginx Deployment, update the image version, then perform a rollback.
Prerequisites
- A working Kubernetes cluster
- kubectl is configured to connect to the cluster
Steps
Step 1: Create Deployment
kubectl create deployment web --image=nginx:1.25 --replicas=3
# Expected output: deployment.apps/web created
kubectl get deployment web
# Expected output: NAME READY UP-TO-DATE AVAILABLE AGE
# web 3/3 3 3 <seconds>
kubectl get pods -l app=web
# Expected output: NAME READY STATUS RESTARTS AGE
# web-<hash1>-<pod-id> 1/1 Running 0 <seconds>
# web-<hash2>-<pod-id> 1/1 Running 0 <seconds>
# web-<hash3>-<pod-id> 1/1 Running 0 <seconds>
Step 2: Upgrade Image Version (Rolling Update)
kubectl set image deployment/web nginx=nginx:1.26
# Expected output: deployment.apps/web image updated
# Observe the rolling update process
kubectl rollout status deployment/web
# Expected output: Waiting for rollout to finish: 2 out of 3 new replicas are available...
# deployment "web" successfully rolled out
Step 3: View Rolling Update History
kubectl rollout history deployment/web
# Expected output: deployment.apps/web
# REVISION CHANGE-CAUSE
# 1 <none>
# 2 <none>
kubectl rollout history deployment/web --revision=2
# Expected output: deployment.apps/web with revision #2
# Pod Template:
# Labels: app=web
# Containers:
# nginx:
# Image: nginx:1.26
Step 4: Perform Rollback
# Roll back to the previous revision
kubectl rollout undo deployment/web
# Expected output: deployment.apps/web rolled back
# Confirm rollback is complete
kubectl rollout status deployment/web
# Expected output: deployment "web" successfully rolled out
# Verify image version has been restored
kubectl describe deployment web | grep Image
# Expected output: nginx:1.25
# Roll back to a specific revision (optional)
kubectl rollout undo deployment/web --to-revision=2
Verification
# Check current replica count and image version
kubectl get deployment web -o wide
# Expected output shows 3/3 READY, IMAGE is nginx:1.25
# Check all Pods are running normally
kubectl get pods -l app=web
# Expected output: 3 Pods all in Running status
# Cleanup
kubectl delete deployment web
# Expected output: deployment.apps "web" deleted
Exam Tips
- Using percentages for
maxSurgeandmaxUnavailablein rolling updates is more flexible and is a CKA exam topic kubectl set imageis the fastest way to update, but the exam often also requires usingkubectl editorkubectl patch- ReplicaSet history is not deleted after rollback; you can roll back to any historical revision again
- Using the
--recordflag when creating a Deployment records command history in CHANGE-CAUSE (older K8s versions) - Deployment rollback is essentially replacing the Pod template with the ReplicaSet corresponding to the historical revision