What is a Deployment?
You rarely create Pods or ReplicaSets directly. Use a Deployment — it manages a ReplicaSet, which maintains the desired number of Pod replicas:
Deployment → ReplicaSet → Pods
A Deployment adds everything the ReplicaSet lacks: rolling updates, rollbacks, revision history, and pause/resume — the change-management layer that makes shipping new versions safe.
Deployment YAML
# deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # can have 1 extra pod during update
maxUnavailable: 0 # never have fewer than desired
template:
metadata:
labels:
app: my-app
version: "2.1.0"
spec:
containers:
- name: app
image: my-org/my-app:2.1.0
ports:
- containerPort: 3000
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5The replicas, selector, and template fields work exactly as they did in the ReplicaSet lesson — the Deployment passes them through to the ReplicaSet it manages. What's new is strategy, which controls how Pods are replaced during an update: maxSurge: 1, maxUnavailable: 0 means "create one new Pod at a time, and never drop below the desired count" — a zero-downtime rollout.
Health Checks
| Probe | Purpose |
|---|---|
| livenessProbe | Is the app alive? Restart the container if it fails. |
| readinessProbe | Is the app ready to serve traffic? Remove from Service endpoints if it fails. |
| startupProbe | Is the app done starting? Delays liveness checks for slow-starting apps. |
# All three probe types
livenessProbe:
httpGet:
path: /healthz
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
startupProbe:
httpGet:
path: /healthz
port: 3000
failureThreshold: 30 # 30 × 10s = 5 minutes for startup
periodSeconds: 10Deployment Operations
# Apply deployment
kubectl apply -f deployment.yml
# Check rollout status
kubectl rollout status deployment/my-app
# View rollout history
kubectl rollout history deployment/my-app
# Update image (triggers rolling update)
kubectl set image deployment/my-app app=my-org/my-app:2.2.0
# Rollback to previous version
kubectl rollout undo deployment/my-app
# Rollback to specific revision
kubectl rollout undo deployment/my-app --to-revision=2
# Scale manually
kubectl scale deployment/my-app --replicas=5
# Pause / resume a rollout
kubectl rollout pause deployment/my-app
kubectl rollout resume deployment/my-appDuring a rolling update, the Deployment creates a new ReplicaSet for the new version and gradually shifts Pods from old to new:
Old Old Old → New Old Old → New New Old → New New New
The old ReplicaSet is kept (scaled to zero) as revision history — which is exactly what makes kubectl rollout undo instant.
Autoscaling with HPA
Manual kubectl scale works, but production workloads scale themselves. A Horizontal Pod Autoscaler adjusts a Deployment's replica count based on CPU or memory utilization:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80kubectl get hpa
kubectl describe hpa my-app-hpaConfigMaps and Secrets
Configuration shouldn't live inside the image — the same application image should run in dev and production with only its config changing. ConfigMaps hold plain configuration; Secrets hold sensitive values:
# configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_PORT: "3000"
LOG_LEVEL: "info"
ALLOWED_ORIGINS: "https://example.com"# secret.yml
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData: # auto base64-encoded
db-password: "s3cur3P@ss!"
jwt-secret: "my-jwt-secret-key"# Reference in deployment
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secretsSet resources.requests and resources.limits for every container. Without requests, the scheduler can't make intelligent placement decisions. Without limits, a runaway container can starve other pods on the same node.