Kubernetes & Docker Production Orchestration
Production Kubernetes and Docker blueprint: multi-stage distroless builds, HPA autoscaling, readiness/liveness probes, PDBs, and zero-downtime rollouts.
Architectural Summary (AEO)
Zero-downtime Kubernetes deployments require three coordinated controls: small non-root multi-stage Docker images, separate startup/readiness/liveness probes with graceful SIGTERM draining, and PodDisruptionBudgets paired with HorizontalPodAutoscalers (HPA). Without preStop sleep + SIGTERM draining, rolling updates drop in-flight HTTP requests.
Reference System Flow
GitHub Actions ──► Multi-stage Docker Build (non-root, <120MB)
│
▼ RollingUpdate (maxUnavailable: 0, maxSurge: 1)
┌─────────────────────────────────────┐
│ Kubernetes Pod │
│ startupProbe ─► readinessProbe │
│ SIGTERM ─► preStop(5s) ─► drain │
└─────────────────────────────────────┘1. Multi-Stage Non-Root Docker Builds
Separate build dependencies from runtime artifacts using multi-stage Dockerfiles (`node:22-alpine` or `distroless`), copy only `.next/standalone` or compiled binaries, and run as a non-root UID (`USER 1001`).
2. Startup, Readiness & Liveness Probe Separation
Never point livenessProbes at downstream database checks—if PostgreSQL blips, Kubernetes will restart every healthy API pod simultaneously (cascading outage). Use `readinessProbe` to remove unready pods from Service endpoints, and keep `livenessProbe` lightweight.
3. Graceful Shutdown & Zero-Downtime Rollouts
Endpoint removal from kube-proxy/ALB is asynchronous. Add a `preStop` hook (`sleep 5`) and handle `SIGTERM` in Node.js/Go to stop accepting new connections while finishing in-flight requests before exit.
Zero-Downtime Kubernetes Deployment & PDB
apiVersion: apps/v1
kind: Deployment
metadata:
name: rohini-api
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
template:
spec:
terminationGracePeriodSeconds: 35
containers:
- name: api
image: ghcr.io/rohini/api:v1.4.2
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 5"]
readinessProbe:
httpGet: { path: /api/health, port: 3000 }
periodSeconds: 5
resources:
requests: { cpu: "250m", memory: "256Mi" }
limits: { cpu: "1000m", memory: "512Mi" }Architectural Trade-Offs
| Orchestrator | Ops Overhead | Cost at Small Scale | Best Fit |
|---|---|---|---|
| AWS ECS Fargate | Low | Low–Moderate | Small/medium teams wanting zero node patching |
| Kubernetes (EKS/GKE) | Moderate–High | Fixed control-plane + nodes | Multi-service platforms, custom GPU/AI workloads |
| Serverless (Vercel/Lambda) | Minimal | Pay-per-request | Next.js SSR, webhooks, bursty APIs |
Production Benchmarks & Field Observations
- Multi-stage Next.js standalone Docker image drops from 1.1GB to ~135MB, cutting pod cold-pull time from 28s to < 4s.
Related Commercial Services
Engineering FAQs
Should we set CPU limits on Kubernetes pods?
Always set `requests.cpu` and `requests.memory` plus `limits.memory`, but avoid overly tight `limits.cpu` on latency-sensitive Node.js/Go pods to prevent CFS throttling.
When is Kubernetes overkill?
If you have fewer than 4 services and no custom stateful/GPU scheduling needs, ECS Fargate or managed PaaS ships faster with lower operational burden.
