Kubernetes and Container Orchestration Guide
A practical introduction to Kubernetes for development teams ready to move beyond manual container management to production-grade orchestration.
Why Containers Changed Everything
Before containers, deploying an application meant configuring a server, installing dependencies, managing conflicts between application versions, and hoping that what worked on the developer’s laptop would work in production. Containers solved this by packaging the application, its dependencies, and its runtime environment into a single portable unit.
Docker made containers accessible. But running a few containers on a single server is simple. Running dozens or hundreds of containers across multiple servers, keeping them healthy, scaling them based on demand, and updating them without downtime requires orchestration. That is where Kubernetes comes in.
Kubernetes Architecture
The Control Plane
Kubernetes operates on a master-worker model. The control plane manages the cluster:
- API Server: The front door for all management commands. Every
kubectlcommand talks to this component. - etcd: A distributed key-value store that holds all cluster state. If etcd loses data, the cluster loses its memory.
- Scheduler: Decides which worker node should run a new pod based on resource availability, affinity rules, and constraints.
- Controller Manager: Runs background loops that ensure the actual state matches the desired state. If you request three replicas and one dies, the controller creates a replacement.
Worker Nodes
Each worker node runs:
- kubelet: An agent that receives instructions from the control plane and manages pods on the node
- Container runtime: The software that actually runs containers (containerd, CRI-O)
- kube-proxy: Handles network rules so pods can communicate with each other and with external clients
The Pod
The pod is the smallest deployable unit in Kubernetes. A pod contains one or more containers that share networking and storage:
apiVersion: v1
kind: Pod
metadata:
name: web-app
labels:
app: web
tier: frontend
spec:
containers:
- name: app
image: registry.example.com/web-app:1.4.2
ports:
- containerPort: 3000
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 15
Always set resource requests and limits. Without them, a single misbehaving pod can consume all node resources and starve everything else.
Essential Kubernetes Objects
Deployments
You rarely create pods directly. Instead, you create a Deployment that manages a set of identical pods:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: app
image: registry.example.com/web-app:1.4.2
ports:
- containerPort: 3000
The Deployment ensures three replicas are always running. When you update the image version, it performs a rolling update: bringing up new pods before terminating old ones. The maxUnavailable: 0 setting means zero downtime during updates.
Services
Pods get random IP addresses that change when they restart. A Service provides a stable network endpoint:
apiVersion: v1
kind: Service
metadata:
name: web-app-service
spec:
type: ClusterIP
selector:
app: web
ports:
- port: 80
targetPort: 3000
Service types include:
- ClusterIP: Internal only. Other pods in the cluster can reach it.
- NodePort: Exposes the service on each node’s IP at a static port.
- LoadBalancer: Creates an external load balancer (works with cloud providers).
ConfigMaps and Secrets
Externalize configuration from your container images:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_HOST: "postgres-service"
LOG_LEVEL: "info"
CACHE_TTL: "300"
Secrets work similarly but base64-encode the values and can be encrypted at rest:
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
data:
DATABASE_PASSWORD: cGFzc3dvcmQxMjM=
Never commit Secret manifests with real values to version control. Use external secret management tools like Sealed Secrets or integrate with your cloud provider’s secret manager.
Scaling and Auto-scaling
Horizontal Pod Autoscaler
Scale pods based on CPU utilization, memory usage, or custom metrics:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
This maintains two to ten replicas, adding pods when average CPU exceeds 70% and removing them when it drops.
Cluster Autoscaler
When pods cannot be scheduled because no node has sufficient resources, the Cluster Autoscaler provisions additional nodes from your cloud provider. When nodes are underutilized, it drains and removes them.
Networking
Ingress Controllers
An Ingress defines external access to services, typically HTTP/HTTPS routing:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app-service
port:
number: 80
Popular ingress controllers include NGINX, Traefik, and cloud-native options like AWS ALB Ingress Controller.
Network Policies
By default, every pod can communicate with every other pod. Network Policies restrict this:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-policy
spec:
podSelector:
matchLabels:
app: api
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- port: 8080
This allows only pods labeled app: web to connect to the API pods on port 8080. All other traffic is denied.
Observability
The Three Pillars
Running containers in production requires visibility into what is happening:
- Logging: Aggregate logs from all pods into a centralized system (ELK stack, Grafana Loki)
- Metrics: Collect CPU, memory, request rates, and error rates (Prometheus + Grafana)
- Tracing: Follow requests across multiple services (Jaeger, Zipkin)
Health Checks
Kubernetes uses probes to determine pod health:
- Liveness probe: Is the container still running? If it fails, Kubernetes restarts the container.
- Readiness probe: Is the container ready to receive traffic? If it fails, the pod is removed from the service endpoint.
- Startup probe: Has the container finished starting up? Useful for slow-starting applications.
Configure all three for production workloads. A pod without health checks is a pod that Kubernetes cannot self-heal.
Production Readiness Checklist
Before running workloads in production:
- Resource requests and limits set for all containers
- Liveness and readiness probes configured
- Pod Disruption Budgets defined for critical services
- Network Policies restricting unnecessary communication
- Secrets managed externally, not in plain text manifests
- Logging, metrics, and alerting in place
- Horizontal Pod Autoscaler configured for variable workloads
- Namespace isolation between environments (dev, staging, production)
- RBAC policies limiting who can do what in each namespace
- Backup and disaster recovery procedures tested
Getting Started
Start small. Containerize one application, deploy it to a managed Kubernetes service (GKE, EKS, AKS, or a local cluster with k3s), and learn the workflow. Add complexity incrementally: services, ingress, autoscaling, monitoring.
Kubernetes has a steep learning curve, but the operational benefits for production workloads are substantial. Automated healing, rolling updates, scaling, and resource management free your team to focus on building applications rather than babysitting servers.