Kubernetes Architecture, Operations, and Troubleshooting Questions
How Kubernetes works, how to run it, and how to debug it. Covers control-plane and node components, the scheduler and API server, cluster design, high availability and multi-cluster topologies, and platform-level operations; the workload primitives (pods, deployments, services, controllers), cluster upgrades, and designing Kubernetes as an internal platform; and the operational depth inside a cluster including pod and service networking, ingress and the CNI model, service mesh, persistent volumes and storage classes, resource requests and limits, and systematically diagnosing scheduling, networking, and storage failures. The full architecture-through-day-two-operations span of Kubernetes.
Explain the differences between Deployment and StatefulSet. Provide a scenario (e.g., a database cluster) that requires StatefulSet semantics and explain which StatefulSet features (stable network IDs, ordinal indexes, persistent volumes) are necessary.
Sample Answer
Deployment and StatefulSet both manage a set of pods from one template, but a Deployment treats every pod as interchangeable, while a StatefulSet gives each pod a stable, ordinal identity and its own dedicated storage. Reach for StatefulSet only when the application itself needs to know which replica it is, not just how many replicas exist.
Direct comparison
| Deployment | StatefulSet | |
|---|---|---|
| Pod naming | random suffix, no ordering | ordinal suffix (pod-0, pod-1, ...), stable across restarts |
| Network identity | pod IP changes on recreation; normally fronted by a load-balancing Service | stable hostname per ordinal, usually via a headless Service (clusterIP: None), so pod-0.svc.namespace.svc.cluster.local always resolves to the same replica |
| Storage | usually shared, external, or none | one PersistentVolumeClaim (PVC, a request for storage) bound to a PersistentVolume (PV, the actual storage resource) per ordinal, via volumeClaimTemplates; deleting the StatefulSet does not delete these PVCs |
| Startup and scale order | no ordering guarantee | default OrderedReady policy starts and stops one ordinal at a time, lowest-to-highest on scale-up and highest-to-lowest on scale-down; a Parallel policy is available when the app does not need this ordering |
Worked example: a PostgreSQL primary-replica cluster
A database cluster with one primary and several read replicas needs exactly the guarantees a Deployment cannot give:
- Stable hostnames: replication configuration and client connection strings can target
postgres-0.postgres-svcby name, and that name always means the same replica, even across restarts. - Ordinal indexes: an init script can branch on the pod's ordinal (ordinal 0 initializes as primary, ordinals 1 and above join as replicas) and rely on ordered startup so replicas do not attempt to join before the primary exists.
- Per-ordinal persistent volumes:
volumeClaimTemplatesties each replica's data directory to its own ordinal, sopostgres-1always reattaches topostgres-1's data after a restart, never to another replica's.
Trade-offs and pitfalls
- Not everything with 'stateful' in its name needs a StatefulSet. Kafka brokers do: each broker owns local partition log segments on disk and needs a stable identity for replica assignment. Kafka Connect workers do not: a Connect worker's task state and offsets live in Kafka topics, not on local disk, so the worker pool is effectively stateless and runs fine as a plain Deployment. Confirm where the durable state actually lives before defaulting to StatefulSet.
- StatefulSet's ordered rollout is slower and more conservative than a Deployment's by design; if a stateful workload's replicas are genuinely independent (sharded, no leader election), that ordering guarantee adds rollout latency with no real benefit, and a
Parallelpod-management policy, or even a Deployment with per-shard volumes mounted individually, may fit better. - Deleting a StatefulSet leaves its PVCs behind on purpose, to protect data from an accidental delete. Teams that do not know this get surprised in both directions: unexpected data loss if they assumed cleanup was automatic, or orphaned storage cost if they never clean it up.
Explain liveness, readiness, and startup probes in Kubernetes. For each type describe when it is evaluated, what consequences a failing probe has on pod lifecycle and traffic routing, and list best practices for implementing probes for a typical HTTP-based web service.
Sample Answer
Liveness, readiness, and startup probes all ask whether a container is okay, but each answer drives a different Kubernetes action: a failing liveness probe gets the container restarted, a failing readiness probe gets the pod pulled out of Service traffic without touching the container at all, and a startup probe simply delays the other two until the app has had time to boot.
What each probe gates
| Probe | Evaluated | Consequence on failure | Effect on traffic |
|---|---|---|---|
| Liveness | continuously, after the container starts | kubelet kills the container; it is recreated per the pod's restartPolicy | indirect only, through the restart |
| Readiness | continuously, independent of liveness | pod is marked NotReady and removed from the Service's Endpoints and EndpointSlices, the objects that track which pod IPs actually receive traffic | direct: no new requests are routed to it until it passes again |
| Startup | only until it first succeeds | container is killed and restarted if it fails before ever succeeding; liveness and readiness are not evaluated at all until it does | none directly, but it prevents liveness from killing a still-booting container |
Worked example: sizing a startup budget by workload archetype
What 'booting' means differs a lot by workload, and the startup probe has to be sized for the actual archetype, not guessed at: a machine learning (ML) inference service loading model weights into memory might need several minutes; a batch worker doing asynchronous Java Virtual Machine (JVM) warmup, classloading, and connection-pool initialization for an extract-transform-load (ETL) job might need under a minute; a stateless HTTP handler might be ready in under a second. Whichever number applies, it has to be encoded as periodSeconds times failureThreshold. Budgeting 5 minutes of startup headroom with a 10-second check interval for the ML case:
10×30=300s=5 min
means periodSeconds: 10 and failureThreshold: 30. Too tight in this calculation and the startup probe itself kills a healthy-but-slow container before it ever gets a chance to serve; too loose, and a genuinely stuck container burns minutes before anything reacts.
Trade-offs and pitfalls
- Swapping liveness and readiness is the classic mistake: pointing liveness at a deep dependency check (database reachability) means a transient database blip restarts every application pod at once instead of simply pulling them from rotation, turning a recoverable dependency issue into a self-inflicted outage.
- Using liveness as a substitute for a startup probe on a slow-booting app causes a restart loop before the app ever finishes initializing, since the container never survives long enough to pass a liveness check tuned for steady-state behavior.
- A readiness probe that is too permissive, common with the JVM-async pattern where the process starts accepting connections before its dependency pools are actually warm, reports the pod as ready while real requests still fail; that failure mode never shows up as a probe failure at all, only as user-visible errors.
Explain PersistentVolume (PV) and PersistentVolumeClaim (PVC) in Kubernetes. Describe how binding works, what reclaimPolicy means, how StorageClass enables dynamic provisioning, and scenarios where PVCs are preferable to ephemeral volumes for stateful apps.
Sample Answer
A PersistentVolume (PV) is a cluster-scoped object representing an actual piece of storage (a cloud disk, an NFS share, and so on); a PersistentVolumeClaim (PVC) is a namespaced request for storage that a Pod actually references. Kubernetes exists to decouple the two: application manifests ask for storage by claim, never by naming a specific disk.
Binding
- Static: an admin pre-creates PVs ahead of time. When a PVC is created, the control plane looks for an existing PV whose capacity, access mode, and (if specified) StorageClass match, and binds them; the PV's
status.phasebecomesBound. - Dynamic: if the PVC references a StorageClass and no matching PV exists yet, the StorageClass's provisioner (a CSI, Container Storage Interface, driver) creates a new PV on demand and binds it automatically. This is the default path in most modern clusters and needs no manual admin step per volume.
accessModes and volumeMode: the other fields people gloss over
| accessMode | Meaning |
|---|---|
| ReadWriteOnce (RWO) | Can be mounted read-write by Pods on one node at a time; historically this meant multiple Pods on that same node could still mount it simultaneously, which surprised people expecting single-Pod exclusivity |
| ReadOnlyMany (ROX) | Can be mounted read-only by many Pods across many nodes |
| ReadWriteMany (RWX) | Can be mounted read-write by many Pods across many nodes at once |
| ReadWriteOncePod (RWOP) | Stable since Kubernetes 1.29, CSI-only: restricts the volume to exactly one Pod cluster-wide, closing the same-node-multiple-Pods gap that plain RWO left open |
volumeMode is a separate field from accessModes:
- Filesystem (default): the volume is formatted with a filesystem and mounted as a directory the container sees normally.
- Block: the volume is exposed to the container as a raw block device with no filesystem at all; used by workloads (some databases, specialized storage software) that manage their own on-disk format and want direct block access rather than going through a filesystem layer.
reclaimPolicy
Set on the PV, this decides what happens to the underlying storage once its PVC is deleted:
- Delete: the underlying storage is destroyed. The default for dynamically provisioned volumes.
- Retain: the storage and its data are kept; an administrator has to manually inspect and clean it up. Preferred for anything holding data you can't regenerate.
- Recycle is deprecated and should be treated as unavailable on a current cluster.
StorageClass and dynamic provisioning
A StorageClass names a provisioner and its parameters (disk type, IOPS, encryption, and similar) plus a reclaimPolicy. A PVC that references a StorageClass triggers that provisioner to create a matching PV automatically, which is what makes dynamic provisioning possible at all: without a StorageClass, every PV needs a human to create it ahead of time.
When PVCs are preferable to ephemeral volumes
Use a PVC whenever data has to survive something the Pod itself doesn't survive: a restart, a reschedule to a different node, or a node failure. Concretely: databases, message queue storage, anything with an SLO (service-level objective) tied to data durability. Ephemeral volumes (emptyDir, ephemeral CSI volumes) are the right choice when data loss on Pod termination is acceptable, for scratch space, caches, or stateless replicas that regenerate their working data on startup.
Worked example
A stateful application needs one 20Gi read-write volume that must survive a Pod reschedule:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes: ["ReadWriteOnce"]
volumeMode: Filesystem
resources:
requests:
storage: 20Gi
storageClassName: fast-ssd
The PVC binds to a dynamically provisioned 20Gi PV from the fast-ssd StorageClass; if the Pod is rescheduled (say, after a node failure), the same PVC reattaches to the same underlying data, whereas an emptyDir volume would have been created empty on the new node with the old data simply gone.
Trade-offs and pitfalls
- A PVC that stays
Pendingusually means either no PV matches it (static case) or the StorageClass's provisioner can't satisfy the request (wrong zone underWaitForFirstConsumer, quota exhausted, or a typo'd StorageClass name); checkkubectl describe pvcfor the actual event before guessing. - Resizing a PVC safely requires the StorageClass to have
allowVolumeExpansion: true; even then, some volume types only support growing, never shrinking, and some require a Pod restart to pick up the new size at the filesystem level. - Plain ReadWriteOnce's same-node multi-Pod loophole is a common source of confusion; if the real requirement is "exactly one Pod, full stop," ReadWriteOncePod is the access mode that actually guarantees that on a current CSI-backed cluster.
Explain the differences between ConfigMap and Secret objects. Show two ways to make a Secret available to a pod (environment variables and mounted files). Discuss basic security considerations for storing secrets and recommended best practices for CI/CD pipelines.
Sample Answer
A ConfigMap and a Secret are both key-value objects for feeding configuration into a pod without baking it into the image, but a Secret is meant for sensitive values and Kubernetes handles it slightly differently. Neither is encrypted by default: Secret values are only base64-encoded in etcd (the cluster's data store), which is trivially reversible, not encryption. Treat 'it's a Secret' as an access-control and audit boundary, not as cryptographic protection, unless encryption at rest has been explicitly turned on.
ConfigMap vs Secret
| ConfigMap | Secret | |
|---|---|---|
| Intended content | non-sensitive config: feature flags, config files, environment settings | sensitive values: passwords, tokens, keys |
| Storage in etcd | plain text | base64-encoded; not encrypted unless encryption at rest is configured |
| Immutable option | immutable: true field | immutable: true field |
Two ways to expose a Secret to a pod
Environment variable:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-secret
key: db-password
Mounted file:
volumes:
- name: secret-vol
secret:
secretName: my-secret
containers:
- name: app
volumeMounts:
- name: secret-vol
mountPath: /etc/creds
readOnly: true
Worked example: what actually happens when a Secret's value changes
Trace what happens after my-secret's db-password key is updated, depending on how it was exposed:
- Env var: the running pod keeps using the old value until it is restarted or replaced. Environment variables are read once at container start and never update in place.
- Mounted file, no subPath: the file at
/etc/creds/db-passworddoes eventually update, once the kubelet's periodic sync catches up (on the order of a minute, not instantly), but the running process only sees the change if it re-reads the file itself, since nothing forces that. - Mounted file with subPath: it never updates at all, because a
subPathmount is bound to a specific file version at mount time.
Neither the env-var path nor the volume path forces a pod restart on a config change. The common pattern to actually guarantee a fresh pod is to hash the ConfigMap or Secret's content into a pod template annotation (Helm'schecksum/configannotation is the usual form); a content change then produces a different pod template hash, which forces a real rollout instead of relying on an in-place file update the application may not even notice.
Trade-offs and pitfalls
- Base64 is not encryption; anyone with read access to Secret objects, or to etcd's data files directly, can decode it in one command. Enabling encryption at rest (an
EncryptionConfigurationbacked by a Key Management Service, KMS, provider, the current recommended approach since the older static-key KMS v1 API was deprecated as of Kubernetes 1.28) protects the etcd-at-rest copy; Role-Based Access Control (RBAC) is what actually protects who can read the Secret object in the first place, and the two are not substitutes for each other. - Environment variables are easy to leak: they show up when describing a running pod, in crash dumps, and in some logging frameworks that log the process environment; prefer mounted files for anything sensitive when the application can read from a file path instead.
- For continuous integration and continuous delivery (CI/CD) pipelines, never let plaintext secrets sit in pipeline configuration or version control; use the pipeline platform's own secret store, scope credentials as narrowly and as short-lived as possible, and prefer pulling secrets at deploy time from an external manager (HashiCorp Vault, a cloud provider's secrets manager, or a Secrets Store CSI driver) over baking them into a committed manifest, even one covered by a
.gitignoreentry.
Explain the differences between a Pod, ReplicaSet, Deployment, StatefulSet, and DaemonSet in Kubernetes. For each resource, describe its primary use cases, how it manages lifecycle and scaling, how updates/rollbacks are handled, and provide concrete examples of when you'd choose each resource (stateless web service, per-node agent, stateful database, etc.).
Sample Answer
Pods are the runtime unit; everything else here is a controller deciding how many pods to run and how to replace them. A ReplicaSet just keeps N identical pods alive. A Deployment wraps a ReplicaSet with declarative rolling updates and rollback history. A StatefulSet keeps stable per-replica identity and storage for workloads that care which replica they are. A DaemonSet guarantees exactly one pod per matching node. Job and CronJob run pods to completion, once or on a schedule, rather than keeping them running indefinitely.
Comparison
| Object | Identity model | Storage | Update mechanics | Reach for it when |
|---|---|---|---|---|
| ReplicaSet | anonymous, interchangeable pods | none built in | no native rolling update | almost never directly; it is what a Deployment manages underneath |
| Deployment | anonymous | typically shared, external, or none | rolling update via surge and unavailable settings, with full revision history and rollback | stateless services: web tiers, API servers |
| StatefulSet | stable ordinal identity (pod-0, pod-1, ...) and hostname | one PersistentVolumeClaim per ordinal via volumeClaimTemplates | ordered rolling update by default, highest ordinal first | stateful systems that need to know which replica they are: databases, brokers |
| DaemonSet | one pod per matching node | node-local, if any | rolling update per node, bounded by maxUnavailable | node-level agents: log collectors, CNI (Container Network Interface) plugins, monitoring exporters |
| Job | runs to completion, tracked by completions, parallelism, backoffLimit | ephemeral | retried on failure up to backoffLimit, not a rolling concept | one-off or batch work |
| CronJob | creates a Job on a schedule | ephemeral | governed by concurrencyPolicy (Allow, Forbid, or Replace) | scheduled batch work: nightly reports, cleanup jobs |
Worked example: rolling-update arithmetic
For a Deployment with replicas: 10, maxSurge: 25%, and maxUnavailable: 25%, Kubernetes rounds surge up and unavailable down:
surge=⌈10×0.25⌉=3
unavailable=⌊10×0.25⌋=2
So at the busiest point of the rollout the Deployment can briefly run up to 13 pods (10 plus 3 surge) while guaranteeing at least 8 stay available (10 minus 2 unavailable). This asymmetric rounding is why setting surge and unavailable to the same percentage still lets the total pod count grow slightly during a rollout instead of staying flat.
DaemonSets interact with node taints in a way that is easy to miss: they commonly carry an explicit toleration for the control-plane taint (conventionally node-role.kubernetes.io/control-plane:NoSchedule) so node-level agents like a CNI plugin or kube-proxy itself still run on control-plane nodes that ordinary application pods are excluded from. That toleration is a manifest choice the DaemonSet author makes, not automatic behavior.
Trade-offs and pitfalls
- Choosing Deployment for a workload that actually needs stable identity (a small quorum service, a primary-replica database) forces the team to rebuild that identity logic in application code; StatefulSet gives it for free, at the cost of slower, ordered rollouts.
- A stuck rollout is usually a readiness-probe failure hiding behind the surge and unavailable math: if new pods never pass readiness, the Deployment can stall indefinitely partway through, with old and new pods coexisting. Check pod events and readiness status before assuming a controller bug.
- A PodDisruptionBudget set too strictly (for example,
minAvailableequal to the full replica count) can make a Deployment's own rolling update unable to proceed, because the update's own temporary unavailability trips the budget meant to protect against unrelated disruptions. - DaemonSets bypass normal replica-count thinking: adding nodes silently adds pods and cost. Forgetting a DaemonSet exists across a large node pool is a common source of untracked resource consumption.
Unlock Full Question Bank
Get access to all 13 Kubernetes Architecture, Operations, and Troubleshooting interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.