Kubernetes Deep Dive · Part 2

The Operational Patterns Kubernetes Won't Teach You, but Production Will

Tisighe Livinstone

Tisighe Livinstone

18 March 2026·4 min read
The Operational Patterns Kubernetes Won't Teach You, but Production Will

These are the specific things I wish someone had put in front of me before I learned them through production incidents. None of this is secret knowledge. But there's a difference between reading about something and understanding when it matters — and that only comes from experience.

Pattern 1: Graceful Shutdown

Here's what happens when Kubernetes terminates a pod: it sends SIGTERM, waits 30 seconds, then sends SIGKILL. If your application doesn't handle SIGTERM, it gets forcibly killed mid-request. Every deployment becomes a reliability event.

There is also a timing gap. Kubernetes removes the pod from the load balancer endpoints and sends SIGTERM — but there's propagation delay between those two events. During that window, traffic might still reach the pod. A preStop sleep of 5 seconds covers it:

lifecycle:
  preStop:
    exec:
      command: ["/bin/sleep", "5"]
terminationGracePeriodSeconds: 30
// Catch SIGTERM and drain requests cleanly
process.on('SIGTERM', async () => {
  server.close(async () => {
    await db.disconnect()
    process.exit(0)
  })
  // Force exit 5 seconds before SIGKILL
  setTimeout(() => process.exit(1), 25000)
})

Pattern 2: PodDisruptionBudgets

I learned this one the hard way. We ran a node upgrade and all three replicas of a critical service were evicted simultaneously. The service went down completely. A PodDisruptionBudget tells Kubernetes the minimum number of pods that must remain available during voluntary disruptions like upgrades and node drains.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: myapp-pdb
  namespace: production
spec:
  minAvailable: 2   # Always keep at least 2 pods running
  selector:
    matchLabels:
      app: myapp
# Verify PDB is applied
kubectl get pdb -n production

# NAME        MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS
# myapp-pdb   2               N/A               1

Add a PDB to every production workload. It takes five minutes and prevents an entire class of incidents.

Pattern 3: Pod Anti-Affinity

Three replicas on the same node is not redundancy — it's a single point of failure with extra steps. Use pod anti-affinity to spread replicas across nodes:

affinity:
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
      podAffinityTerm:
        labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - myapp
        topologyKey: kubernetes.io/hostname

Use preferred rather than required. The required variant blocks scheduling when there aren't enough nodes with different hostnames, which trades one problem for another.

# Verify pods are on different nodes after applying
kubectl get pods -n production -o wide
# NAME          NODE
# myapp-abc     ip-10-0-1-xx...  ← different nodes
# myapp-def     ip-10-0-2-xx...
# myapp-ghi     ip-10-0-3-xx...

Pattern 4: Resource Quotas Stop Runaway Processes

Without quotas, one team running a load test can starve other services of resources. I've seen it happen. Add a quota per namespace:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "8"
    requests.memory: "16Gi"
    limits.cpu: "16"
    limits.memory: "32Gi"
    pods: "50"

Pair with a LimitRange so pods without resource specs get sensible defaults instead of BestEffort QoS:

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: team-a
spec:
  limits:
  - type: Container
    defaultRequest:
      cpu: "100m"
      memory: "128Mi"
    default:
      cpu: "500m"
      memory: "256Mi"
kubectl apply -f quota.yaml
kubectl apply -f limitrange.yaml

# Verify
kubectl describe resourcequota team-quota -n team-a
kubectl describe limitrange default-limits -n team-a

The Daily kubectl Commands

These become muscle memory:

# Why is this pod not running?
kubectl describe pod POD_NAME -n NAMESPACE

# What did it say before it crashed?
kubectl logs POD_NAME -n NAMESPACE --previous

# How much is it actually using?
kubectl top pods -n NAMESPACE --sort-by=memory

# What happened recently in this namespace?
kubectl get events -n NAMESPACE --sort-by=.lastTimestamp | tail -20

# Is my change safe? (dry run without applying)
kubectl apply -f manifest.yaml --dry-run=client

# Quickly see all non-running pods
kubectl get pods -A | grep -v Running | grep -v Completed

Understanding why each of these patterns matters is covered in Kubernetes Pod Lifecycle — specifically why graceful shutdown requires both the application and the preStop hook, and what the SIGTERM timing actually looks like.

Tisighe Livinstone

Tisighe Livinstone

Cloud & DevOps Engineer

Writing about Kubernetes, Terraform, GitOps, observability and cloud security. Mostly from systems IBased on things I've actually built and broken in production.apos;ve built and run myself.

Comments

All comments are reviewed before appearing.

Leave a Comment