Kubernetes Deployment
The Heart of Stateless Apps
Start here if you are new! In the world of Kubernetes, managing one container is easy. But what if you need to run 100 copies of the same application? And what if one crashes? Do you want to restart it manually? No, right?
That is where a Deployment comes in.
Basically, a Deployment is a “supervisor” for your pods (containers). You just tell the Deployment, “I want 3 copies of my Nginx app running at all times,” and it will make sure exactly 3 are running. If one crashes, it replaces it. If you want to update the app from version 1 to version 2, the Deployment handles the update smoothly without stopping the whole system.
Think of a Kubernetes Deployment like a Construction Project Manager.
- The Deployment (Project Manager): You give them the blueprint (YAML file). You say, “I want 5 walls built.” The Manager doesn’t build the walls themselves; they hire a contractor.
- The ReplicaSet (Contractor/Team Lead): The Deployment hires a ReplicaSet. The ReplicaSet’s only job is to count. It ensures there are exactly 5 workers present. If a worker falls sick (Pod crash), the ReplicaSet hires a new one immediately.
- The Pod (Worker): These are the actual workers doing the job (running your application).
You talk to the Manager (Deployment). The Manager talks to the Team Lead (ReplicaSet). The Team Lead manages the Workers (Pods).
Deployment > ReplicaSet > Pod
Key Characteristics to Remember
| Feature | Description | Cheat Sheet Sentence |
| Declarative | You define “what” you want, not “how” to do it. | “I want 3 pods” (K8s figures out the rest). |
| Self-Healing | Automatically replaces failed pods. | “If it breaks, K8s fixes it.” |
| Scaling | Increases or decreases the number of replicas. | “Traffic up? Scale up. Traffic down? Scale down.” |
| Rolling Updates | Updates pods one by one (or in batches) to avoid downtime. | “Update without the user noticing.” |
| Rollback | Reverts to a previous version if the new one fails. | “Ctrl+Z for your infrastructure.” |
| Revision History | Keeps a record of old ReplicaSets. | “K8s remembers your past versions.” |
–
When you create a Deployment in Kubernetes, a chain reaction happens in the control plane. It is not magic; it is logic.
- Submission: You submit the Deployment YAML to the API Server.
- Controller Watch: The Deployment Controller (part of
kube-controller-manager) sees this new request. - ReplicaSet Creation: The Deployment Controller creates a ReplicaSet. It hands over the responsibility of managing Pods to this ReplicaSet.
- Pod Creation: The ReplicaSet notices that the current number of Pods is 0, but the desired number is (for example) 3. It requests the API server to create 3 Pods.
- Scheduling: The Scheduler assigns these new Pods to worker nodes.
- Kubelet Action: The Kubelet on the worker nodes sees the assignment and starts the containers.
Why this layer? Why not just manage Pods directly? Because Pods are “mortal.” They die and don’t come back. Deployments make your application “immortal” (stateless).
–
DevSecOps Architect Level
At the Architect level, a simple Deployment is not enough. You need Progressive Delivery and High Availability.
- Rolling Update: By default, a Kubernetes deployment uses the Rolling Update deployment model.
- Max Unavailable (25%): This means “At worst, only 25% of my pods can be down.” So, if you have 100 pods, at least 75 must be running and happy at all times.
- Max Surge (25%): This means “At peak, I can have 25% extra pods.” So, if you want 100 pods, Kubernetes is allowed to temporarily create up to 125 pods during the update to make things faster.
- https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy
We can use more advanced Deployment models in Kubernetes Deployment.
Use Cases
- Web Servers (Nginx/Apache): Perfect for Deployments.
- Microservices (Spring Boot/Node.js): Standard component.
- Workers: Background job processors.
Benefits
- Stateless management: You don’t care about individual pods.
- Version Control: You can
kubectl rollout undoto go back in time. - Automation: Integrates perfectly with CI/CD pipelines.
Technical Challenges, Limitations, Common Issues
| Problem | Symptom | Solution |
| CrashLoopBackOff | Pod starts, crashes, restarts, repeats. | Check logs (kubectl logs). Usually a code error or missing config. |
| ImagePullBackOff | K8s cannot download the Docker image. | Check image name, tag, or image pull secrets (credentials). |
| Pending State | Pods sit in “Pending” forever. | Cluster is full (no CPU/RAM left) or Taints/Tolerations mismatch. |
| Config Drift | Someone changed the deployment manually via kubectl edit. | Use GitOps (ArgoCD) to force the cluster to match the Git repo. |
| Native Canary Limitation | Can’t do traffic % splitting natively. | Use Argo Rollouts or a Service Mesh (Istio). |
https://kubernetes.io/docs/concepts/workloads/controllers/deployment
Example of Deployment.yaml file
apiVersion: apps/v1 # The API version for Deployments
kind: Deployment # The type of resource we are creating
metadata:
name: backend-api # Name of the deployment (keep it simple)
namespace: production # logical isolation (like a folder)
labels: # Tags to organize resources
app: backend-api
env: production
tier: backend
annotations: # Metadata for tools (not used by K8s core)
kubernetes.io/change-cause: "Upgraded to v2.4 for security patch"
spec:
replicas: 3 # if you didn't specify this, it would default to 1, which is a single point of failure!
revisionHistoryLimit: 5 # Keep last 5 versions for rollback (default is 10)
# ---------------------------------------------------------
# UPDATE STRATEGY: ZERO DOWNTIME CONFIGURATION
# ---------------------------------------------------------
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0 # Safety First! Never kill a pod until a new one is ready.
maxSurge: 1 # Speed! Create 1 extra pod, then kill 1 old pod.
# ---------------------------------------------------------
# SELECTOR: CONNECTS DEPLOYMENT TO PODS
# ---------------------------------------------------------
selector:
matchLabels:
app: backend-api # MUST match the template labels below
# ---------------------------------------------------------
# TEMPLATE: THE BLUEPRINT FOR THE PODS
# ---------------------------------------------------------
template:
metadata:
labels:
app: backend-api # The label the Service looks for
spec:
serviceAccountName: backend-sa # Custom permission for this pod (Concept of Least Privilege)
# -------------------------------------------------------
# SECURITY CONTEXT (POD LEVEL): DevSecOps Best Practice
# -------------------------------------------------------
securityContext:
runAsUser: 1000 # Run as a standard user (UID 1000)
runAsGroup: 3000 # Run as a standard group
fsGroup: 2000 # File system group ownership
containers:
- name: api-container
image: my-registry/backend-api:v2.4
imagePullPolicy: IfNotPresent # Don't download again if we have it (saves bandwidth)
# -----------------------------------------------------
# PORTS
# -----------------------------------------------------
ports:
- containerPort: 8080 # Port the app listens on inside the container
name: http
protocol: TCP
# -----------------------------------------------------
# RESOURCES: PREVENT "NOISY NEIGHBORS"
# -----------------------------------------------------
resources:
requests: # Minimum required to start
memory: "128Mi"
cpu: "250m" # 250 millicores (1/4 of a CPU)
limits: # Maximum allowed (prevents crashing the node)
memory: "512Mi"
cpu: "500m"
# -----------------------------------------------------
# PROBES: SELF-HEALING & TRAFFIC CONTROL
# -----------------------------------------------------
livenessProbe: # "Are you alive?" (Restarts pod if fails)
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15 # Give app 15s to boot before checking
periodSeconds: 20 # Check every 20s
readinessProbe: # "Can you take traffic?" (Stops traffic if fails)
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
startupProbe: # "Are you started?" (Good for slow Java/Legacy apps)
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # Try 30 times before giving up
periodSeconds: 10
# -----------------------------------------------------
# ENVIRONMENT VARIABLES
# -----------------------------------------------------
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config # Read from a ConfigMap (don't hardcode!)
key: db-host
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets # Read from a Secret (DevSecOps mandatory!)
key: db-password
# -----------------------------------------------------
# SECURITY CONTEXT (CONTAINER LEVEL)
# -----------------------------------------------------
securityContext:
allowPrivilegeEscalation: false # Prevent sudo/root escalation
readOnlyRootFilesystem: true # Hacker cannot write to file system
# -------------------------------------------------------
# AFFINITY: HIGH AVAILABILITY SCHEDULING
# -------------------------------------------------------
affinity:
podAntiAffinity: # "Don't put me next to myself"
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- backend-api
topologyKey: "kubernetes.io/hostname" # Spread across different nodes
# -------------------------------------------------------
# GRACEFUL SHUTDOWN
# -------------------------------------------------------
terminationGracePeriodSeconds: 60 # Wait 60s for connections to close before killingPractice lab will be added soon.