Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

Archive ยท Kubernetes

Pods and Deployments

Write a production-shaped Deployment: labels, rolling updates, resources, security context, scaling, rollouts and rollbacks, plus how pods really behave.

3 min read

  • Kubernetes 1.21
  • apps/v1 Deployment
  • ReplicaSet
  • kubectl rollout

A Pod is one or more containers that share a network namespace and storage; it is the smallest thing Kubernetes schedules. Pods are mortal: they are not restarted on another node when their node dies. You almost never create bare pods. A Deployment declares how many identical pods you want and manages updates, using a ReplicaSet underneath.

Versions in use on this date. Kubernetes 1.21 (released 8 April 2021). apps/v1 is the stable Deployment API; the older extensions/v1beta1 and apps/v1beta1/2 versions are long gone.

Ownership chain

flowchart TB
  D[Deployment web<br/>desired state and strategy] --> RS1[ReplicaSet web-7d9f<br/>current version]
  D -.->|kept for rollback| RS0[ReplicaSet web-5b8c<br/>previous version scaled to 0]
  RS1 --> P1[Pod web-7d9f-aaa]
  RS1 --> P2[Pod web-7d9f-bbb]
  RS1 --> P3[Pod web-7d9f-ccc]
  SVC[Service web<br/>selector app=web] --> P1
  SVC --> P2
  SVC --> P3
  HPA[HorizontalPodAutoscaler] -.->|changes replicas| D

The Service finds pods through labels, not names, so the labels in the pod template and the Service selector must match.

A realistic Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: shop
  labels: { app: web }
spec:
  replicas: 3
  revisionHistoryLimit: 5
  selector:
    matchLabels: { app: web }          # immutable after creation
  strategy:
    type: RollingUpdate
    rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }
  template:
    metadata:
      labels: { app: web, version: "1.4.2" }
    spec:
      terminationGracePeriodSeconds: 30
      containers:
        - name: web
          image: registry.example.com/shop/web:1.4.2     # never :latest
          ports: [{ containerPort: 8080, name: http }]
          env:
            - name: LOG_LEVEL
              value: info
          resources:
            requests: { cpu: 100m, memory: 128Mi }
            limits: { memory: 256Mi }
          readinessProbe:
            httpGet: { path: /healthz, port: http }
            periodSeconds: 5
          securityContext:
            runAsNonRoot: true
            runAsUser: 10001
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities: { drop: ["ALL"] }

Points worth explaining:

  • selector and template labels tie the Deployment to its pods; you cannot change the selector later.
  • maxSurge: 1, maxUnavailable: 0 replaces pods one at a time without reducing capacity: a new pod must become Ready before an old one is removed.
  • Requests are what the scheduler reserves; limits are caps. Memory limits kill the container when exceeded (OOMKilled); CPU limits throttle.
  • Readiness probe: the pod receives Service traffic only when ready (the probes post goes deeper).
  • securityContext: run as non-root, no privilege escalation, read-only root filesystem. These are cheap and prevent many attacks; PodSecurityPolicy is being deprecated, so apply them in the manifest and plan for Pod Security Admission later.

Operate it

kubectl create namespace shop
kubectl apply -f deployment.yaml
kubectl -n shop get deploy,rs,pods -l app=web -o wide
kubectl -n shop rollout status deployment/web
kubectl -n shop scale deployment/web --replicas=5

# update the image (or edit the manifest and re-apply)
kubectl -n shop set image deployment/web web=registry.example.com/shop/web:1.4.3
kubectl -n shop rollout status deployment/web
kubectl -n shop rollout history deployment/web
kubectl -n shop rollout undo deployment/web             # back to the previous revision
kubectl -n shop rollout restart deployment/web          # fresh pods with the same spec
kubectl -n shop rollout pause deployment/web            # batch several changes, then resume

Prefer committing the manifest to Git and applying it from CI over using set image by hand, so the repository stays the source of truth (kubectl diff -f previews changes).

How pods behave

  • A pod gets its own IP, but the IP changes on every recreation. Use Services for stable access.
  • Containers in a pod share localhost, which is why sidecars (log shippers, proxies) work.
  • Termination: Kubernetes sends SIGTERM, waits terminationGracePeriodSeconds, then SIGKILL. Handle SIGTERM and finish in-flight requests. A preStop sleep of a few seconds avoids dropping traffic while endpoints update.
  • A pod that never becomes Ready blocks the rollout; kubectl describe pod shows why.
  • restartPolicy: Always (the default for Deployments) restarts containers on the same node with exponential back-off (CrashLoopBackOff).

Troubleshooting a rollout

kubectl -n shop describe deployment web | sed -n '/Conditions/,$p'
kubectl -n shop get pods -l app=web
kubectl -n shop logs deployment/web --tail=50
kubectl -n shop get events --sort-by=.lastTimestamp | tail

Stuck rollouts usually mean a failing readiness probe, an image that cannot be pulled, or requests larger than any node can provide. Set progressDeadlineSeconds (default 600) so CI fails clearly, and use kubectl rollout undo to recover quickly.