What is a Kubernetes Deployment?
When most engineers say "I deployed my app to Kubernetes," what they actually created is a Deployment. The Deployment is the primary workload resource in Kubernetes - it is how you tell the cluster what container to run, how many copies to run, and how to handle updates to it.
Understanding the Deployment object means understanding the three-layer hierarchy it sits in: the Deployment manages ReplicaSets, and ReplicaSets manage Pods. Each layer has a specific responsibility.
The Pod, ReplicaSet, Deployment Hierarchy
A Pod is the smallest deployable unit in Kubernetes. It wraps one or more containers that share a network namespace and storage volumes. Pods are ephemeral - they can be killed and replaced at any time.
A ReplicaSet ensures that a specific number of identical Pod replicas are running at all times. If a pod dies, the ReplicaSet creates a replacement. You rarely create ReplicaSets directly.
A Deployment manages ReplicaSets and adds the ability to do rolling updates and rollbacks. When you update a Deployment (changing the container image, for example), Kubernetes creates a new ReplicaSet with the new configuration and gradually scales it up while scaling down the old one. The old ReplicaSet is kept around with zero replicas so rollback is instant.
A Production-Grade Deployment YAML
Here is a complete Deployment manifest with resource limits, health probes, and proper labels - the kind you would actually use in production:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
namespace: production
labels:
app: api-server
version: v2.1.0
team: platform
spec:
replicas: 3
selector:
matchLabels:
app: api-server
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # allow 1 extra pod during update (4 total)
maxUnavailable: 0 # never have fewer than 3 healthy pods
template:
metadata:
labels:
app: api-server
version: v2.1.0
spec:
containers:
- name: api-server
image: myregistry/api-server:v2.1.0
ports:
- containerPort: 3000
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
readinessProbe:
httpGet:
path: /health/ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
livenessProbe:
httpGet:
path: /health/live
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
failureThreshold: 3
env:
- name: APP_ENV
value: production
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: api-secrets
key: database-url
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api-server
A few things worth calling out in this manifest:
Resource requests and limits: requests tells the scheduler how much CPU and memory to reserve for this pod on a node. limits is the hard cap - if the container tries to use more memory than the limit, it is OOMKilled. Always set both in production; a Deployment without resource limits can starve other workloads on the same node.
maxSurge and maxUnavailable: With maxSurge: 1 and maxUnavailable: 0, a rolling update always maintains at least three healthy pods and brings up one extra pod before terminating any old ones. This achieves zero-downtime deploys.
topologySpreadConstraints: This ensures pods are distributed across different nodes. Without this, Kubernetes might schedule all three replicas on the same node, defeating the purpose of having multiple replicas.
Readiness vs Liveness Probes
These are two of the most important configurations in any production Deployment, and they serve different purposes.
The readiness probe answers: "is this pod ready to receive traffic?" If the readiness probe fails, Kubernetes removes the pod from the Service's endpoint list. Traffic stops going to it. The pod is not restarted. This is ideal for handling slow startup times - the pod stays alive but does not receive traffic until it is fully initialised.
The liveness probe answers: "is this pod alive?" If the liveness probe fails repeatedly, Kubernetes restarts the pod. Use this to detect deadlocks or infinite loops that cause the process to hang without crashing.
A common mistake is using the same endpoint for both probes. Your readiness probe endpoint should check that external dependencies (database connection, cache) are reachable. Your liveness probe endpoint should only check that the process itself is responsive.
Updating and Rolling Back
To update a Deployment, change the image tag and apply:
# Update the image and record the change in rollout history
kubectl set image deployment/api-server \
api-server=myregistry/api-server:v2.2.0 \
--record
# Watch the rollout progress
kubectl rollout status deployment/api-server
# If something goes wrong, roll back to the previous version
kubectl rollout undo deployment/api-server
# Check rollout history for all revisions
kubectl rollout history deployment/api-server
The --record flag annotates the ReplicaSet with the command that triggered it, making it easier to identify which revision corresponds to which change in the rollout history.
