Skip to main content
< All Topics

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
FeatureDescriptionCheat Sheet Sentence
DeclarativeYou define “what” you want, not “how” to do it.“I want 3 pods” (K8s figures out the rest).
Self-HealingAutomatically replaces failed pods.“If it breaks, K8s fixes it.”
ScalingIncreases or decreases the number of replicas.“Traffic up? Scale up. Traffic down? Scale down.”
Rolling UpdatesUpdates pods one by one (or in batches) to avoid downtime.“Update without the user noticing.”
RollbackReverts to a previous version if the new one fails.“Ctrl+Z for your infrastructure.”
Revision HistoryKeeps 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.

  1. Submission: You submit the Deployment YAML to the API Server.
  2. Controller Watch: The Deployment Controller (part of kube-controller-manager) sees this new request.
  3. ReplicaSet Creation: The Deployment Controller creates a ReplicaSet. It hands over the responsibility of managing Pods to this ReplicaSet.
  4. 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.
  5. Scheduling: The Scheduler assigns these new Pods to worker nodes.
  6. 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.

  1. 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 undo to go back in time.
  • Automation: Integrates perfectly with CI/CD pipelines.
Technical Challenges, Limitations, Common Issues
ProblemSymptomSolution
CrashLoopBackOffPod starts, crashes, restarts, repeats.Check logs (kubectl logs). Usually a code error or missing config.
ImagePullBackOffK8s cannot download the Docker image.Check image name, tag, or image pull secrets (credentials).
Pending StatePods sit in “Pending” forever.Cluster is full (no CPU/RAM left) or Taints/Tolerations mismatch.
Config DriftSomeone changed the deployment manually via kubectl edit.Use GitOps (ArgoCD) to force the cluster to match the Git repo.
Native Canary LimitationCan’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

YAML
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 killing

Practice lab will be added soon.

Contents
Scroll to Top