Kubernetes for system design interviews
Open in the interactive app: practice, quiz, mock interview →In one minute
- Kubernetes is a declarative control plane for containers: you write desired state as API objects, and controllers keep changing the world until it matches.
- All state lives in etcd behind one API server. Every other component, including the scheduler and each node's kubelet, is a client that watches the API and writes back.
- Controllers are level-triggered reconcile loops. They do not react to "what changed"; they compare desired and observed state and act on the difference, which makes them robust to missed events and crashes.
- The scheduler only chooses a node and writes a binding. The kubelet on that node starts containers through the CRI and wires networking through CNI.
- Choose it when you run dozens of services and have people to operate a platform. For a handful of services, a managed container service (Cloud Run, ECS, App Service) is usually cheaper in people and time.
How it works
Objects and desired state
Everything is a typed object with metadata, spec (what you want) and status (what is). A Deployment's spec says "4 replicas of this pod template, roll out with maxSurge 25 percent and maxUnavailable 25 percent", which are also the defaults (Kubernetes docs). You never tell Kubernetes to start a container; you change an object and wait.
The control plane
The API server is the only component that talks to etcd: it authenticates, authorises with RBAC, runs admission webhooks, validates and stores objects. kube-controller-manager runs dozens of controllers (Deployment, ReplicaSet, Job, Node, EndpointSlice, garbage collector) in one process, and kube-scheduler assigns pods to nodes; each uses a Lease for leader election.
The nodes
Each node runs the kubelet, a container runtime such as containerd, and usually kube-proxy or an eBPF replacement. The kubelet watches for pods bound to its node, starts them, runs probes and reports status. Every pod gets its own IP from a flat network where pods reach each other without NAT; a CNI plugin (Calico, Cilium, the cloud's VPC plugin) provides it. A Service gives a stable virtual IP that kube-proxy turns into iptables, IPVS or nftables rules spreading traffic over ready endpoints. iptables is still the default mode, nftables has been GA since 1.33, and IPVS was deprecated in 1.35 and will be off by default from 1.40 (Kubernetes docs).
A Deployment rollout
The Deployment controller owns ReplicaSets, one per pod template, named by a hash of the template. On a template change it creates a new ReplicaSet and shifts replica counts between old and new within the surge and unavailability budget, waiting for new pods to become ready. The ReplicaSet controller creates or deletes pods to match each count.
Down the stack
Layer 1The contract
- Declarative and eventually consistent. A successful write means the object is durably stored, not that the world has changed. Status catches up later.
- Optimistic concurrency. Every object has a
resourceVersion. An update with a stale one fails with409 Conflict, so controllers re-read and retry instead of overwriting. - List and watch. A client can list a resource at a version and then watch from that version, receiving every later change in order, unless the version has been compacted, in which case it gets
410 Goneand must re-list (API concepts). - Scale envelope. Upstream tests target up to 5,000 nodes, 150,000 pods, 300,000 containers and 110 pods per node (Kubernetes docs). Each object is limited to about 1.5 MiB by etcd's default request size (etcd docs).
- Failure behaviour. If the control plane is down, running pods keep running and Services keep routing; nothing new is scheduled or healed.
Layer 2The mechanism
API server and etcd. Objects are serialised (protobuf for built-in types) and stored under keys like /registry/pods/default/web-7d9f-abc. etcd is a Raft-replicated key-value store with MVCC on bbolt: each write gets a cluster-wide revision, which becomes the object's resourceVersion. The API server keeps a watch cache per resource type, so thousands of watchers are served from memory rather than from etcd, and it compacts old revisions every 5 minutes by default. Since 1.31 the cache also answers consistent ("most recent") lists, after confirming with etcd that it is up to date, so a plain LIST no longer has to read etcd.
Informers and work queues. A controller does not poll. A shared informer lists once, then watches, maintaining a local cache and calling handlers on add, update and delete. Handlers only push an object key (namespace/name) onto a rate-limited work queue, which deduplicates: ten updates to one Deployment while a worker is busy collapse into one pending key. Workers pop keys and call reconcile(key), which reads current state from the cache, computes the difference from spec, issues API writes and returns. Errors re-queue with exponential backoff. A periodic resync re-queues everything, so a missed event is eventually corrected.
Reconcile is level-triggered. Because the loop asks "how many pods should exist and how many do", it is idempotent and safe to run twice or after a crash. The Deployment controller also records observedGeneration in status so clients can tell whether the controller has seen the latest spec.
Scheduler. Unscheduled pods (empty spec.nodeName) enter a priority queue. The scheduling framework runs plugins in phases: PreFilter and Filter remove infeasible nodes (resources, taints, node affinity, topology spread), Score ranks the rest (balanced allocation, image locality, inter-pod affinity), then Reserve, Permit and finally Bind, which writes a Binding subresource setting nodeName. In large clusters it stops after finding enough feasible nodes (percentageOfNodesToScore). Decisions use requests, not actual usage (Scheduling Framework).
Kubelet, CRI and CNI. The kubelet syncs each pod: it asks the runtime over CRI gRPC to RunPodSandbox, which creates a pause container holding the pod's network namespace. The runtime calls the CNI plugin's ADD command with that namespace path; the plugin creates a veth pair or ENI attachment, assigns an IP from IPAM and returns it. Then the kubelet pulls images and calls CreateContainer and StartContainer for each container, placing them in a cgroup under kubepods/<qos>/pod<uid>. It heartbeats by renewing a Lease in kube-node-lease every 10 s (Kubernetes docs); if heartbeats stop for the 50 s node-monitor-grace-period, the node controller sets its Ready condition to Unknown and taints it unreachable, and pods are evicted after an automatically added 300 s toleration (Kubernetes docs).
Layer 3The machine
etcd is the throughput limit. Every write is a Raft append that must be fsynced on a quorum, so etcd wants WAL fsync p99 under 10 ms (etcd FAQ); on slow network disks, leader elections and API timeouts follow. Its default space quota is 2 GiB (8 GiB is the suggested maximum), and once exceeded the cluster becomes read-only until compaction and defragmentation. A cluster with 100,000 pods holds a few hundred MB in etcd, and each pod update rewrites the whole object.
Unbounded LIST calls are the classic overload. A list of all pods that bypasses the watch cache (an exact resourceVersion, or an old cluster) makes the API server read every object from etcd, decode it and re-encode JSON, using hundreds of MB and seconds of CPU. Even from cache, the full response is built in memory, which is why streaming lists (beta and on by default since 1.34) let informers start with a watch that sends the initial state item by item. API Priority and Fairness, stable since 1.29, queues requests by flow so one noisy client cannot starve the controllers (Kubernetes docs).
On the node, pod startup is dominated by image pulls (seconds to minutes), then CNI setup (tens to hundreds of ms). iptables-mode Service routing makes rule updates take seconds with tens of thousands of Services, and packet latency grows with the rule count; IPVS, nftables and eBPF datapaths use hash lookups (Kubernetes blog).
Why it's built this way
- Machines and networks fail all the time at scale, so components must recover from any state, which makes level-triggered reconciliation safer than event-driven scripts.
- Many independent controllers need a consistent shared truth, so all state goes through one API backed by a consensus store.
- Consensus writes are slow and etcd is small, so the API server caches and fans out watches, and objects stay small.
- Polling thousands of objects from dozens of controllers would crush the API, so informers list once and watch, and work queues collapse bursts.
- Scheduling and running containers are different problems at different scales, so the scheduler only binds and each kubelet owns its node, with CRI and CNI keeping runtimes and networks pluggable.
Diagram
sequenceDiagram
participant U as kubectl
participant A as "API server"
participant E as etcd
participant C as "Deployment + RS controllers"
participant S as Scheduler
participant K as Kubelet
U->>A: apply Deployment (replicas 3)
A->>E: write, revision 1042
A-->>C: watch event
C->>A: create ReplicaSet, then 3 Pods
A-->>S: watch: pods with no node
S->>A: Binding pod to node-7
A-->>K: watch: pod bound here
K->>K: CRI RunPodSandbox, CNI ADD
K->>K: pull image, start containers
K->>A: status ReadyWorked example
A Deployment has 100 replicas, maxSurge 25 percent and maxUnavailable 25 percent, so during a rollout there may be up to 125 pods and must be at least 75 ready.
The controller first scales the new ReplicaSet to 25 (surge) and the old one down to 75 (the unavailability budget allows losing 25). As new pods become ready, it moves the next 25 from old to new. If a new pod takes 30 s to pull, start and pass readiness, the rollout needs about four waves, roughly 2 to 3 minutes. Setting maxUnavailable 0 and maxSurge 10 percent keeps full capacity but needs ten waves and 10 extra pods of headroom on the nodes.
Control-plane cost: each pod generates roughly 5 to 10 writes over its life (create, bind, several status updates, delete). Replacing 100 pods is about 1,000 writes, trivial for etcd, which sustains thousands of writes per second on good disks. The same rollout across 2,000 Deployments at once is 2 million writes and can saturate it, which is why platform teams stagger large rollouts.
In the wild
- OpenAI, 2024: a telemetry DaemonSet takes down the control plane, and DNS with it. A new telemetry service made every node run Kubernetes API calls whose cost grew with cluster size, which overloaded the API servers in most large clusters. Service discovery depended on the control plane, so it failed too, but node DNS caches hid the problem for about 20 minutes and the rollout kept going. Services were degraded or down for about 4 hours 22 minutes. Fixes: phased rollouts gated on control-plane health, break-glass API access, and discovery decoupled from the control plane (OpenAI).
- Datadog, 2023: an OS update deletes the CNI's routes on every node. An automatic security update restarted systemd-networkd on Ubuntu 22.04, which removed the routes Cilium managed, and tens of thousands of nodes dropped off the network within an hour. A legacy update channel used the same 06:00 to 07:00 UTC window in every region, so otherwise isolated regions failed together. All services were operational in every region about 27 hours later, and the incident closed after about 48 hours once historical data was backfilled (Datadog).
Trade-offs
| Option | Strengths | Weaknesses | Choose when |
|---|---|---|---|
| Managed Kubernetes (EKS, GKE, AKS) | Standard API, huge ecosystem, bin packing, self-healing | Steep learning curve, many moving parts, cluster upgrades | Dozens of services, a platform team |
| Self-managed Kubernetes | Full control, on-prem | You run etcd and upgrades | Regulatory or on-prem requirements |
| Serverless containers (Cloud Run, Fargate) | No nodes, scale to zero | Less control, cold starts, per-request cost | Few services, spiky traffic |
| Nomad | Simpler, mixed workloads | Smaller ecosystem | Small ops team, VMs plus containers |
| VMs with autoscaling groups | Simple mental model, strong isolation | Slow scaling, poor packing | A few large stateless services |
Pitfalls in production
- Requests that do not match reality. The scheduler packs by requests, so under-requested pods get OOM-killed or starve neighbours. Set requests from measured usage and review them.
- Readiness lies. A probe that returns OK before caches warm sends traffic to cold pods and the rollout moves on. Make readiness reflect real ability to serve.
- etcd on slow disks. High
fsynclatency causes leader churn and API timeouts. Use dedicated SSDs and alert onwal_fsync_duration_seconds. - Unbounded LISTs. Custom controllers that list everything on a timer overload the API server. Use informers and pagination.
- Huge objects. ConfigMaps near 1 MB and CRDs with large status slow every watcher. Keep data in real storage.
- No PodDisruptionBudget. Node drains evict all replicas at once. Define PDBs for every critical workload.
Leadership lens
- Ask what the team gains over a managed container service. Kubernetes pays off with many services and shared platform tooling, not for three apps.
- Budget platform people: upgrades every few months (each minor release gets about a year of patch support, Kubernetes releases), add-ons (ingress, CNI, cert-manager, autoscaler), security patching and on-call for the control plane. Add-ons retire too: the community ingress-nginx controller got no fixes after March 2026, and the Ingress API itself is frozen in favour of Gateway API (Kubernetes docs).
- Decide multi-tenancy early: namespaces plus quotas and network policies, or separate clusters per team or environment.
- Treat custom controllers like distributed systems code: idempotent reconcile, owner references, finalizers with timeouts, and load tests against the API server.
Check yourself
Why are Kubernetes controllers level-triggered rather than edge-triggered?
Show answer
If they acted only on events, a missed or duplicated event or a crash would leave the system wrong forever. Comparing full desired and observed state on every pass corrects any drift.
What happens to running pods if the whole control plane goes down for 10 minutes?
Show answer
They keep running and Services keep routing via existing rules, but nothing is scheduled, scaled or replaced, and status goes stale.
Why does the scheduler write a
Bindinginstead of telling the kubelet to start a pod?Show answer
All coordination goes through the API; the binding is durable state the kubelet watches, so either side can crash and resume without a direct RPC.
Why does a work queue deduplicate keys rather than queue every event?
Show answer
Reconcile reads the latest state anyway, so processing one key once covers all pending changes and bursts do not multiply work.
Sources and further study
Primary
- Verma et al., Large-scale cluster management at Google with Borg (EuroSys 2015): the predecessor system, its cell sizes and the scheduling ideas Kubernetes inherited.
- Ongaro and Ousterhout, In Search of an Understandable Consensus Algorithm (Raft): the protocol behind etcd and every Kubernetes write.
Systems docs
- etcd: System limits: request size and storage quota, the hard ceilings under the API server.
- API Priority and Fairness: how the API server queues and sheds requests by priority level and flow.
- Gateway API: the successor to Ingress, with role-oriented routing resources.
Books
- Site Reliability Engineering, Beyer et al. (eds.), chapter 2 "The Production Environment at Google, from the Viewpoint of an SRE": Borg, Chubby and the cluster model Kubernetes generalised.
- Designing Distributed Systems, 2nd edition, Brendan Burns, chapter 6 "Replicated Load-Balanced Services": the serving pattern a Deployment plus Service implements, from a Kubernetes co-founder.
Other curricula
- roadmap.sh: Kubernetes: a learning path from pods and Deployments to scheduling, autoscaling, security and cluster operations.
- Azure Architecture Center: AKS baseline architecture: treats Kubernetes as one piece of a reference design with ingress, identity, network policy and upgrades decided up front.
Watch
- Kubernetes Deconstructed (Carson Anderson, CNCF): builds up the API server, etcd, controllers, scheduler and kubelet one piece at a time.
- Deep Dive into Kubernetes Internals for Builders and Operators (USENIX LISA19): control-plane internals, watches and controller patterns for people who run clusters.
Free resources
- Cluster Architecture: the official overview of control-plane and node components.
- Controllers: the control-loop pattern and how controllers act through the API server.
- Scheduling Framework: every scheduler extension point from queue sort to bind.
- Kubernetes API Concepts: resource versions, watches,
410 Gone, pagination and bookmarks. - Considerations for large clusters: the tested scale limits and what to tune near them.
- Borg, Omega, and Kubernetes: the lessons from Google's earlier systems that shaped Kubernetes' design.