Understanding Deployments
Why Deployments Matter
The Problem: Managing individual pods is complex and error-prone at scale.
The Solution: Deployments provide declarative updates for Pods and ReplicaSets.
Key Benefits: Automated rollouts, rollbacks, scaling, and self-healing.
Real-World Analogy
Think of a Deployment as a factory production line manager:
- Deployment = Production manager with the blueprint
- ReplicaSet = Shift supervisor ensuring worker count
- Pods = Individual workers on the line
- Desired State = Production targets to meet
- Rolling Update = Gradual shift changes without stopping production
Deployment Architecture
Deployment
The top-level controller. Manages ReplicaSets and provides declarative updates, rollback, and scaling.
ReplicaSet
Created by the Deployment. Ensures the desired number of pod replicas are running at all times.
Pods
The actual running instances of your application. Managed by the ReplicaSet, self-healing on failure.
Basic Deployment YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3 # Number of pod replicas
selector:
matchLabels:
app: nginx
template: # Pod template
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
Working with Deployments
Creating and Managing Deployments
# Create deployment from YAML
kubectl apply -f deployment.yaml
# Create deployment imperatively
kubectl create deployment nginx --image=nginx:1.21 --replicas=3
# Get deployments
kubectl get deployments
kubectl get deploy nginx-deployment -o wide
# Describe deployment
kubectl describe deployment nginx-deployment
# Get deployment in YAML
kubectl get deployment nginx-deployment -o yaml
# Watch deployment status
kubectl rollout status deployment/nginx-deployment
# Get ReplicaSets created by deployment
kubectl get rs -l app=nginx
# Get pods created by deployment
kubectl get pods -l app=nginx
Advanced Deployment Configuration
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
namespace: production
labels:
app: myapp
version: v1.0.0
spec:
replicas: 5
revisionHistoryLimit: 10 # Keep 10 old ReplicaSets
selector:
matchLabels:
app: myapp
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2 # Max pods above desired
maxUnavailable: 1 # Max pods unavailable
template:
metadata:
labels:
app: myapp
version: v1.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- myapp
topologyKey: kubernetes.io/hostname
containers:
- name: app
image: myapp:v1.0.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
- containerPort: 9090
name: metrics
env:
- name: ENV
value: production
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
Deployment Best Practices
- Always use labels: For organization and selection
- Set resource limits: Prevent resource exhaustion
- Configure health checks: Ensure only healthy pods receive traffic
- Use antiAffinity: Spread pods across nodes for HA
- Set revisionHistoryLimit: Control how many old ReplicaSets to keep
Scaling Applications
Manual Scaling
# Scale deployment to 10 replicas
kubectl scale deployment nginx-deployment --replicas=10
# Scale multiple deployments
kubectl scale deployment app1 app2 --replicas=5
# Scale based on current replicas
kubectl scale deployment nginx-deployment --current-replicas=3 --replicas=5
# Edit deployment to change replicas
kubectl edit deployment nginx-deployment
# Patch deployment
kubectl patch deployment nginx-deployment -p '{"spec":{"replicas":8}}'
Horizontal Pod Autoscaler (HPA)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: app-deployment
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
# Create HPA imperatively
kubectl autoscale deployment app-deployment --cpu-percent=70 --min=3 --max=10
# Get HPA status
kubectl get hpa
kubectl describe hpa app-hpa
# Watch HPA in action
kubectl get hpa -w
# Generate load to test HPA
kubectl run -it load-generator --rm --image=busybox --restart=Never -- /bin/sh
# Inside the pod:
while true; do wget -q -O- http://app-service; done
Rolling Updates & Rollbacks
Rolling Update Process
During a rolling update, Kubernetes gradually replaces old pods with new ones, ensuring zero downtime:
Step 1: Initial State
All 3 pods are running v1 of your application. The Deployment is stable.
Step 2: Creating New
A new v2 pod is created while 2 v1 pods still serve traffic. Readiness probes gate traffic.
Step 3: Replacing
Once v2 is ready, an old v1 pod terminates. Now 1 v1 + 2 v2 pods are running.
Step 4: Complete
All pods are now running v2. The old ReplicaSet is scaled to 0 but kept for rollback.
Performing Updates
# Update image
kubectl set image deployment/nginx-deployment nginx=nginx:1.22
# Update with record (for rollback)
kubectl set image deployment/nginx-deployment nginx=nginx:1.22 --record
# Update environment variable
kubectl set env deployment/nginx-deployment ENV=production
# Watch rollout status
kubectl rollout status deployment/nginx-deployment
# Pause rollout
kubectl rollout pause deployment/nginx-deployment
# Resume rollout
kubectl rollout resume deployment/nginx-deployment
# Check rollout history
kubectl rollout history deployment/nginx-deployment
# Rollback to previous version
kubectl rollout undo deployment/nginx-deployment
# Rollback to specific revision
kubectl rollout undo deployment/nginx-deployment --to-revision=2
# View specific revision
kubectl rollout history deployment/nginx-deployment --revision=3
Update Strategies Comparison
| Strategy | Description | Use Case | Downtime |
|---|---|---|---|
| RollingUpdate | Gradually replaces old pods with new ones | Most production deployments | Zero downtime |
| Recreate | Terminates all old pods before creating new ones | Dev environments, stateful apps | Brief downtime |
| Blue/Green | Switch between two identical environments | Critical apps, instant rollback needed | Zero downtime |
| Canary | Gradual rollout to subset of users | Risk mitigation, A/B testing | Zero downtime |
Common Update Pitfalls
Problem: Update stuck or failing
- Check image pull errors:
kubectl describe pods - Verify health checks are passing
- Ensure sufficient resources available
- Check for PodDisruptionBudget conflicts
Deployment Strategies
Rolling Update
Default strategy, gradually replaces pods. Zero downtime, configurable pace, automatic rollback on failure.
Blue/Green
Two complete environments running side-by-side. Instant switch, easy rollback, but resource intensive.
Canary
Gradual rollout to a subset of users first. Great for risk mitigation and real user testing. More complex setup.
Blue/Green Deployment Example
# Blue Deployment (Current)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-blue
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: app
image: myapp:v1.0
---
# Green Deployment (New)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-green
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: green
template:
metadata:
labels:
app: myapp
version: green
spec:
containers:
- name: app
image: myapp:v2.0
---
# Service pointing to active deployment
apiVersion: v1
kind: Service
metadata:
name: app-service
spec:
selector:
app: myapp
version: blue # Switch to 'green' to deploy
ports:
- port: 80
targetPort: 8080
Canary Deployment Example
# Step 1: Deploy stable version
kubectl apply -f stable-deployment.yaml
# Step 2: Deploy canary version (small replica count)
kubectl apply -f canary-deployment.yaml
# Step 3: Split traffic using Service
# Both deployments have same labels for Service selector
# Step 4: Monitor metrics
kubectl top pods -l app=myapp
# Step 5: Gradually increase canary replicas
kubectl scale deployment canary-deployment --replicas=2
# Step 6: If successful, replace stable with canary
kubectl set image deployment/stable-deployment app=myapp:v2.0
# Step 7: Remove canary deployment
kubectl delete deployment canary-deployment
Practice Problems
Easy Create a Production Deployment
Create a production-ready deployment with 5 replicas, rolling update strategy, resource limits, health checks, and pod anti-affinity.
Use spec.strategy.type: RollingUpdate with maxSurge and maxUnavailable. Add livenessProbe and readinessProbe under the container spec. Use podAntiAffinity to spread pods across nodes.
apiVersion: apps/v1
kind: Deployment
metadata:
name: production-app
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: production-app
template:
metadata:
labels:
app: production-app
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- production-app
topologyKey: kubernetes.io/hostname
containers:
- name: app
image: nginx:1.21
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
Medium Implement Blue/Green Deployment
Set up a blue/green deployment: create blue (v1) and green (v2) deployments, a service pointing to blue, then switch to green and verify.
Use different label values (version: blue vs version: green) to distinguish deployments. The Service selector determines which set receives traffic. Use kubectl patch to switch.
# Create blue deployment
kubectl create deployment app-blue --image=nginx:1.20 --replicas=3
kubectl label deployment app-blue version=blue
# Create green deployment
kubectl create deployment app-green --image=nginx:1.21 --replicas=3
kubectl label deployment app-green version=green
# Create service
kubectl expose deployment app-blue --port=80 --name=app-service
# Switch to green
kubectl patch service app-service -p '{"spec":{"selector":{"version":"green"}}}'
# Verify
kubectl get endpoints app-service
# Rollback to blue if needed
kubectl patch service app-service -p '{"spec":{"selector":{"version":"blue"}}}'
Medium Configure Autoscaling
Set up HPA with custom metrics: create a deployment with resource requests, configure CPU/memory-based autoscaling, generate load, and monitor scaling behavior.
You must set resource requests on the deployment before HPA can measure utilization. Use kubectl autoscale or apply an HPA YAML. Generate load with a busybox pod.
# Create deployment
kubectl create deployment autoscale-app --image=nginx
kubectl set resources deployment autoscale-app \
--requests=cpu=100m,memory=128Mi
# Create HPA
kubectl autoscale deployment autoscale-app \
--cpu-percent=50 \
--min=2 \
--max=10
# Generate load
kubectl run -it load-generator --rm --image=busybox \
--restart=Never -- \
sh -c "while true; do wget -q -O- http://autoscale-app; done"
# Monitor HPA
watch kubectl get hpa
# Check metrics
kubectl top pods -l app=autoscale-app
Hard Zero-Downtime Update Challenge
Perform a zero-downtime update that does not affect active connections. Implement a PodDisruptionBudget, use preStop hooks for graceful shutdown, and monitor the rollout with rollback readiness.
A PodDisruptionBudget ensures a minimum number of pods remain available. The preStop hook gives in-flight requests time to complete before the container exits. Combine with readiness probes for a fully graceful rollout.
# PodDisruptionBudget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: app-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: myapp
---
# Deployment with preStop hook
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
terminationGracePeriodSeconds: 60
containers:
- name: app
image: myapp:v2.0
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 15"]
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
Pro Tip
Start with basic deployments and work your way up to advanced strategies. Most production issues come from missing health checks and insufficient resource limits - nail those first before worrying about canary deployments!