Kubernetes Architecture & Components Overview
A human-friendly breakdown of the Kubernetes control plane, worker nodes, and core cluster services — with real production context, examples, and best practices.
You’re about to run applications in production on Kubernetes. Before you dive into YAML and deployments, you need to understand who does what inside a cluster. Think of this chapter as your mental model — the map you’ll reference every time something breaks.
Let’s walk through the control plane, the worker nodes, and the core objects that glue everything together.
The Control Plane — The Brain of the Cluster
The control plane is the set of components that make global decisions about the cluster. It’s the management layer — it schedules workloads, responds to events, and maintains the desired state.
If the cluster were an airport, the control plane would be the air traffic control tower. The workers (nodes) are the runways and gates where actual planes (your containers) operate.
Entry point for all interactions
Cluster brain & state
Desired state enforcer
Pod placement decisions
API Server — The Front Door
Everything talks to the API server. kubectl, CI/CD pipelines, the scheduler, controllers, even the nodes themselves — every single interaction goes through this single endpoint.
The API server is like a bank teller. You (or any system) submit a request — “create a deployment”, “scale this service” — and the teller validates your identity (auth), checks your permissions (RBAC), and writes the transaction to the ledger (etcd). If the teller is down, nobody does anything.
In production, your API server must be highly available. On EKS, AWS runs it across three Availability Zones automatically. If you’re self-managing, you need at least three control plane nodes with a load balancer in front.
etcd — The Source of Truth
etcd is a distributed key-value store that holds the entire cluster state — every deployment, every secret, every node, every config map. It’s the cluster’s memory.
Never run etcd with fewer than 3 members. etcd uses a quorum-based consensus protocol (Raft). With 3 nodes, you can lose 1 and still operate. With 2 nodes, losing 1 means quorum is lost and the cluster stops. Always use an odd number — 3 or 5.
Here’s what your etcd backup strategy should look like in practice:
# Backup etcd (self-managed clusters)
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# On EKS — etcd is managed by AWS, but back up cluster resources with Velero
velero backup create daily-backup --include-cluster-resources=true --ttl 168h If you’re on EKS, AWS handles etcd durability for you. But don’t skip Velero backups — etcd isn’t the only thing that matters. You still need to back up your PVCs, CRDs, and custom resources.
Controller Manager — The Desired State Enforcer
The controller manager runs controllers — loops that watch the current state of the cluster and work to make it match the desired state.
If you set replicas: 3 on a Deployment and a node dies taking one pod with it, the controller manager detects the drift and schedules a replacement. It never sleeps, never gets distracted.
// WATCH LOOP
A controller constantly watches the API server for changes to the resource it manages (e.g., a Deployment).
// DETECT DRIFT
It compares the current state (3 pods running) against the desired state (3 replicas). If a pod is missing, it registers a deficit.
// RECONCILE
It creates a new Pod object via the API server or triggers other actions to bring the cluster back to the desired state.
Scheduler — The Pod Placement Engine
When a new pod is created (with no node assigned), the scheduler picks the best node for it. It evaluates:
- Resource requirements — does the node have enough CPU/memory?
- Affinity and anti-affinity rules — should this pod run near or away from other pods?
- Taints and tolerations — is the pod allowed on this node?
- Node health — is the node reporting ready?
Imagine you’re deploying a payment processing service. You have 3 replicas and want them on separate nodes so a single node failure doesn’t take down payments. You add a podAntiAffinity rule, and the scheduler ensures each replica lands on a different node — even a different Availability Zone.
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: payment-service
topologyKey: "topology.kubernetes.io/zone"
Worker Nodes — Where the Work Happens
Worker nodes are the machines (EC2 instances, bare metal, or VMs) that run your containerized applications. Each node runs three essential components:
Node registration & pod management
containerd, CRI-O, etc.
Service routing & network rules
kubelet — The Node Agent
The kubelet is an agent that runs on every node. It registers the node with the control plane and ensures containers are running as instructed by the API server.
If the kubelet stops reporting, the control plane marks the node as NotReady after a timeout and reschedules the pods elsewhere. This is why monitoring kubelet health is critical.
On EKS, your node IAM role must have the correct permissions for the kubelet to register successfully. If you’re using custom AMIs or self-managed nodes, verify the kubelet bootstrap config — misconfigured kubelet flags are one of the most common causes of nodes failing to join the cluster.
Container Runtime — Actually Running Things
The container runtime is the software that pulls images and runs containers. In modern Kubernetes, that’s almost always containerd or CRI-O. Docker was deprecated as a runtime after Kubernetes 1.24.
kube-proxy — Network Rules Manager
kube-proxy maintains network rules on each node. It implements Kubernetes Services by managing iptables or IPVS rules, routing traffic to the right pods.
For large clusters (100+ nodes), IPVS mode performs better than iptables because it uses a hash table instead of a chain of rules. Switching is as simple as setting --proxy-mode=ipvs on kube-proxy — worth doing for production at scale.
Node Groups & Instance Diversity
In production, you rarely have a single pool of identical nodes. Different workloads have different needs:
| Workload Type | Recommended Instance | Why |
|---|---|---|
| Web APIs & microservices | General-purpose (m7i, t3) | Balanced CPU/memory |
| ML training / inference | GPU (p4d, g5) | CUDA cores, high memory bandwidth |
| Databases (in-cluster) | Storage optimized (i7i, r7i) | High IOPS, local NVMe |
| Batch / CI workers | Compute optimized (c7i) | High vCPU count, cost-effective |
Use Managed Node Groups on EKS. They automatically use EKS-optimized AMIs, handle rolling upgrades, and integrate with IAM. Only use self-managed nodes if you need custom AMIs or specialized bootstrap scripts.
Pods & Higher-Level Controllers
The pod is the smallest deployable unit in Kubernetes. But in production, you almost never create pods directly — you use controllers that manage pods for you.
Deployments — The Workhorse
Deployments manage stateless applications. They handle:
- Running a specified number of replicas
- Rolling updates with zero downtime
- Rollbacks to a previous version
- Horizontal scaling (manual or via HPA)
A production-ready Deployment has three things you should never skip:
// HEALTH PROBES
Liveness probe — tells Kubernetes if the container is alive. If it fails, the pod is restarted.
Readiness probe — tells Kubernetes if the container is ready to serve traffic. If it fails, the pod is removed from Service endpoints.
// RESOURCE REQUESTS & LIMITS
Requests — what the scheduler guarantees the container gets.
Limits — the maximum the container is allowed to use. Never set limits without requests, and never over-request “just in case” — it wastes resources and money.
// ANTI-AFFINITY
Ensures replicas are spread across different nodes (and ideally different AZs) so a single failure doesn’t take down the entire service.
Here’s what a production-ready Deployment looks like:
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-api
namespace: production
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: payment-api
topologyKey: "topology.kubernetes.io/zone"
containers:
- name: app
image: myapp/payment-api:v2.1.4
ports:
- containerPort: 8080
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 500m
memory: 768Mi
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 3
periodSeconds: 5 Setting maxUnavailable: 0 ensures that during a rolling update, the number of available replicas never drops below your desired count. The new pod starts up and receives traffic before the old one is terminated. This is how you achieve zero-downtime deployments.
StatefulSets — For Apps That Need Identity
StatefulSets are for stateful applications — databases, message brokers, anything that needs stable network identities and persistent storage.
Each pod in a StatefulSet gets:
- A stable hostname (e.g.,
db-0,db-1,db-2) - Its own persistent volume (that survives rescheduling)
- Ordered, graceful deployment and scaling
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
template:
spec:
containers:
- name: postgres
image: postgres:16
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
The guidance here is honest: managed databases (RDS, Aurora, ElastiCache) reduce operational burden significantly. Running stateful workloads on Kubernetes is doable — many companies do it — but you need solid backup strategies (Velero, pg_dump, snapshots), monitoring, and failover procedures. If your FinTech app loses a transaction, “but Kubernetes rescheduled the pod” won’t calm your compliance team.
DaemonSets — One Pod Per Node
DaemonSets ensure every node runs a copy of a pod. They’re perfect for:
- Log shippers — Fluent Bit, Filebeat
- Monitoring agents — Node Exporter, Datadog agent
- Network plugins — Calico, Cilium
- Security agents — Falco, Aqua
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: observability
spec:
selector:
matchLabels:
app: fluent-bit
template:
spec:
containers:
- name: fluent-bit
image: fluent/fluent-bit:3.0
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
Jobs & CronJobs — One-Time & Scheduled Tasks
Jobs run a set of pods to completion — useful for batch processing, database migrations, or data exports. CronJobs add a schedule:
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-db-backup
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: myapp/backup-tool:1.0
command: ["pg_dump", "-Fc", "myapp_prod", "-f", "/backups/dump.sqlc"]
restartPolicy: OnFailure
Always set concurrencyPolicy: Forbid on CronJobs that shouldn’t overlap — like database backups. If the previous backup is still running when the next schedule fires, Kubernetes will skip the new run instead of letting them pile up.
Services — Stable Networking for Ephemeral Pods
Pods come and go. They get rescheduled, replaced, and restarted. Services provide a stable endpoint (a virtual IP and DNS name) that persists regardless of pod churn.
| Type | When to Use | Example |
|---|---|---|
| ClusterIP (default) | Internal microservice communication | Frontend → backend API |
| NodePort | Development or debugging | Direct access to a specific node |
| LoadBalancer | Exposing a service externally (each creates a cloud LB) | Simple single-service exposure |
| ExternalName | Mapping a K8s service to an external DNS name | Pointing to a legacy service outside the cluster |
In production, you rarely use LoadBalancer services for every app. Instead, use ClusterIP for internal services and an Ingress (e.g., AWS ALB Ingress Controller, NGINX Ingress) to route external traffic. One LoadBalancer for many services saves money and simplifies management.
Namespaces — Organizing Your Cluster
Namespaces are virtual clusters within your physical cluster. They provide:
- Isolation — separate teams, environments, or applications
- Scope — for resource names (you can have a
payment-apiservice in bothstagingandprodnamespaces) - Policy boundaries — RBAC, network policies, and resource quotas apply at the namespace level
kubectl create namespace production
kubectl create namespace staging
kubectl config set-context prod --namespace=production
kubectl config set-context stage --namespace=staging
Common approaches:
- Per environment:
production,staging,development - Per team:
platform-team,payments-team,data-team - Per application: a namespace for each microservice
For multi-tenant clusters, use per-team namespaces with strict ResourceQuotas and NetworkPolicies.
What’s Next?
Now that you understand the architecture, the next guide will cover High Availability Architecture — designing your cluster to survive failures, including control plane redundancy, multi-AZ distribution, and upgrade strategies.
- The control plane (API server, etcd, controller manager, scheduler) manages the cluster — it’s the air traffic control
- Worker nodes (kubelet, container runtime, kube-proxy) run your actual workloads
- Pods are the smallest unit, but you use Deployments, StatefulSets, DaemonSets, and Jobs/CronJobs to manage them
- Services provide stable networking for dynamic pods
- Namespaces organize and isolate cluster resources