New batches starting this week · Limited seats

Kubernetes Master Handbook (2026): Architecture, Internals & YAML — Basics to Expert

The Cloud Soft Solutions Kubernetes Master Handbook — a trainer-grade, 17-chapter guide covering Kubernetes architecture, control-plane internals, CNI networking, and every production workload object (Pod, Deployment, DaemonSet, StatefulSet), with a troubleshooting playbook and a command/YAML reference. Basics to expert.

Cloud Soft Solutions — India's No.1 cloud placement institute in Hyderabad with 5,500+ placements (AWS, Azure, DevOps, GCP)
Last updated · 58 min read · 12,835 words

This is the web edition of the Cloud Soft Solutions Kubernetes Master Handbook — a trainer-grade guide covering Kubernetes architecture, control-plane internals, CNI networking and the production workload objects, from basics to expert. Use the contents below to jump to any chapter. Preparing for interviews or the CKA/CKAD/CKS? Pair it with our AI & ML interview questions and the Cloud + DevOps roadmap.

Table of Contents

  1. What Is Kubernetes
  2. Why We Need Kubernetes in Real Time
  3. History and Evolution
  4. Docker Swarm vs Kubernetes
  5. Kubernetes Architecture — The Big Picture
  6. Control Plane Components in Depth
  7. Worker Node Components in Depth
  8. Cluster Add-ons
  9. CNI and Cluster Networking
  10. Namespace
  11. Pod — The Atomic Unit
  12. Deployment and ReplicaSet
  13. DaemonSet
  14. StatefulSet
  15. Choosing the Right Workload Object
  16. Troubleshooting Playbook
  17. Command and YAML Reference
Chapter One

What Is Kubernetes

The definition, the mental model, and the boundary of what it does

Kubernetes is an open-source platform for automating the deployment, scaling, healing and operation of containerised applications across a fleet of machines. The name comes from the Greek κυβερνήτης — helmsman or pilot — and it is commonly abbreviated K8s, where the 8 stands for the eight letters between the K and the s.

1.1 The one-sentence definition, unpacked

That definition is easy to recite and hard to actually understand, so let us take it apart clause by clause. Every phrase in it carries weight.

"Containerised applications"

Kubernetes does not run your code directly. It runs containers — the images your teams build with Docker, Buildah, Kaniko or Jib. Kubernetes assumes the packaging problem is already solved. It concerns itself only with where those containers run, how many of them run, how they find each other, and what happens when they die.

"Across a fleet of machines"

This is the central idea. A single Docker host is a machine you must think about. A Kubernetes cluster is a pool of capacity you stop thinking about. You declare "I need six replicas of this service with 500m CPU each" and Kubernetes decides which of your forty nodes has room. You never write a hostname. When a node dies at 3 a.m., the workload lands somewhere else and nobody is paged.

"Automating deployment, scaling, healing and operation"

These are the four verbs that justify the entire platform:

  • Deployment — roll a new image out gradually, watch health, stop if it fails, roll back to the previous version on one command.
  • Scaling — add or remove replicas manually, on CPU/memory pressure, on custom metrics, or on a queue depth; and add or remove nodes underneath when the pods will not fit.
  • Healing — restart crashed containers, replace pods on dead nodes, remove unhealthy pods from load-balancer rotation, reschedule when a node is drained.
  • Operation — service discovery, configuration injection, secret distribution, storage attachment, rolling certificate updates, traffic policy.

1.2 Declarative intent and the reconciliation loop

If you remember one concept from this entire handbook, make it this one. Kubernetes is a declarative, level-triggered control system. Almost every question an interviewer asks about Kubernetes internals is really this idea in disguise.

In an imperative system you issue commands: start this container, stop that one, copy this file. The system does what you said and forgets. If the container dies afterwards, nothing happens, because nobody is watching.

In Kubernetes you do not issue commands. You submit a desired state — a document that says what the world should look like. A controller then runs forever in a loop:

The universal controller loop (pseudocode)
for {
    desired  := readSpecFromAPIServer()   // what the user asked for
    actual   := observeRealWorld()        // what is actually running

    if actual == desired {
        continue                          // nothing to do, sleep and re-check
    }

    diff := computeDifference(desired, actual)
    takeCorrectiveAction(diff)            // create, delete, patch
    writeStatusBackToAPIServer()
}

Three consequences follow from this design, and all three matter in production:

PropertyWhat it means in practice
Self-healing is freeNobody wrote code that says "if a pod dies, restart it". The ReplicaSet controller simply notices that observed replicas (2) no longer equals desired replicas (3) and creates one. Healing is a side effect of the loop, not a feature.
Level-triggered, not edge-triggeredKubernetes reacts to state, not to events. If a controller crashes and misses ten events, it recovers by comparing current state on restart. A missed event never causes permanent divergence. This is why Kubernetes is resilient to its own component failures.
Eventual consistencyChanges are not instantaneous. You apply a manifest and the API server returns success immediately — that only means the intent was recorded. Actual convergence happens asynchronously, over seconds or minutes. Pipelines that assume kubectl apply means "deployed" are the single most common CI/CD bug in Kubernetes shops.
Trainer note — the sentence that unlocks Kubernetes "Kubernetes never does anything to your cluster. It continuously compares two documents — what you asked for and what exists — and closes the gap." Once a learner internalises this, the API server, etcd, controllers, the scheduler and even operators stop being separate topics and become one topic seen from five angles.

Spec and status: the shape of every object

Because of the loop, every Kubernetes object has the same two-part shape. This is not a convention; it is enforced by the API machinery.

Every object, without exception
apiVersion: apps/v1          # which API group and version defines this object
kind: Deployment             # which type of object
metadata:                    # identity: name, namespace, labels, annotations
  name: payments-api
  namespace: production
spec:                        # DESIRED state  — written by you
  replicas: 3
status:                      # OBSERVED state — written by the controller, never by you
  readyReplicas: 3
  availableReplicas: 3

You write spec. Kubernetes writes status. If you ever find yourself trying to edit status, you have misunderstood the model. And when you debug a workload, the first useful question is always: what does spec say, what does status say, and why do they differ?

1.3 What Kubernetes is not

Interviewers ask this deliberately, because candidates who cannot name the boundaries usually cannot design within them.

Kubernetes is NOT…Explanation
A PaaS like HerokuIt gives you primitives, not an opinionated developer experience. Teams build their PaaS on top of Kubernetes (that is what Argo CD, Backstage, Knative and OpenShift do).
A build systemIt does not compile code or build images. Your CI does that. Kubernetes consumes finished images from a registry.
A CI/CD toolIt has no concept of a pipeline. Jenkins, GitHub Actions, Azure DevOps and Argo CD supply that.
A container runtimeIt does not start containers itself. containerd or CRI-O does, through the CRI interface.
A logging or monitoring systemIt exposes metrics and writes container stdout to disk. Prometheus, Grafana, Elasticsearch and New Relic supply the actual platform.
A database or message busIt runs them, but provides none. Storage durability comes from CSI drivers and the underlying storage system.
A service meshIt gives you basic L4 service discovery and load balancing. mTLS, retries, circuit breaking and traffic splitting come from Istio, Linkerd or Cilium Service Mesh.
A secrets managerA Kubernetes Secret is base64-encoded, not encrypted, unless you enable encryption at rest. Real secret management means Vault, AWS Secrets Manager or Azure Key Vault with an external-secrets operator.
Common interview trap "Are Kubernetes Secrets secure?" The correct answer is: by default a Secret is only base64-encoded in etcd — encoding, not encryption. It becomes reasonably secure only when you enable EncryptionConfiguration at rest, restrict RBAC on the secrets resource, disable automatic ServiceAccount token mounting where unnecessary, and ideally source real secrets from an external vault. Candidates who answer a flat "yes" are marked down immediately.
Chapter Two

Why We Need Kubernetes in Real Time

The concrete production problems that justify the complexity

Kubernetes is genuinely complex. That complexity is only worth paying for if it removes a larger complexity somewhere else. This chapter shows exactly what that larger complexity looks like in a real production environment — and, honestly, when it does not exist and you should walk away.

2.1 Life before orchestration — the pain, concretely

Picture a mid-sized banking platform: twenty-two microservices, four environments, thirty virtual machines, three release windows a week. Without an orchestrator, here is what the team actually deals with.

Scenario A — a node dies at 02:40

Without Kubernetes: monitoring pages the on-call engineer. They log in, discover the host is gone, find which six services were pinned to it, locate spare capacity, SSH into replacement hosts, pull the right image tags from a wiki page that is two weeks stale, start containers with the right environment variables, update the load balancer pool by hand, and verify. Mean time to recovery: 45–90 minutes, with a tired human doing error-prone work.

With Kubernetes: the node controller marks the node NotReady after roughly 40 seconds. After the eviction timeout the pods on that node are marked for deletion. Their owning ReplicaSets observe a shortfall, create replacement pods, the scheduler places them on healthy nodes, kubelet pulls images and starts them, readiness probes pass, endpoints are updated, traffic flows. Mean time to recovery: 60–120 seconds, with nobody awake.

Scenario B — Friday evening traffic spike

Without Kubernetes: someone must predict the spike, provision VMs in advance, and pay for them all week. If the prediction is wrong in one direction you burn money; wrong in the other, you drop transactions.

With Kubernetes: the HorizontalPodAutoscaler watches CPU or a custom metric such as queue depth, scales the deployment from 6 replicas to 30, and the Cluster Autoscaler adds nodes when pods go Pending for lack of capacity. When the spike passes, both scale back down. You pay for the peak only during the peak.

Scenario C — releasing version 2.4.1

Without Kubernetes: a runbook with 40 manual steps, a maintenance window, a rollback plan that involves re-deploying old artefacts, and a genuine risk that hosts 1–4 are on the new version while hosts 5–6 are still on the old one because the script failed halfway.

With Kubernetes: kubectl set image or a Git commit. Pods are replaced a few at a time, each new pod must pass its readiness probe before the next old one is removed, and kubectl rollout undo returns you to the previous ReplicaSet in seconds. There is no partial state, because the controller will not proceed past a failing surge.

2.2 The ten problems Kubernetes actually solves

#ProblemHow Kubernetes addresses it
1Bin-packing and placementThe scheduler places pods on nodes based on requested CPU/memory, affinity rules, taints and topology spread — raising utilisation well above the 15–25% typical of one-app-per-VM estates.
2Self-healingCrashed containers restart; pods on dead nodes are recreated elsewhere; failing pods are pulled out of Service endpoints by readiness probes.
3Horizontal scalingHPA on CPU, memory, or custom/external metrics; VPA for right-sizing; Cluster Autoscaler / Karpenter for node capacity.
4Service discoveryEvery Service gets a stable virtual IP and a DNS name. Applications talk to payments.production.svc.cluster.local and never learn a pod IP.
5Zero-downtime deploymentRolling updates gated on readiness, with surge and unavailability budgets, plus versioned rollback through retained ReplicaSets.
6Configuration and secret injectionConfigMaps and Secrets decouple configuration from images, so the same image promotes unchanged from dev to production.
7Storage abstractionPersistentVolumeClaims request storage by class and size; the CSI driver provisions EBS, Azure Disk, NFS or Ceph without the manifest knowing which.
8Multi-tenancy and isolationNamespaces, ResourceQuota, LimitRange, RBAC and NetworkPolicy allow many teams to share one cluster with enforced boundaries.
9PortabilityThe same manifests run on EKS, AKS, GKE, OpenShift and on-premises. Cloud-specific detail is confined to StorageClasses, LoadBalancer annotations and IAM integration.
10ExtensibilityCustom Resource Definitions plus controllers let you manage databases, certificates or entire environments with the same declarative model — this is what "operators" are.
Real-time framing for interviews Do not answer "why Kubernetes" with a feature list. Answer with a failure story: "Before, a node failure meant a 45-minute manual recovery and a paged engineer. After, the ReplicaSet controller rescheduled the workload in under two minutes and the incident closed itself. That single behaviour paid for the migration." Concrete beats comprehensive.

2.3 When you should NOT use Kubernetes

A world-class engineer is defined as much by knowing when not to reach for a tool. Kubernetes is a poor fit when:

  • You have one or two services and no operations team. A managed PaaS, ECS Fargate, Azure App Service or Cloud Run will deliver the same outcome at a fraction of the operational cost.
  • Your workload is a single stateful monolith with a vertical scaling profile. Kubernetes adds indirection without adding value.
  • You cannot staff platform expertise. An unmaintained cluster — outdated version, unpatched CNI, no backup of etcd, no upgrade rehearsal — is more dangerous than the VMs it replaced.
  • Your latency budget cannot absorb the network path. Overlay networking, kube-proxy and ingress add hops. Most applications never notice; ultra-low-latency trading systems do.
  • Regulatory constraints forbid shared tenancy and you would end up running one cluster per application anyway, which removes the bin-packing benefit.
Cost of ownership, stated plainly A production-grade Kubernetes platform needs: version upgrades every few months, CNI and CSI driver lifecycle management, etcd backup and restore rehearsal, RBAC governance, certificate rotation, admission policy, observability, and a documented DR procedure. Budget for a platform team or buy a managed control plane (EKS, AKS, GKE) and still expect meaningful ongoing effort.
Chapter Three

History and Evolution

Borg to CNCF — and why the history explains the design

Kubernetes did not emerge from a whiteboard. It is the third-generation product of roughly fifteen years of running containers at Google scale, and several design decisions that look arbitrary make perfect sense once you know where they came from.

3.1 Borg, Omega and the Google lineage

Borg (circa 2003 onwards)

Google's internal cluster manager, running essentially all of Google's workloads on shared machine pools. Borg introduced ideas that survive verbatim in Kubernetes: the allocation unit that groups co-located processes (which became the Pod), declarative job specifications, labels for grouping, bin-packing schedulers, and the crucial insight that batch and serving workloads should share the same fleet to raise utilisation.

Omega (circa 2013)

A research successor exploring a different architecture: instead of a monolithic scheduler, Omega used a shared, consistent, transactional store of cluster state that many independent components read and wrote optimistically. That is exactly the etcd-plus-API-server-plus-many-controllers architecture Kubernetes uses today. Omega is the direct ancestor of the control plane's shape.

Kubernetes (2014)

Announced by Google in June 2014, largely authored by engineers with deep Borg experience — Joe Beda, Brendan Burns, Craig McLuckie, Tim Hockin, Brian Grant and others. The deliberate decision was to build an open, API-driven system rather than open-sourcing Borg itself, and to make everything extensible so the community could grow it.

Why the history matters technically The Pod exists because Borg's allocation unit existed. Labels and selectors exist because Borg proved that rigid hierarchies fail at scale and loose label matching does not. The shared-state control plane exists because Omega proved a monolithic scheduler becomes a bottleneck. These are not arbitrary choices; they are field-tested ones.

3.2 Timeline: 2013 to today

WhenMilestone
March 2013Docker released publicly. Containers become accessible to ordinary developers; the orchestration gap becomes obvious almost immediately.
June 2014Kubernetes announced and open-sourced by Google.
Late 2014Red Hat, Microsoft, IBM and others join development. Docker Swarm and Apache Mesos emerge as competitors — the "orchestration wars" begin.
July 2015Kubernetes 1.0 released. Google donates the project to the newly created Cloud Native Computing Foundation (CNCF) under the Linux Foundation — a deliberate move to signal vendor neutrality.
2016Helm appears as the package manager. kubeadm makes cluster bootstrap reproducible. Minikube brings local clusters to laptops.
2017The decisive year. RBAC goes stable, CRDs arrive, and the major clouds ship managed offerings — Amazon EKS, Azure AKS and GKE maturing. In October, Docker Inc. itself announces support for Kubernetes in Docker EE, widely read as the end of the orchestration wars.
2018Kubernetes becomes the first CNCF project to graduate. The Container Storage Interface (CSI) and Container Runtime Interface (CRI) mature, decoupling storage and runtime from the core.
2019–2020Operator pattern spreads. Service meshes (Istio, Linkerd) mature. Dockershim deprecation announced in 1.20 — containerd and CRI-O become the standard runtimes.
2021–2022Dockershim removed in v1.24. GitOps consolidates around Argo CD and Flux, both reaching CNCF graduation. eBPF-based networking (Cilium) gains serious adoption.
2023–2024Gateway API matures as the successor to Ingress. Sidecar containers become a first-class, natively-supported concept. Kubernetes becomes the default substrate for AI/ML workloads with device plugins and scheduling for GPUs.
2025 onwardFocus on cost efficiency, multi-cluster fleet management, security supply chain (SBOM, signed images, admission policy), and running large-scale inference and training workloads.

3.3 The CNCF, release cadence and API deprecation

Kubernetes ships approximately three minor releases per year. Each minor version receives patch support for roughly fourteen months. Managed providers typically support three to four minor versions concurrently, which in practice means you must upgrade at least once a year, and realistically twice.

API versioning — and why it bites people

Kubernetes APIs move through maturity stages, and the stage is visible in the apiVersion string:

StageExampleGuarantees
Alphav1alpha1Disabled by default, may be dropped without notice, may be buggy, no upgrade guarantee. Never use in production.
Betav1beta1Enabled by default historically (no longer, for new APIs), well tested, but the schema may still change incompatibly. Widely used in production against advice.
Stablev1, apps/v1Will not be removed within a major version. Safe.
The classic upgrade failure A cluster upgrade breaks because manifests still reference removed APIs — extensions/v1beta1 Deployments and Ingresses (removed in 1.16 and 1.22), policy/v1beta1 PodDisruptionBudget, or batch/v1beta1 CronJob. Run kubectl convert, or tools such as pluto or kubent, against your manifests before every upgrade. This single check prevents the majority of upgrade incidents.
Trainer talking point Ask learners: "Why does Deployment live in apps/v1 while Pod lives in plain v1?" Answer: Pod, Service, Namespace, ConfigMap and Secret belong to the original core (or "legacy") API group, which has an empty group name — hence just v1. Everything added later lives in a named group: apps, batch, networking.k8s.io, rbac.authorization.k8s.io. The URL paths differ too: /api/v1/... for core, /apis/apps/v1/... for everything else.
Chapter Four

Docker Swarm vs Kubernetes

An honest comparison — Swarm is simpler, and that is sometimes correct

Swarm lost the orchestration war, but it is not a bad system — it is a deliberately smaller one. Understanding precisely where the two diverge sharpens your understanding of what Kubernetes chose to make complicated, and why.

4.1 Architectural comparison

Docker Swarm architecture

  • Manager nodes maintain cluster state using an embedded Raft log — no external datastore to operate.
  • Worker nodes run the Docker Engine and execute tasks assigned by managers.
  • The Service is the top-level abstraction; it produces Tasks, and each task is exactly one container.
  • Networking uses a built-in overlay driver, with a routing mesh that makes any node accept traffic for any published port.
  • The API is the Docker API you already know — docker service create, docker stack deploy.

Kubernetes architecture

  • A control plane of separable components — API server, etcd, scheduler, controller manager — each independently scalable and replaceable.
  • etcd as an external, consistent datastore, itself Raft-based but operated separately.
  • The Pod as the scheduling unit, which may hold multiple co-located containers sharing a network namespace and volumes.
  • Pluggable everything: runtime via CRI, networking via CNI, storage via CSI, devices via device plugins, admission via webhooks.
  • A rich, versioned, extensible REST API that users can add their own types to via CRDs.

4.2 Feature-by-feature matrix

DimensionDocker SwarmKubernetes
Setup effortMinutes. docker swarm init, then join tokens.Hours to days self-managed; minutes with EKS/AKS/GKE but with real configuration decisions.
Learning curveGentle — if you know Docker Compose you are most of the way there.Steep. Dozens of object kinds, controllers, and an entire ecosystem.
Smallest unitTask = one container.Pod = one or more co-located containers sharing network and volumes.
State storeBuilt-in Raft inside managers.External etcd cluster, operated and backed up separately.
ScalingManual: docker service scale. No native autoscaling.Manual, HPA (CPU/memory/custom/external metrics), VPA, plus node-level Cluster Autoscaler and Karpenter.
Load balancingRouting mesh with built-in IPVS; simple and effective for L4.Service (ClusterIP/NodePort/LoadBalancer) plus Ingress and Gateway API for L7 routing, TLS, path/host rules.
Rolling updatesYes, with --update-parallelism, delay, and failure action.Yes, with maxSurge/maxUnavailable, readiness gating, revision history and one-command rollback; plus canary/blue-green via Argo Rollouts or Flagger.
Health checksDocker HEALTHCHECK only.Liveness, readiness and startup probes with independent semantics.
StorageDocker volume plugins; limited dynamic provisioning.Full CSI ecosystem: StorageClass, dynamic provisioning, snapshots, resizing, topology awareness.
Networking policyNo native micro-segmentation.NetworkPolicy (with a supporting CNI), plus mesh-level mTLS and L7 policy.
SecretsEncrypted at rest in the Raft log — arguably better by default.Base64 in etcd unless encryption at rest is configured; but far richer RBAC and external integration.
RBACMinimal in Community Edition.Comprehensive: Roles, ClusterRoles, bindings, ServiceAccounts, admission control.
ExtensibilityEssentially none — the object model is fixed.CRDs, operators, admission webhooks, custom schedulers, custom controllers.
EcosystemSmall and largely static.Vast: Helm, Argo CD, Istio, Prometheus, cert-manager, Kyverno, KEDA, and hundreds more.
Managed offeringsNone from major clouds.EKS, AKS, GKE, OpenShift, Rancher, and more.
Job marketMarginal.The default expectation for DevOps/SRE roles.

4.3 The same app in both — side by side

Three replicas of a web service, published on port 80, with a rolling update policy.

Docker Swarm
docker swarm init

docker service create \
  --name web \
  --replicas 3 \
  --publish published=80,target=8080 \
  --update-parallelism 1 \
  --update-delay 10s \
  --limit-cpu 0.5 --limit-memory 512M \
  nginx:1.27

docker service scale web=5
docker service update --image nginx:1.28 web
docker service rollback web
Kubernetes — equivalent, as declarative YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - containerPort: 8080
          resources:
            requests: { cpu: "250m", memory: "256Mi" }
            limits:   { cpu: "500m", memory: "512Mi" }
          readinessProbe:
            httpGet: { path: /healthz, port: 8080 }
---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

The Swarm version is shorter. That is a real advantage and you should say so honestly. But notice what the Kubernetes version gained: a readiness probe that gates the rollout, a distinction between resource requests (used for scheduling) and limits (used for enforcement), a surge policy that guarantees no capacity dips below 100% during the update, and a manifest that lives in Git and can be reconciled continuously by Argo CD.

4.4 Verdict and interview answer

Model answer "Swarm optimises for simplicity and Kubernetes optimises for capability. Swarm is a reasonable choice for a small team running a handful of stateless services who want orchestration without a platform team — setup is minutes and the mental model is Docker Compose. Kubernetes wins the moment you need autoscaling, L7 routing, network policy, storage orchestration, fine-grained RBAC, or extensibility through operators. It also wins on ecosystem and hiring, which in practice decides most real migrations. In my experience the deciding factors are usually autoscaling and the CNCF ecosystem rather than any single feature."
Nuance that impresses Add: "Swarm actually does one thing better out of the box — secrets are encrypted in the Raft log by default, whereas Kubernetes Secrets are only base64-encoded unless you configure encryption at rest." Acknowledging where the losing technology is stronger signals genuine engineering judgement rather than tribalism.
Chapter Five

Kubernetes Architecture — The Big Picture

How the pieces fit before we open each one

A Kubernetes cluster is two planes: a control plane that holds and reconciles desired state, and a data plane of worker nodes that actually runs containers. Every component belongs to one of these, and every component talks to exactly one other component — the API server.

5.1 Control plane vs data plane

PlaneComponentsResponsibility
Control planekube-apiserver, etcd, kube-scheduler, kube-controller-manager, cloud-controller-managerStore desired state, decide what should happen, and drive the cluster towards that state. Runs no user workload in a well-designed cluster.
Data planekubelet, container runtime, kube-proxy, CNI pluginActually run containers, wire up networking, report status upward.

5.2 The shared-state, level-triggered design

Three architectural rules govern everything. State them to a learner early and they will predict component behaviour correctly for the rest of the course.

Rule 1 — Only the API server touches etcd

No scheduler, no controller, no kubelet ever opens a connection to etcd. This single constraint gives Kubernetes its authentication, authorisation, validation, admission control, auditing and versioning — all enforced in one place. It also means etcd can be replaced (some distributions use SQLite via k3s' kine) without any other component knowing.

Rule 2 — Components never talk to each other

The scheduler does not call the kubelet. The controller manager does not call the scheduler. Every interaction is mediated: a component writes an object, and another component watches for that change. This is a message bus implemented as a database with watch semantics. The benefit is enormous — any component can crash and restart without a handshake protocol, because state is durable and observation is idempotent.

Rule 3 — Watch, do not poll

Components open long-lived HTTP watch streams against the API server and receive incremental updates. Client-go maintains a local informer cache so controllers read from memory rather than hammering the API. When you write a controller or operator, using informers rather than list loops is the difference between a cluster that scales to 5,000 nodes and one that melts at 200.

5.3 End-to-end request flow of kubectl apply

This is the single most valuable walkthrough in Kubernetes education. Interviewers ask it constantly because a complete answer proves you understand every component.

What happens when you run kubectl apply -f deployment.yaml
 1. kubectl  reads kubeconfig, resolves the API server endpoint and credentials.
             Converts YAML to JSON, performs client-side discovery of the resource,
             then sends POST/PATCH to  /apis/apps/v1/namespaces/prod/deployments

 2. AUTHENTICATION   - API server verifies WHO you are:
                       client certificate, bearer token, OIDC, or webhook.

 3. AUTHORIZATION    - RBAC evaluates: may this subject 'create' 'deployments'
                       in namespace 'prod'?

 4. ADMISSION (mutating)  - MutatingAdmissionWebhooks and built-in plugins may
                       CHANGE the object: inject sidecars, add default
                       ServiceAccount, apply defaulting, set imagePullSecrets.

 5. VALIDATION       - schema validation against the OpenAPI definition.

 6. ADMISSION (validating) - ValidatingAdmissionWebhooks / Policy (Kyverno, OPA,
                       Pod Security Admission) may REJECT the object.

 7. PERSIST          - object written to etcd. resourceVersion assigned.
                       API server returns 201 to kubectl.  *** You are done. ***
                       Everything below happens asynchronously.

 8. DEPLOYMENT CONTROLLER  sees the new Deployment via its watch.
                       Creates a ReplicaSet with a pod-template-hash.

 9. REPLICASET CONTROLLER  sees a ReplicaSet with spec.replicas=3 and 0 existing
                       pods. Creates 3 Pod objects - with NO nodeName set.

10. SCHEDULER       sees 3 Pods with spec.nodeName == "".  For each pod:
                       FILTER  (predicates): which nodes are even feasible?
                       SCORE   (priorities): which feasible node is best?
                       BIND:   writes  spec.nodeName = node-07  via the API server.

11. KUBELET on node-07 watches for pods bound to itself. Sees the new pod.
                       - Calls CNI ADD  -> pod gets an IP and a veth pair
                       - Calls CSI      -> mounts any volumes
                       - Calls CRI      -> pulls images, creates the pause (sandbox)
                                          container, then app containers
                       - Runs startup/liveness/readiness probes
                       - PATCHes pod status back to the API server

12. ENDPOINT / ENDPOINTSLICE CONTROLLER sees the pod become Ready and adds its
                       IP to the EndpointSlice of any matching Service.

13. KUBE-PROXY on every node watches EndpointSlices and programs iptables/IPVS
                       rules so the Service ClusterIP now load-balances to the pod.

14. COREDNS already resolves the Service name; traffic now reaches the new pod.
How to answer this in an interview Do not recite fourteen steps. Compress to the five inflection points and expand only if asked: "kubectl hits the API server, which authenticates, authorises, runs admission and writes to etcd. The Deployment controller creates a ReplicaSet, the ReplicaSet controller creates unscheduled Pods, the scheduler filters and scores nodes and binds each Pod to one, and the kubelet on that node calls CNI, CSI and CRI to actually run it. Finally the endpoint controller and kube-proxy wire it into Service routing."
The detail that separates senior from mid-level Step 7 is where the API call succeeds. Everything after it is asynchronous reconciliation. This is why kubectl apply returning "configured" tells you nothing about whether your application is running. The correct CI/CD gate is kubectl rollout status deployment/x --timeout=5m, which waits for the controller to report convergence.
Chapter Six

Control Plane Components in Depth

Every component, what it does, how it fails, and how to operate it

6.1 kube-apiserver

The API server is the front door and the only door. It is a stateless REST server that exposes the entire Kubernetes object model over HTTPS, validates everything, and is the sole writer to etcd. Because it is stateless, you scale it horizontally behind a load balancer.

Responsibilities, in order of execution

  1. TLS termination and authentication — X.509 client certificates, bearer tokens (ServiceAccount JWTs), OIDC tokens, or an authentication webhook. The result is a username, UID and set of groups.
  2. Authorization — normally RBAC, optionally Node authorizer, ABAC, or a webhook. Evaluates verb + resource + namespace against Roles and ClusterRoles bound to the subject.
  3. Mutating admission — built-in plugins plus MutatingAdmissionWebhook. This is where service meshes inject sidecars and where defaults get applied.
  4. Schema validation — against the OpenAPI v3 schema for the resource, including CRD schemas.
  5. Validating admissionValidatingAdmissionWebhook, ValidatingAdmissionPolicy (CEL-based, built in), ResourceQuota enforcement, Pod Security Admission.
  6. Persistence — write to etcd, assign a resourceVersion, emit the change to all watchers.
  7. Watch fan-out — maintain long-lived streams to every controller, kubelet and kubectl watch in the cluster.

Key flags you will actually touch

FlagPurpose
--etcd-serversetcd endpoints. Must be the full HA set.
--encryption-provider-configEnables encryption at rest for Secrets. Not on by default.
--audit-policy-file / --audit-log-pathAudit logging — mandatory in BFSI and regulated environments.
--enable-admission-pluginse.g. NodeRestriction,PodSecurity,ResourceQuota,LimitRanger.
--max-requests-inflightThrottling. Raise on large clusters; the first knob when the API server saturates.
--service-cluster-ip-rangeCIDR from which Service ClusterIPs are allocated. Immutable after cluster creation.
--anonymous-auth=falseHardening. Should be false in production.
Failure mode If all API server replicas are down: no kubectl, no deployments, no scheduling, no controller action, no scaling. But existing pods keep running — kubelet continues managing containers from its local state, and kube-proxy keeps its existing iptables rules. Traffic to healthy pods is unaffected. This is a deliberate and very important property: the control plane is not in the data path.

6.2 etcd

etcd is a distributed, strongly consistent key-value store using the Raft consensus algorithm. It holds the complete state of the cluster: every object, every status field, every secret. It is the only stateful component and therefore the only one that can lose your cluster permanently.

Raft, quorum and why you always use an odd number

Raft elects a leader; all writes go through the leader and are committed once a majority (quorum) of members have persisted them. Quorum for N members is (N/2)+1.

MembersQuorumFailures toleratedComment
110Dev only. Any loss is total loss.
220Worse than 1. Both must be up — you doubled failure probability for zero benefit.
321Standard production minimum.
532Large or highly critical clusters.
Lose quorum, lose the cluster If quorum is lost, etcd goes read-only — no new objects, no scheduling, no scaling, no rollouts. Recovery means restoring from a snapshot, which is why an untested backup is equivalent to no backup. Rehearse restore quarterly.

Backup and restore

Snapshot and restore
# --- BACKUP (run on a schedule; ship the file off-cluster) ---
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%F-%H%M).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

# --- RESTORE (stop API server + etcd on ALL control plane nodes first) ---
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-2026-01-15.db \
  --data-dir=/var/lib/etcd-restored
# Then point the etcd static pod manifest at the new data dir and restart.

Operational rules for etcd

  • Put it on fast disks. etcd is fsync-bound. SSD/NVMe is not optional.
  • Watch the database size. Default quota is 2 GiB (often raised to 8 GiB). Exceeding it puts etcd into a maintenance alarm and read-only mode.
  • Compact and defragment. Kubernetes auto-compacts revisions, but defragmentation must be scheduled — one member at a time, never simultaneously.
  • Keep latency between members low. Stretching etcd across regions is a classic mistake; across AZs in one region is fine.
  • Encrypt at rest. Secrets are plaintext-adjacent in etcd without EncryptionConfiguration.
Managed clusters On EKS, AKS and GKE, etcd is operated by the cloud provider — you cannot access it and you do not back it up. That is a genuine benefit, but it does not back up your application state. You still need Velero (or equivalent) for namespace, object and persistent-volume backup.

6.3 kube-scheduler

The scheduler watches for pods with an empty spec.nodeName and decides which node each should run on. It does not start anything — its entire output is a single write: the binding.

The two-phase algorithm

Phase 1 — Filtering (predicates): eliminate nodes that cannot run the pod — insufficient resources, hostPort conflict, nodeSelector/affinity mismatch, untolerated taints, zonal volume mismatch, anti-affinity violation, or node conditions (memory/disk/PID pressure, not Ready).

Phase 2 — Scoring (priorities): rank surviving nodes 0–100 across plugins (NodeResourcesFit, ImageLocality, InterPodAffinity, PodTopologySpread, TaintToleration, NodeAffinity), apply weights, pick the highest total.

Influencing the scheduler from a Pod spec
spec:
  priorityClassName: high-priority          # enables preemption of lower priority
  nodeSelector:
    disktype: ssd
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:   # HARD
        nodeSelectorTerms:
          - matchExpressions:
              - key: topology.kubernetes.io/zone
                operator: In
                values: ["ap-south-1a", "ap-south-1b"]
    podAntiAffinity:                        # never two replicas on one node
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchLabels: { app: payments-api }
          topologyKey: kubernetes.io/hostname
  tolerations:
    - key: "workload"
      operator: "Equal"
      value: "banking"
      effect: "NoSchedule"
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: topology.kubernetes.io/zone
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels: { app: payments-api }
Debugging Pending pods kubectl describe pod <name> and read the Events section. The scheduler writes exactly why each node was rejected — for example 0/12 nodes are available: 5 Insufficient cpu, 4 node(s) had untolerated taint. That one line usually solves the ticket.

6.4 kube-controller-manager

A single binary that runs roughly thirty independent control loops in separate goroutines. Each one watches a resource type and drives reality towards spec. Understanding that it is many controllers, not one, is what makes Kubernetes behaviour predictable.

ControllerWhat it reconciles
DeploymentCreates and scales ReplicaSets to execute rollouts; manages revision history and rollback.
ReplicaSetCreates/deletes Pods so that the count matching the selector equals spec.replicas.
NodeWatches node heartbeats (Leases). Marks nodes NotReady, applies NoExecute taints, triggers eviction after the toleration period (default 300s).
Endpoints / EndpointSliceMaintain the list of ready pod IPs behind each Service.
NamespaceOn namespace deletion, deletes every object inside it — the source of "namespace stuck in Terminating" when a finalizer blocks.
Garbage collectorDeletes objects whose ownerReferences point to a deleted parent — why deleting a Deployment removes its ReplicaSets and Pods.
HPAReads metrics, computes desired replicas, patches the scale subresource.

Leader election

In an HA control plane you run three controller-manager instances, but only one is active. They compete for a Lease object; the holder renews it, and if it fails to renew, another takes over. Without this, three controllers would each create three replicas for a three-replica ReplicaSet. The same mechanism protects the scheduler.

6.5 cloud-controller-manager

Kubernetes was originally full of cloud-specific code. That was extracted into the cloud-controller-manager so the core could stay vendor-neutral. On EKS and AKS this component is managed for you, but you must understand what it does because it explains several everyday behaviours.

Sub-controllerBehaviour
Node controllerEnriches Node objects with cloud metadata — instance type, region, zone labels, provider ID — and deletes Node objects when the underlying VM is terminated.
Route controllerPrograms cloud VPC/VNet route tables so pod CIDRs are reachable between nodes.
Service controllerWhen you create a Service of type LoadBalancer, this creates the actual AWS NLB/ALB or Azure Load Balancer and writes the external IP/hostname into status.loadBalancer.
Why your LoadBalancer Service says <pending> Because no cloud-controller-manager is running, or it lacks IAM/RBAC permission to create load balancers, or the subnets are not tagged correctly. On EKS the classic cause is missing kubernetes.io/role/elb tags on public subnets. On bare metal there is no cloud provider — install MetalLB or use NodePort/Ingress.
Chapter Seven

Worker Node Components in Depth

kubelet, the container runtime, and kube-proxy

7.1 kubelet

The kubelet is the node agent — the only component that actually causes containers to exist. It watches the API server for pods bound to its own node, then drives the runtime, network and storage plugins until the observed state matches the pod spec.

The pause container — a question that catches people out

Every pod has a hidden container, usually called pause or the "sandbox" or "infra" container. It does almost nothing: it starts, reaps zombie processes, and sleeps. Its purpose is to own the pod's Linux namespaces. Application containers join the pause container's network and IPC namespaces, which is precisely why containers in a pod share one IP and can reach each other on localhost. If an app container crashes and restarts, the pause container survives, so the pod keeps the same IP.

Node-pressure eviction — the ranking that decides who dies

When a node runs low on memory or disk, kubelet evicts pods before the kernel OOM killer acts. The order is by QoS class, then by how far the pod exceeds its requests:

QoS classHow you get itEviction order
GuaranteedEvery container has requests == limits for both CPU and memory.Evicted last. Use for critical stateful workloads.
BurstableAt least one container has a request, but requests != limits.Evicted second, ordered by usage above requests.
BestEffortNo requests or limits set at all.Evicted first. Never ship production workloads this way.

Static pods

kubelet also reads manifests directly from /etc/kubernetes/manifests/ and runs them without any control plane involvement. This is the bootstrap trick: on a kubeadm cluster, the API server, etcd, scheduler and controller-manager are themselves static pods managed by kubelet.

7.2 Container runtime and the CRI

kubelet does not know how to start a container. It speaks the Container Runtime Interface — a gRPC API — to whatever runtime is installed. This is why "Kubernetes removed Docker" was true and yet changed nothing for developers.

LayerExamplesRole
OrchestratorkubeletDecides what should run.
CRI interfacegRPC over a unix socketRuntimeService (sandboxes, containers, exec, logs) and ImageService (pull, list, remove).
High-level runtimecontainerd, CRI-OImage pull and management, snapshotting, sandbox lifecycle.
Low-level runtime (OCI)runc, crun, gVisor, KataCreates the Linux namespaces, cgroups, seccomp and capabilities, then execs the process.
Interview answer on dockershim "Kubernetes deprecated dockershim, not Docker images. The kubelet speaks CRI; containerd and CRI-O implement it, and both run OCI images built by Docker without modification. Practically, the change affected node tooling — docker ps became crictl ps — and nothing in the developer workflow."

7.3 kube-proxy and Service implementation

kube-proxy runs on every node and implements the Service abstraction. It does not proxy traffic itself in normal modes — it programs the kernel to do the load balancing, then gets out of the way.

ModeMechanismCharacteristics
iptablesChains of NAT rules with statistical matching.Default for years. Random selection. O(n) rule evaluation — degrades beyond a few thousand services.
ipvsLinux IPVS kernel load balancer with hash tables.O(1) lookup, scales to tens of thousands of services. Recommended for large clusters.
nftablesNewer kernel packet framework.Better scaling and update performance than iptables mode.
eBPF (no kube-proxy)Cilium/Calico replace kube-proxy with eBPF programs.Lowest latency, best scale, socket-level load balancing. Increasingly the production choice.
Service typeBehaviour
ClusterIPDefault. Virtual IP reachable only inside the cluster.
NodePortOpens the same high port (30000–32767) on every node.
LoadBalancerNodePort plus a cloud load balancer provisioned by the cloud-controller-manager.
ExternalNameNo proxying — CoreDNS returns a CNAME to an external DNS name.
Headless (clusterIP: None)No virtual IP. DNS returns pod IPs directly. Essential for StatefulSets.
externalTrafficPolicy — a real production trade-off Cluster (default) lets any node forward to a pod on any node — even spread, but an extra hop and the client source IP is lost to SNAT. Local forwards only to pods on the receiving node — preserves the client IP and removes the hop, but traffic is uneven if pods are not spread evenly. Choose Local when you need real client IPs; pair it with a topology spread constraint.
Chapter Eight

Cluster Add-ons

The components that are not "core" but without which nothing works

A cluster with only the core components can schedule pods — and almost nothing else. No DNS, no metrics, no ingress, no dynamic storage. Add-ons fill that gap. They are ordinary Kubernetes workloads that happen to provide platform capability — Kubernetes extends itself using its own primitives.

8.1 CoreDNS

CoreDNS is the cluster DNS server, running as a Deployment in kube-system fronted by a Service with a well-known ClusterIP (typically 10.96.0.10). Every kubelet writes that IP into every pod's /etc/resolv.conf.

RecordResolves to
paymentsThe Service in the caller's own namespace (via search domains).
payments.production.svc.cluster.localThe fully qualified name. ClusterIP of the Service.
postgres-0.postgres.production.svc.cluster.localA specific StatefulSet pod via a headless Service.
The ndots:5 performance trap ndots:5 means any name with fewer than five dots is first tried against every search domain. Looking up api.stripe.com (2 dots) generates four failing queries before the correct one. At high request rates this floods CoreDNS. Fix: use a trailing dot (api.stripe.com.), set dnsConfig.options: [{name: ndots, value: "2"}], or enable NodeLocal DNSCache.

8.2 metrics-server and the metrics pipeline

PipelineProviderPurpose
Resource metrics (metrics.k8s.io)metrics-serverLive CPU/memory only, not stored. Powers kubectl top and default HPA targets.
Custom/external metricsPrometheus Adapter, KEDAAny metric — request rate, queue depth, latency. Powers advanced HPA scaling.
Monitoring (not an API)Prometheus, GrafanaLong-term storage, dashboards, alerting, SLOs.
HPA shows <unknown>/80% Almost always: metrics-server not installed/Available; the pods have no CPU requests (HPA computes utilisation as a percentage of requests — no request, no denominator); or metrics-server cannot reach kubelets because of TLS.

8.3 Ingress controllers

An Ingress object is only a set of L7 routing rules stored in the API. It does nothing on its own. An Ingress controller (ingress-nginx, AWS Load Balancer Controller, AGIC, Traefik, or a Gateway API implementation) is the workload that watches Ingress objects and configures a real proxy. Without a controller installed, creating an Ingress produces no effect whatsoever.

Ingress with TLS and path routing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: banking-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts: ["api.bank.example.com"]
      secretName: bank-tls          # cert-manager creates and renews this
  rules:
    - host: api.bank.example.com
      http:
        paths:
          - path: /payments
            pathType: Prefix
            backend:
              service:
                name: payments
                port:
                  number: 80

8.4 CSI storage drivers

The Container Storage Interface moved vendor code out of the Kubernetes tree into independently released drivers. A CSI driver has a controller Deployment (provision, delete, attach, snapshot) and a node DaemonSet (stage, mount, unmount).

StorageClass then PVC then Pod
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3-encrypted
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  encrypted: "true"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer   # bind only after scheduling
WaitForFirstConsumer is not optional in multi-AZ clusters With Immediate binding, the volume is provisioned in an arbitrary zone before the scheduler picks a node. If the pod later lands in a different zone, it is stuck Pending forever with a volume node affinity conflict. WaitForFirstConsumer delays provisioning until placement is known.

8.5 Autoscalers, dashboard and other add-ons

Add-onPurpose
Cluster AutoscalerAdds nodes when pods are Pending for capacity; removes underutilised nodes. Node-level.
KarpenterProvisions right-sized instances directly from pending pod requirements — usually faster and cheaper.
KEDAEvent-driven autoscaling from queue depth, Kafka lag, cron — including scale-to-zero.
cert-managerIssues and auto-renews TLS certificates from Let's Encrypt or an internal CA.
external-secretsSyncs secrets from Vault, AWS Secrets Manager or Azure Key Vault into the cluster.
Kyverno / OPA GatekeeperPolicy as code: block untrusted registries, require limits, enforce labels.
kube-state-metricsExports the state of Kubernetes objects as Prometheus metrics.
Chapter Nine

CNI and Cluster Networking

The four rules, the specification, the plugins, and NetworkPolicy

Kubernetes deliberately ships no networking implementation. It defines a model, and the Container Network Interface lets any plugin satisfy it. This is why a fresh cluster has every pod stuck in ContainerCreating until you install a CNI — and why "which CNI?" is one of the highest-consequence decisions in cluster design.

9.1 The four rules of the Kubernetes network model

  1. Every pod gets its own unique IP address across the whole cluster.
  2. Pods can communicate with all other pods on any node without NAT. The IP a pod sees itself as is the IP other pods see it as.
  3. Agents on a node (kubelet, system daemons) can reach all pods on that node.
  4. Pods in the host network namespace can communicate with all pods on all nodes without NAT.
Why this matters — the "IP-per-pod" decision Docker's default model is one IP per host plus port mapping, which forces port-conflict management and breaks any protocol that advertises its own address. Kubernetes chose one IP per pod, so every application can bind its natural port. It costs more IP address space — which is exactly the trade-off that bites on AWS VPC CNI.

9.2 What the CNI specification actually is

  • Plugin binaries live in /opt/cni/bin/.
  • Plugin configuration lives in /etc/cni/net.d/ as a JSON conflist, evaluated in lexical order — the first file wins.
  • The runtime invokes a binary with a verb via environment variables and JSON on stdin: ADD, DEL, CHECK, VERSION.
  • The plugin returns JSON describing the interfaces, IPs, routes and DNS it configured.
What happens on CNI ADD
1. kubelet asks the runtime to create the pod sandbox (pause container)
   -> a new, empty network namespace exists
2. Runtime invokes the CNI plugin with CNI_COMMAND=ADD and the namespace path
3. Plugin creates a veth pair: eth0 inside the pod, calixxxxx on the host
4. IPAM allocates an address from the node pod CIDR (or the VPC)
5. Plugin assigns the IP to eth0, sets the default route via the host end
6. Plugin programs host routing / eBPF maps so other nodes can reach this IP
7. Plugin returns JSON; kubelet records podIP in pod.status
8. On pod deletion, CNI_COMMAND=DEL tears it down and releases the IP

9.3 Plugin comparison

PluginData pathNetworkPolicyBest suited to
FlannelVXLAN overlay (or host-gw)No (needs Calico)Learning, simple clusters.
CalicoBGP routing, or VXLAN/IPIP overlay; eBPF mode availableYes — rich policyThe most common production choice.
CiliumeBPF end-to-end; can replace kube-proxyYes — including L7Large, performance/security-sensitive clusters. Hubble observability.
AWS VPC CNIPod IPs from the VPC subnet via ENI secondary addresses — no overlayNo natively (add Calico)EKS default. Pods are first-class VPC citizens.
Azure CNIPod IPs from the VNet subnet (or Overlay mode)Azure NP or CalicoAKS. Overlay mode when VNet space is scarce.
The AWS VPC CNI IP-exhaustion problem — expect this question Each EC2 instance type supports a fixed number of ENIs, each with a fixed number of secondary IPs. Max pods per node is roughly (ENIs x (IPs per ENI - 1)) + 2. A t3.medium tops out around 17 pods regardless of free CPU/memory. Mitigations: enable prefix delegation (ENABLE_PREFIX_DELEGATION=true, assigning /28 blocks), use larger instances, add secondary CIDR blocks, or switch to custom networking.

9.4 NetworkPolicy deep dive

By default, every pod can reach every other pod in the cluster. NetworkPolicy stops that. Three rules govern its semantics:

  1. Policies are additive and allow-only. There is no deny rule. The union of all matching policies is what is permitted.
  2. A pod is "isolated" for a direction the moment any policy selects it for that direction. Before that, everything is allowed; after, only what is explicitly allowed.
  3. The CNI must implement it. Applying a NetworkPolicy with Flannel or plain AWS VPC CNI silently does nothing — one of the most dangerous false-security situations in Kubernetes.
Default deny, then a tiered allow
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}              # ALL pods in this namespace
  policyTypes: [Ingress, Egress]
# no rules listed = nothing allowed
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payments-api-policy
  namespace: production
spec:
  podSelector:
    matchLabels: { app: payments-api }
  policyTypes: [Ingress, Egress]
  ingress:
    - from:
        - podSelector: { matchLabels: { app: web-frontend } }
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector: { matchLabels: { app: postgres } }
      ports:
        - protocol: TCP
          port: 5432
Two subtleties that catch experienced engineers (1) In a single from block, a namespaceSelector and podSelector as separate list items are OR; as two keys inside one item they are AND. The indentation changes the meaning — and the permissive interpretation is the accidental one.
(2) NetworkPolicy works on pod IPs, not Service IPs. Never target a ClusterIP; target the pods behind it. Also remember to allow DNS egress (UDP/TCP 53 to kube-dns) or everything breaks.
Chapter Ten

Namespace

Logical partitioning, quotas, and the limits of the isolation it provides

A Namespace is a virtual cluster inside a physical cluster: a scope for names, a target for RBAC and quotas, and a boundary for policy. It is the primary multi-tenancy primitive — and it is important to know exactly how weak that boundary is by default.

10.1 YAML anatomy and lifecycle

namespace.yaml — with the labels that matter
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    kubernetes.io/metadata.name: production   # set automatically; you select on it
    environment: production
    team: payments
    # Pod Security Admission — enforce the restricted profile
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/warn: restricted

Namespaced vs cluster-scoped

NamespacedCluster-scoped
Pod, Deployment, ReplicaSet, StatefulSet, DaemonSet, Job, CronJob, Service, Ingress, ConfigMap, Secret, PVC, ServiceAccount, Role, RoleBinding, NetworkPolicy, HPA, ResourceQuota, LimitRangeNode, PersistentVolume, StorageClass, ClusterRole, ClusterRoleBinding, CustomResourceDefinition, Namespace itself, IngressClass, PriorityClass

Terminating namespaces — the stuck-forever problem

Deleting a namespace triggers the namespace controller to delete every object inside it. If any object has a finalizer that never completes — commonly a CRD whose operator has already been uninstalled — the namespace sits in Terminating indefinitely.

Do not reflexively force-remove namespace finalizers The widely-circulated trick of PUTting an empty finalizer list to /api/v1/namespaces/x/finalize makes the namespace disappear but orphans the real resources — cloud load balancers, disks and DNS records keep existing and keep billing. Always find and fix the actual blocking finalizer first.

10.2 ResourceQuota and LimitRange

ResourceQuota — a ceiling for the whole namespace
apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    requests.cpu: "100"
    requests.memory: 200Gi
    limits.cpu: "200"
    limits.memory: 400Gi
    pods: "300"
    services.loadbalancers: "5"   # cost control — each one is a real cloud LB
    services.nodeports: "0"       # forbid NodePort entirely
The rule people trip over Once a ResourceQuota constrains requests.cpu or limits.memory in a namespace, every new pod must declare those fields or creation is rejected. Existing pods keep running, but the next rollout fails with must specify limits.memory. Pair every ResourceQuota with a LimitRange so defaults are supplied automatically.
LimitRange — defaults and per-object bounds
apiVersion: v1
kind: LimitRange
metadata:
  name: production-limits
  namespace: production
spec:
  limits:
    - type: Container
      default:          { cpu: "500m", memory: "512Mi" }   # applied as limits
      defaultRequest:   { cpu: "100m", memory: "128Mi" }   # applied as requests
      max:              { cpu: "4",    memory: "8Gi" }
      min:              { cpu: "50m",  memory: "64Mi" }
What Namespaces do NOT isolate Namespaces are a naming and policy boundary, not a security boundary on their own. Without additional controls, pods in different namespaces can still reach each other over the network (until NetworkPolicy), share the same kernel and nodes, and see cluster-scoped resources. Hostile multi-tenancy requires separate clusters or virtual-cluster technology.
Chapter Eleven

Pod — The Atomic Unit

The most important object in Kubernetes, field by field

11.1 Why a Pod and not a container

A Pod is one or more containers that are always co-scheduled on the same node, share a network namespace and IP, share IPC, and can share volumes. It is the smallest thing Kubernetes schedules — you cannot schedule a container.

Why introduce this extra layer? Because some processes are genuinely a single unit of deployment even though they are separate binaries — a web server and a log shipper that tails its files; an application and a proxy that terminates mTLS on its behalf. These must live on the same machine, start together, die together, and share a filesystem or loopback interface.

Port conflicts inside a pod Because containers in a pod share one network namespace, two containers cannot both bind port 8080. This is the most common mistake when adding a sidecar. Give each its own port.

11.2 A production-grade Pod spec (abridged)

Key fields, annotated
apiVersion: v1
kind: Pod
metadata:
  name: payments-api
  namespace: production
  labels: { app: payments-api, tier: backend, version: "2.4.1" }
spec:
  serviceAccountName: payments-sa
  automountServiceAccountToken: false    # only mount if the app calls the API
  terminationGracePeriodSeconds: 60
  securityContext:
    runAsNonRoot: true
    runAsUser: 10001
    fsGroup: 10001
    seccompProfile: { type: RuntimeDefault }
  initContainers:
    - name: wait-for-db                   # run to completion, before app starts
      image: busybox:1.36
      command: ["sh","-c","until nc -z postgres 5432; do sleep 2; done"]
  containers:
    - name: app
      image: registry.example.com/payments-api:2.4.1   # NEVER use :latest
      ports:
        - { name: http, containerPort: 8080 }
      env:
        - name: POD_NAME                  # downward API
          valueFrom: { fieldRef: { fieldPath: metadata.name } }
        - name: DB_PASSWORD
          valueFrom: { secretKeyRef: { name: payments-db, key: password } }
      resources:
        requests: { cpu: "250m", memory: "512Mi" }   # used for SCHEDULING
        limits:   { cpu: "1",    memory: "1Gi" }      # used for ENFORCEMENT
      startupProbe:                       # protects slow starters from liveness
        httpGet: { path: /healthz/startup, port: http }
        failureThreshold: 30
        periodSeconds: 5
      livenessProbe:                      # failing => container RESTARTED
        httpGet: { path: /healthz/live, port: http }
        periodSeconds: 10
      readinessProbe:                     # failing => removed from endpoints
        httpGet: { path: /healthz/ready, port: http }
        periodSeconds: 5
      lifecycle:
        preStop:
          exec: { command: ["sh","-c","sleep 10"] }
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities: { drop: ["ALL"] }

11.3 Lifecycle, phases and conditions

Phases (Pending, Running, Succeeded, Failed, Unknown) are coarse. Conditions carry the useful detail: PodScheduled, Initialized, ContainersReady, Ready. A pod can be Running and not Ready — exactly the state where readiness probes are keeping it out of the load balancer.

CrashLoopBackOff is not an error — it is a backoff timer. The container is exiting, kubelet is restarting it, and the delay doubles: 10s, 20s, 40s, 80s, 160s, capped at 5 minutes. The actual failure reason is in the previous container's logs.

Diagnosing a crashing pod
kubectl logs payments-api-7d9f --previous       # logs of the CRASHED instance
kubectl describe pod payments-api-7d9f          # events + exit codes + reasons

# Exit code meanings
#   0 clean   1 app error   137 SIGKILL (usually OOMKilled)
# 139 SIGSEGV   143 SIGTERM   127 command not found

11.4 Probes, init containers, sidecars

ProbeOn failureUse it for
startupProbeKills the container after threshold; suppresses liveness/readiness until it first succeeds.Slow-starting apps — JVM warm-up, large cache load, migrations.
livenessProbeContainer is restarted.Deadlock detection only. Test that this process is alive — never a dependency.
readinessProbePod IP removed from Service endpoints. No restart."Can I serve traffic right now?" Legitimately checks dependencies and warm caches.
The cascading-failure anti-pattern Never check a database in a liveness probe. If the database has a brief outage, every replica of every service fails liveness simultaneously, all restart, all lose their connection pools, and the recovery stampede takes the database down again. Dependencies belong in readiness.

11.5 Resources, QoS classes and eviction

ResourceExceeding the limitImplication
CPUThrottled by the CFS scheduler. Slowed, not killed.CPU is compressible. Symptom of a too-low limit is latency, not crashes — invisible unless you watch container_cpu_cfs_throttled_seconds_total.
MemoryOOMKilled — the kernel terminates the process immediately.Memory is incompressible. No graceful degradation. Exit code 137.

11.6 Scheduling controls and security context

  • Taints are on nodes and repel pods: "nothing runs here unless it explicitly tolerates me."
  • Tolerations are on pods and merely permit scheduling onto a tainted node — they do not attract.
  • Node affinity is on pods and attracts: "put me on nodes that look like this."
  • To dedicate nodes to a workload you need both: taint the nodes and add node affinity/nodeSelector to the workload.
Taint effects
kubectl taint nodes node-07 workload=banking:NoSchedule
#   NoSchedule        - no new pods without a matching toleration
#   PreferNoSchedule  - soft; scheduler avoids if it can
#   NoExecute         - as NoSchedule, AND evicts running pods that do not tolerate
kubectl taint nodes node-07 workload=banking:NoSchedule-   # trailing dash removes
Chapter Twelve

Deployment and ReplicaSet

The workhorse for stateless services

12.1 The three-object chain

You create a Deployment. It creates a ReplicaSet. That creates Pods. Three objects, three controllers, one purpose — and understanding the division of labour explains every rollout behaviour you will ever debug.

Ownership chain
Deployment  payments-api         owns rollout strategy, revision history, rollback
  |
  |-- ReplicaSet  ...-6d4b8f9c7   (revision 3 - CURRENT, replicas: 3)
  |       |-- Pod ...-x7k2p
  |       |-- Pod ...-m9q4r
  |       |-- Pod ...-t2v8n
  |-- ReplicaSet  ...-5f9c7d8b6   (revision 2 - OLD, replicas: 0)
  |-- ReplicaSet  ...-4c8b6a5d9   (revision 1 - OLD, replicas: 0)

Old ReplicaSets are kept, scaled to zero, so rollback is instant.
revisionHistoryLimit controls how many are retained (default 10).

The pod-template-hash label makes this work. The Deployment controller hashes the pod template and adds it to the ReplicaSet's selector and pods' labels. Change the template, get a new hash, get a new ReplicaSet. Change only replicas and the hash is unchanged — which is why scaling does not create a new revision.

12.2 Production Deployment YAML (abridged)

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payments-api
  namespace: production
  annotations:
    kubernetes.io/change-cause: "Upgrade to 2.4.1 - fixes settlement retry bug"
spec:
  replicas: 6
  selector:
    matchLabels: { app: payments-api }   # IMMUTABLE after creation
  revisionHistoryLimit: 5
  progressDeadlineSeconds: 600           # mark Failed if no progress in 10 min
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 2
      maxUnavailable: 0                   # never dip below replicas ready - safest
  template:
    metadata:
      labels: { app: payments-api, version: "2.4.1" }
    spec:
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector: { matchLabels: { app: payments-api } }
      containers:
        - name: app
          image: registry.example.com/payments-api:2.4.1
          resources:
            requests: { cpu: "250m", memory: "512Mi" }
            limits:   { cpu: "1",    memory: "512Mi" }
---
apiVersion: policy/v1
kind: PodDisruptionBudget          # protects you during VOLUNTARY disruption
metadata:
  name: payments-api-pdb
  namespace: production
spec:
  minAvailable: 4
  selector:
    matchLabels: { app: payments-api }
selector is immutable You cannot change spec.selector on an existing Deployment — it fails with field is immutable. The only path is delete and recreate. Choose selector labels carefully on day one and keep them minimal (app: name is enough; do not put version in the selector).

12.3 Rolling update mathematics

With replicas: 10, maxSurge: 2, maxUnavailable: 2: maximum total pods at any moment is replicas + maxSurge = 12; minimum available is replicas - maxUnavailable = 8. The controller never proceeds past a batch whose new pods are not Ready — a broken image or failing readiness probe stalls the rollout and leaves the old pods serving traffic, which is exactly the desired behaviour.

ConfigurationBehaviour
maxSurge: 1, maxUnavailable: 0Safest default. Full capacity maintained throughout. Requires headroom for one extra pod.
maxSurge: 0, maxUnavailable: 1Never exceeds the replica count. Use when capacity is hard-capped. Capacity dips during rollout.
strategy.type: RecreateTerminate everything, then start the new version. Causes downtime. Necessary for RWO volumes or incompatible schema migrations.

12.4 Rollout, rollback and deployment strategies

The rollout command set
kubectl set image deployment/payments-api app=payments-api:2.4.2 -n production
kubectl rollout status deployment/payments-api -n production --timeout=10m   # gate CI on this
kubectl rollout history deployment/payments-api -n production
kubectl rollout undo deployment/payments-api -n production                    # previous
kubectl rollout undo deployment/payments-api --to-revision=3 -n production
kubectl rollout restart deployment/payments-api -n production                 # re-roll same image

Beyond native rolling updates: Blue-Green (two full Deployments, flip the Service selector — instant switch and rollback, needs double capacity); Canary (small traffic percentage to the new version, promoted on metrics via Argo Rollouts or Flagger); and Shadow (duplicate real traffic to the new version, discard responses).

The ConfigMap rollout problem Changing a ConfigMap does not restart pods that consume it. Env-var references never update; volume-mounted files update after a delay but the process must reload them. Fix by annotating the pod template with a checksum of the config so any change triggers a rollout, or make ConfigMaps immutable and versioned by name (Kustomize configMapGenerator with a hash suffix).
Chapter Thirteen

DaemonSet

Exactly one pod per node — the shape of all node-level infrastructure

13.1 Use cases and scheduling behaviour

A DaemonSet guarantees that a copy of a pod runs on every node matching its selector. When a node joins the cluster, a pod appears on it automatically. When a node leaves, the pod is garbage collected. There is no replicas field — the node count is the replica count.

CategoryExamples
Networkingcalico-node, cilium-agent, aws-node (VPC CNI), kube-proxy, NodeLocal DNSCache
StorageCSI node drivers (ebs-csi-node, azuredisk-csi-node)
LoggingFluent Bit, Fluentd, Filebeat, Vector, Datadog Agent
Metricsnode-exporter, New Relic infrastructure agent
SecurityFalco, Sysdig, vulnerability scanners

Since v1.12 DaemonSet pods are scheduled by the default scheduler using node affinity the controller injects, so they respect taints, priority, preemption and resource availability — and can be left Pending if a node is full.

Give DaemonSets a high priority class Infrastructure daemons should not be evicted or left pending because application pods filled the node. Use priorityClassName: system-node-critical for CNI/CSI/kube-proxy-class workloads and system-cluster-critical for cluster-wide essentials.

13.2 Production DaemonSet YAML (abridged)

A log-collection agent
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluent-bit
  namespace: logging
spec:
  selector:
    matchLabels: { app: fluent-bit }
  # NOTE: there is NO spec.replicas — node count determines it
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1                  # how many nodes updated at once
  template:
    metadata:
      labels: { app: fluent-bit }
    spec:
      tolerations:
        - operator: Exists               # tolerate EVERYTHING (run on every node)
      priorityClassName: system-node-critical
      containers:
        - name: fluent-bit
          image: fluent/fluent-bit:3.0.7
          resources:
            requests: { cpu: "50m",  memory: "96Mi" }   # SMALL - charged on every node
            limits:   { cpu: "300m", memory: "256Mi" }
          volumeMounts:
            - { name: varlog, mountPath: /var/log, readOnly: true }
      volumes:
        - name: varlog                   # hostPath is the defining characteristic:
          hostPath:                      # DaemonSets exist to read/write the NODE
            path: /var/log
            type: Directory

13.3 Tolerations, host access and update strategy

tolerations: [{operator: Exists}] means "tolerate every taint, including ones invented later." For a logging or security agent that must cover every node, that is correct. For anything else it is dangerous — your pod lands on cordoned nodes, reserved GPU nodes, and nodes under disk pressure. Prefer enumerating specific taints.

Resource requests on a DaemonSet are multiplied by every node A DaemonSet requesting 500m CPU and 1Gi memory on a 200-node cluster reserves 100 CPUs and 200 GiB that no application can use. Node-level agents should be lean. Measure actual usage and set requests close to the real p95.
Finding nodes missing a pod (usually a taint you did not tolerate)
kubectl -n logging get pods -l app=fluent-bit -o wide
kubectl get nodes -o name | sed 's|node/||' | sort > /tmp/all-nodes
kubectl -n logging get pods -l app=fluent-bit \
  -o jsonpath='{.items[*].spec.nodeName}' | tr ' ' '\n' | sort > /tmp/covered
comm -23 /tmp/all-nodes /tmp/covered
Chapter Fourteen

StatefulSet

Stable identity, ordered lifecycle and per-replica storage

14.1 Stable identity, ordering and storage

A Deployment treats its pods as interchangeable cattle: random names, random start order, shared storage or none. A StatefulSet treats them as individuals with permanent identities. That is exactly what clustered databases, queues and consensus systems require.

GuaranteeDetail
Stable network identityPods are named <sts>-<ordinal>postgres-0, postgres-1. The name survives rescheduling. With a headless Service, each pod gets its own DNS record.
Stable storagevolumeClaimTemplates creates one PVC per pod. If postgres-1 is rescheduled, it reattaches to its own volume.
Ordered lifecycleCreation is sequential 0 → 1 → 2, each waiting for the previous to be Ready. Deletion and scale-down are reverse order. Updates go highest ordinal to lowest.

14.2 Production StatefulSet YAML (abridged)

PostgreSQL with per-replica storage
apiVersion: v1
kind: Service
metadata: { name: postgres, namespace: production }
spec:
  clusterIP: None                      # HEADLESS: DNS returns individual pod IPs
  selector: { app: postgres }
  ports: [{ name: postgres, port: 5432 }]
---
apiVersion: apps/v1
kind: StatefulSet
metadata: { name: postgres, namespace: production }
spec:
  serviceName: postgres                # MUST match the headless Service name
  replicas: 3
  selector:
    matchLabels: { app: postgres }
  podManagementPolicy: OrderedReady    # OrderedReady | Parallel
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0                     # only ordinals >= partition are updated
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Retain                # keep data if the STS is deleted
    whenScaled: Retain
  template:
    metadata:
      labels: { app: postgres }
    spec:
      terminationGracePeriodSeconds: 120   # give the DB time to checkpoint
      affinity:
        podAntiAffinity:               # never two DB replicas on one node
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector: { matchLabels: { app: postgres } }
              topologyKey: kubernetes.io/hostname
      containers:
        - name: postgres
          image: postgres:16.4
          resources:
            requests: { cpu: "1", memory: "4Gi" }
            limits:   { cpu: "2", memory: "4Gi" }   # Guaranteed QoS
  volumeClaimTemplates:
    - metadata: { name: data }         # produces data-postgres-0, -1, -2
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: gp3-encrypted
        resources: { requests: { storage: 500Gi } }

14.3 Headless Services and DNS records

What DNS returns
# Headless Service name -> A records for ALL ready pod IPs
postgres.production.svc.cluster.local             -> 10.244.1.7, 10.244.2.9, 10.244.3.4
# Per-pod stable record - THIS is the point of a StatefulSet
postgres-0.postgres.production.svc.cluster.local  -> 10.244.1.7
postgres-1.postgres.production.svc.cluster.local  -> 10.244.2.9

Because names are deterministic, a clustered application configures itself from its own ordinal — a Kafka broker's ID is the ordinal, a Postgres replica knows the primary is always -0. No service registry, no discovery protocol.

14.4 Scaling, updates and the operational traps

TrapWhat happens and what to do
PVCs are never deleted automaticallyDeleting the StatefulSet leaves every PVC and its cloud disk behind — and its bill. Handle deliberately with persistentVolumeClaimRetentionPolicy or explicit cleanup.
Scaling down destroys quorumScaling a 3-node etcd/Zookeeper/Kafka to 1 breaks consensus. Kubernetes will happily do it. Scale stateful systems only with a documented procedure.
volumeClaimTemplates is immutableYou cannot resize storage by editing the template. Patch each PVC individually (with allowVolumeExpansion: true), then recreate the STS with --cascade=orphan.
A stuck pod blocks everythingWith OrderedReady, if postgres-1 never becomes Ready, postgres-2 is never created. Look at the lowest unhealthy ordinal.
Node failure with RWO volumesAn EBS/Azure Disk cannot detach from an unreachable node immediately. The pod stays Terminating and the replacement Pending until the attach-detach controller forces detachment (six minutes by default).
StatefulSet is not a database operatorIt gives identity and storage, nothing about replication, failover or backup. For production databases use an operator — CloudNativePG, Percona, Strimzi for Kafka.
Chapter Fifteen

Choosing the Right Workload Object

A decision table you can hand to a team
ObjectUse whenDo not use when
PodDebugging, one-off exec containers, teaching.Anything in production — a bare Pod is never recreated if its node dies.
ReplicaSetAlmost never created directly.Use a Deployment, which manages ReplicaSets and adds rollout/rollback.
DeploymentStateless services: APIs, web front ends, workers with no local state. The default.The workload needs stable names, ordered startup, or per-replica storage.
StatefulSetDatabases, message brokers, consensus systems.The app is stateless — StatefulSets are slower to scale and recover.
DaemonSetNode-level agents: logging, metrics, CNI, CSI, security.You want N replicas independent of node count.
JobRun-to-completion batch: migrations, reports, ETL, backfills.The work must run continuously.
CronJobScheduled Jobs: nightly backups, periodic reconciliation.Sub-minute scheduling, or work that must never overlap (without concurrencyPolicy: Forbid).
The decision tree, compressed
Does it run once and finish?
|- YES -> on a schedule?  --- YES -> CronJob
|                         --- NO  -> Job
|- NO  -> does it need one pod on EVERY node?
          |- YES -> DaemonSet
          |- NO  -> does any replica need a stable name,
                    a stable disk, or ordered startup?
                    |- YES -> StatefulSet   (and consider an Operator)
                    |- NO  -> Deployment    <- the answer ~80% of the time
Chapter Sixteen

Troubleshooting Playbook

Symptom, cause, command — for the failures you will actually meet

16.1 Pod will not start

StatusLikely causeFirst command
PendingNo node satisfies the pod: insufficient CPU/memory, untolerated taint, affinity conflict, unbound PVC, zonal volume mismatch.kubectl describe pod X — read the scheduler's rejection line.
ImagePullBackOffWrong image/tag, private registry without imagePullSecrets, registry rate limit, no route.kubectl describe pod X; then crictl pull <image>.
CrashLoopBackOffProcess exits immediately: bad config, missing env var, failed dependency, over-aggressive liveness.kubectl logs X --previous
OOMKilled (137)Memory limit too low, or a leak. Heap not aligned with the container limit.kubectl describe pod X then check memory over time.
CreateContainerConfigErrorA referenced ConfigMap or Secret key does not exist.kubectl describe pod X names the missing object.
ContainerCreating (stuck)CNI failure (no IP), volume mount failure, or image still pulling.kubectl describe pod X; then journalctl -u kubelet.
Terminating (stuck)A finalizer, a preStop hook that never returns, or a volume that will not detach.kubectl get pod X -o yaml | grep -A5 finalizers

16.2 Service or DNS problems

Work down the chain — each step isolates one layer
# 1. Does the Service have endpoints at all?
kubectl get endpointslices -l kubernetes.io/service-name=payments -n production
#   EMPTY => selector matches no READY pod (label mismatch OR readiness failing)

# 2. Compare selector to actual pod labels
kubectl get svc payments -n production -o jsonpath='{.spec.selector}'
kubectl get pods -n production --show-labels | grep payments

# 3. Reach the pod directly, bypassing the Service?
kubectl -n production port-forward pod/payments-abc123 8080:8080

# 4. Reach the ClusterIP from another pod?
kubectl -n production run tester --rm -it --image=nicolaka/netshoot -- \
  curl -v http://payments:80/healthz

# 5. Is DNS working?
kubectl -n production run tester --rm -it --image=nicolaka/netshoot -- \
  nslookup payments.production.svc.cluster.local

# 6. Is a NetworkPolicy blocking it?
kubectl get networkpolicy -n production

16.3 Node and cluster level

Node triage
kubectl get nodes -o wide
kubectl describe node node-07          # conditions, allocatable, taints, pods
kubectl top nodes
kubectl get pods -A --field-selector spec.nodeName=node-07 -o wide

# Safely take a node out of service
kubectl cordon node-07
kubectl drain node-07 --ignore-daemonsets --delete-emptydir-data --grace-period=60
kubectl uncordon node-07

# Control plane health
kubectl get --raw='/readyz?verbose'
kubectl get events -A --sort-by='.lastTimestamp' | tail -50

16.4 The universal debugging toolkit

Commands worth memorising
# Ephemeral debug container on a running pod (image has no shell? fine)
kubectl debug -it payments-abc123 --image=nicolaka/netshoot --target=app
# Debug pod on a specific node, host filesystem access
kubectl debug node/node-07 -it --image=busybox
# What can this ServiceAccount actually do?
kubectl auth can-i --list --as=system:serviceaccount:production:payments-sa
# Diff before applying — essential in GitOps reviews
kubectl diff -f deployment.yaml
Chapter Seventeen

Command and YAML Reference

The working set, condensed

17.1 Discovery and self-documentation

Never memorise a field again
kubectl api-resources                          # every kind, its group, short name
kubectl api-versions                           # every group/version served
kubectl explain deployment.spec.strategy       # the schema, from the live server
kubectl explain pod.spec.containers.resources --recursive
kubectl create deployment web --image=nginx --dry-run=client -o yaml > deploy.yaml

17.2 Imperative generators (scaffold fast, then commit the YAML)

Fast scaffolding
kubectl create ns production
kubectl create deployment web --image=nginx:1.27 --replicas=3 --dry-run=client -o yaml
kubectl expose deployment web --port=80 --target-port=8080 --dry-run=client -o yaml
kubectl create configmap app-config --from-file=./config/ --dry-run=client -o yaml
kubectl create secret generic db-creds --from-literal=password=s3cr3t --dry-run=client -o yaml
kubectl create job migrate --image=myapp:1.0 -- /app/migrate
kubectl create cronjob backup --image=backup:1.0 --schedule="0 2 * * *" -- /backup.sh
kubectl create rolebinding dev-binding --role=dev --serviceaccount=production:payments-sa

17.3 Everyday operations

The daily set
kubectl get pods -A -o wide
kubectl get pods --field-selector status.phase=Failed -A
kubectl get pods --sort-by=.status.containerStatuses[0].restartCount -A
kubectl logs -f deployment/payments-api --all-containers --max-log-requests=10
kubectl logs -l app=payments-api --tail=100 --since=15m --prefix
kubectl exec -it payments-abc123 -c app -- sh
kubectl port-forward svc/payments 8080:80 -n production
kubectl apply -k ./overlays/production          # Kustomize
kubectl patch deployment payments-api -p '{"spec":{"replicas":8}}'

17.4 The manifest skeletons

HorizontalPodAutoscaler + CronJob
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: payments-api, namespace: production }
spec:
  scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: payments-api }
  minReplicas: 4
  maxReplicas: 40
  metrics:
    - type: Resource
      resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } }
  behavior:
    scaleDown: { stabilizationWindowSeconds: 300 }   # avoid flapping
---
apiVersion: batch/v1
kind: CronJob
metadata: { name: nightly-backup, namespace: production }
spec:
  schedule: "0 2 * * *"
  timeZone: "Asia/Kolkata"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  jobTemplate:
    spec:
      backoffLimit: 2
      ttlSecondsAfterFinished: 86400
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: backup-tool:2.1
              command: ["/backup.sh"]

Where to go next

The natural progression from this handbook: Helm for packaging, Kustomize for environment overlays, Argo CD for GitOps delivery, Prometheus and Grafana for SLO-driven operations, and Operators and CRDs for extending the control plane. Certification paths worth targeting are CKA (operations), CKAD (workloads) and CKS (security) — all hands-on. Cloud Soft Solutions runs these in the APEX & NEXUS programs in Ameerpet, Hyderabad.

How to Prepare and Where to Go Next

This handbook covers the fundamentals interviewers and the CKA/CKAD/CKS exams test hardest. To go job-ready, pair it with hands-on practice and our related guides: the Cloud + DevOps & Agentic AI roadmap, the Fresher-to-Hired 2026 roadmap, and our AI & ML and RAG interview-question sets. Browse live openings in the 2026 fresher jobs hub.

Learn Kubernetes Hands-On

APEX — AI, ML, Cloud & Cyber Security Engineering Program

Docker, Kubernetes, Terraform, CI/CD and cloud-native deployment on real projects, with interview prep and a 100% placement guarantee — in Ameerpet, Hyderabad (classroom + live online).

Explore the APEX Program →

📞 Want a structured DevOps/Kubernetes learning path or placement help? Call or WhatsApp +91 96660 19191 / +91 99496 16388, or email info@cloudsoftsol.com. Explore our Cloud & DevOps training and full course catalogue.

Frequently Asked Questions

What is Kubernetes in simple terms?

Kubernetes (K8s) is an open-source platform that automates the deployment, scaling, healing and operation of containerised applications across a fleet of machines. You declare a desired state — how many replicas, how much CPU, which config — and Kubernetes continuously compares that to reality and closes the gap, restarting crashed containers, rescheduling pods off dead nodes and rolling out new versions safely.

What are the main Kubernetes control plane components?

The control plane is the API server (the only front door and the sole writer to etcd), etcd (the strongly-consistent key-value store holding all cluster state), the scheduler (which binds pods to nodes via filtering and scoring), the controller-manager (roughly 30 reconciliation loops such as Deployment, ReplicaSet and Node controllers), and the cloud-controller-manager (which provisions cloud load balancers and enriches node metadata).

Deployment vs StatefulSet — when do I use which?

Use a Deployment for stateless services (APIs, web front ends, workers) where pods are interchangeable. Use a StatefulSet when replicas need a stable identity, ordered startup, or per-replica persistent storage — databases, message brokers and consensus systems like Kafka, PostgreSQL or etcd. For production databases, prefer an operator (CloudNativePG, Strimzi) that builds on StatefulSets.

Why is a Pod not just a container?

A Pod is one or more containers that are always co-scheduled on the same node and share a network namespace (one IP, mutual localhost), IPC and volumes. This lets genuinely-coupled processes — an app plus a log shipper or an mTLS proxy sidecar — start, stop and communicate as one unit, without running a process supervisor inside a container. The Pod, not the container, is the smallest unit Kubernetes schedules.

Is Kubernetes worth learning in 2026?

Yes. Kubernetes is the default expectation for DevOps, SRE, Cloud and platform-engineering roles, and is now also the standard substrate for AI/ML and LLM workloads. Command fluency (kubectl), understanding the control plane, and hands-on work with Deployments, Services, networking and the CKA/CKAD/CKS certifications remain among the highest-return skills for cloud engineers.

Share𝕏inf
EnrollWhatsAppCall us