In Lesson 1, you saw the big picture. Now we go deep into each control plane component — what it actually does, how it's configured, and what breaks in production.

1. kube-apiserver — The Heart of the Cluster

The API server is the only component that reads and writes to etcd. Every other component — scheduler, controllers, kubelet, even kubectl — talks exclusively to the API server. It is the cluster's central nervous system.

What It Actually Does

When a request arrives at the API server, it passes through a strict pipeline:

AuthN AuthZ Mutating Admission Validation Validating Admission Persist → etcd Response ← Request rejected at any failing stage →
StageWhat HappensExample
AuthenticationIdentifies the callerClient cert CN=admin, ServiceAccount token
AuthorizationChecks if this identity can do this actionRBAC: can user X create pods in ns Y?
Mutating AdmissionWebhooks can modify the requestInject sidecar container, add labels
Schema ValidationIs the object well-formed?Reject if replicas: -1
Validating AdmissionWebhooks can reject (not modify)Deny images not from approved registry
PersistWrite to etcdObject stored, resourceVersion assigned
Key: Mutating admission runs before validation. This means a webhook can inject fields that then pass validation. Validating admission runs after validation — it sees the final, valid object and can only accept or reject.

API Server as a Watch Hub

Beyond CRUD, the API server's most critical function is watch notifications. Controllers and kubelets open long-lived watch connections:

GET /api/v1/pods?watch=true&resourceVersion=12345

The API server streams change events (ADDED, MODIFIED, DELETED) for any objects matching the watch. This is how the entire system stays reactive without polling.

A large cluster may have thousands of simultaneous watch connections. The API server maintains an in-memory watch cache to avoid hitting etcd for every watch notification. This cache is why the API server is memory-hungry in large clusters (often 4-16GB RAM).

Running Multiple API Servers

In HA setups, you run 3+ API server instances behind a load balancer. They are stateless — all state lives in etcd. Any instance can serve any request. The load balancer just needs to health-check /healthz.

2. etcd — The Cluster's Memory

etcd is a distributed, consistent key-value store that uses the Raft consensus algorithm. It's where every Kubernetes object lives — Pods, Services, Secrets, ConfigMaps, everything.

How Data is Organized

/registry/pods/default/nginx-7d9fc44b6c-x2k4j
/registry/deployments/default/nginx
/registry/services/specs/default/kubernetes
/registry/secrets/kube-system/bootstrap-token-abcdef
/registry/nodes/worker-1

Each key stores the full serialized object (protobuf by default, JSON if configured).

Raft Consensus — Why 3 or 5 Nodes?

Raft requires a quorum (majority) to accept writes:

Cluster SizeQuorum NeededTolerates Failures
110 (no HA)
321 node failure
532 node failures
743 node failures (rarely worth the write latency cost)
Why odd numbers? Even numbers (e.g., 4) give the same fault tolerance as the odd number below them (3), but with higher write latency because more nodes must acknowledge. 3 and 5 are the sweet spots.

Key Operational Concepts

  • resourceVersion: Every K8s object has one — it maps to etcd's ModRevision. Used for optimistic concurrency (conflict detection) and watch resumption.
  • Compaction: etcd keeps history of all revisions. Compaction removes old revisions to reclaim space. K8s triggers this automatically.
  • Defragmentation: After compaction, disk space isn't freed until defrag runs. Must be done per-member, sequentially.
  • Quota: Default 2GB. If etcd exceeds quota, it goes read-only (alarm state). Your cluster effectively freezes for writes.
The #1 etcd production incident: quota exceeded. Large clusters with many Events or frequently-updated objects (e.g., Endpoints) can hit this. Monitor etcd_mvcc_db_total_size_in_bytes and set alerts at 80% of quota. Increasing quota beyond 8GB is not recommended — if you need more, reduce object churn instead.

Backup and Restore

# Take a snapshot
etcdctl snapshot save /backup/etcd-$(date +%Y%m%d).db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/etcd/ca.crt \
  --cert=/etc/etcd/server.crt \
  --key=/etc/etcd/server.key

# Restore (stops the cluster!)
etcdctl snapshot restore /backup/etcd-20240101.db \
  --data-dir=/var/lib/etcd-restored
CKA Exam: etcd backup and restore is a guaranteed exam topic. Know the flags, know that restore creates a new data directory, and know that you must update the etcd static pod manifest to point to the new directory.

3. kube-scheduler — The Placement Engine

The scheduler's job is deceptively simple: for each unscheduled Pod, pick the best node. But "best" involves a complex scoring algorithm.

The Scheduling Cycle

Filter Remove unfit nodes Score Rank remaining Reserve Optimistic lock Bind Assign nodeName 100 nodes → 30 30 nodes → top 1 Claim resources Write to API server

Filter Plugins (Must Pass)

PluginRejects Node If...
NodeResourcesFitNot enough CPU/memory for Pod's requests
NodeAffinityNode labels don't match required affinity
TaintTolerationNode has taint the Pod doesn't tolerate
PodTopologySpreadWould violate maxSkew constraint
VolumeBindingRequired PV not available in that zone

Score Plugins (Rank Nodes 0-100)

PluginPrefers Nodes With...
LeastAllocatedMost free resources (spread workload)
MostAllocatedLeast free resources (bin-pack for cost)
ImageLocalityContainer image already pulled
InterPodAffinityMatching Pod affinity rules
NodeAffinityPreferred (soft) node affinity matches
In large clusters (5000+ nodes), the scheduler uses a percentageOfNodesToScore parameter (default: adapts based on cluster size) to avoid scoring all nodes. It stops once it finds enough feasible nodes. This is why scheduling decisions can vary between identical clusters.

What Happens When No Node Fits?

The Pod stays Pending. The scheduler emits an event explaining why:

Events:
  Type     Reason            Message
  ----     ------            -------
  Warning  FailedScheduling  0/3 nodes are available:
                             1 Insufficient cpu,
                             2 node(s) had taint {key=dedicated:NoSchedule}

This is the first thing to check for stuck Pods: kubectl describe pod <name> and read the Events.

4. kube-controller-manager — The Reconciliation Engine

A single binary containing ~30 independent controllers, each running its own reconciliation loop. Think of it as 30 microservices compiled into one process for convenience.

The Controller Pattern

while true:
    desired = read_spec_from_api_server()
    actual  = observe_current_state()
    if actual != desired:
        take_action_to_converge(actual → desired)
    sleep(resync_interval)  # or react to watch events

Key Controllers and What They Do

ControllerWatchesActs OnProduction Impact
DeploymentDeploymentsCreates/scales ReplicaSetsDrives all rolling updates
ReplicaSetReplicaSets + PodsCreates/deletes PodsMaintains desired replica count
Node LifecycleNode heartbeatsTaints/evicts from dead nodes40s default timeout → NotReady
EndpointSliceServices + PodsUpdates EndpointSlicesDrives service discovery
ServiceAccountNamespacesCreates default SA per namespaceEnsures Pods can authenticate
PV BinderPVCs + PVsBinds claims to volumesStorage provisioning
JobJobs + PodsCreates Pods, tracks completionsBatch workloads
CronJobCronJobsCreates Jobs on scheduleScheduled batch work
Garbage CollectorownerReferencesCascading deletesCleans up orphaned objects

Leader Election

Only one controller-manager instance is active at a time (the leader). Others are hot standbys. Leader election uses a Lease object in the kube-system namespace:

kubectl get lease -n kube-system kube-controller-manager
# holderIdentity: master-1_xxxx
# leaseDurationSeconds: 15
# renewTime: 2024-01-15T10:30:45Z

If the leader fails to renew within the lease duration, another instance takes over.

Key: The controller manager and scheduler both use leader election. Only ONE instance of each is reconciling at any time. This avoids conflicts. The API server does NOT need leader election — it's stateless and all instances can serve simultaneously.
Node lifecycle controller timings matter enormously. Default: a node is marked NotReady after 40s of missed heartbeats, then pods are evicted after 5min. In cloud environments with flaky networking, these timeouts are often tuned to avoid false evictions. Flags: --node-monitor-grace-period and --pod-eviction-timeout.

5. cloud-controller-manager — The Cloud Bridge

Introduced to decouple cloud-specific logic from the core Kubernetes codebase. It runs controllers that interact with your cloud provider's API:

ControllerWhat It DoesCloud API Used
NodeDetects node deletion in cloud, updates Node objectsEC2/Compute Engine
RouteConfigures network routes between nodesVPC routing
Service (LB)Provisions cloud load balancers for type: LoadBalancerELB/ALB/Cloud LB

In self-managed (bare-metal) clusters, there is no cloud-controller-manager. You either use MetalLB for LoadBalancer services or go without.

In managed Kubernetes (EKS, GKE, AKS), the cloud-controller-manager is run by the provider and you never interact with it directly. But understanding it explains why type: LoadBalancer "just works" on cloud but not on bare metal.

Control Plane Communication Map

kube-apiserver etcd R/W kube-scheduler controller-mgr cloud-ctrl-mgr kubelet kubectl All arrows point TO the API server (except API server → etcd)
The Star Topology: Notice every component talks to the API server, and only the API server talks to etcd. Components never talk directly to each other. This simplifies security (you only need to secure one endpoint) and makes the system more resilient (components can restart independently).

Summary: What to Remember

ComponentStateless?Leader-Elected?Talks to etcd?Key Metric to Monitor
API Server✅ YesNo (all active)✅ Only oneRequest latency, watch count
etcd❌ StatefulRaft leaderIs etcdDB size, fsync latency, leader changes
Scheduler✅ Yes✅ YesNoScheduling latency, pending pods
Controller Mgr✅ Yes✅ YesNoWork queue depth, reconcile errors
Cloud Ctrl Mgr✅ Yes✅ YesNoCloud API errors, LB provision time

📝 Quiz: Control Plane Deep-Dive

Q1: In the API server request pipeline, can a validating admission webhook modify the object?

No. Validating webhooks can only accept or reject. Only mutating admission webhooks can modify the object. Validating webhooks see the final form after all mutations.

Q2: You have a 5-node etcd cluster. How many nodes can fail simultaneously while the cluster still accepts writes?

2 nodes. A 5-node cluster needs a quorum of 3. With 3 remaining alive, it can still form quorum and accept writes.

Q3: The scheduler has 50 feasible nodes after filtering. What does it do next?

It runs scoring plugins on each feasible node (0-100 per plugin), sums the weighted scores, and picks the node with the highest total score. It then enters the Reserve phase and finally Binds the Pod.

Q4: If the kube-controller-manager leader dies, how long before another instance takes over?

Approximately 15 seconds (the default lease duration). When the leader fails to renew its Lease object, another instance acquires the lease and becomes the new leader.

Q5: Why does Kubernetes use a star topology (everything → API server) instead of letting components talk directly?

Three reasons: (1) Security — only one endpoint to authenticate/authorize against. (2) Consistency — all state goes through one gatekeeper (serialized writes to etcd). (3) Decoupling — components can be restarted/replaced independently without breaking communication between others.

Q6: Your etcd monitoring shows db_total_size approaching 2GB. What's about to happen, and how do you fix it?

etcd will trigger a quota alarm and refuse all writes — your cluster freezes for mutations. Fix: (1) Compact old revisions: etcdctl compact. (2) Defrag: etcdctl defrag. (3) Disarm the alarm: etcdctl alarm disarm. Long-term: reduce object churn (e.g., reduce Event TTL, use EndpointSlices instead of Endpoints).