Kubernetes doesn’t orchestrate containers; it orchestrates desired states of containers.
Let’s see this in action. Imagine we have a simple web application.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-webapp
spec:
replicas: 3
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp-container
image: nginx:1.21.6
ports:
- containerPort: 80
This Deployment object tells Kubernetes: "I want 3 replicas of a pod running the nginx:1.21.6 image, and these pods should have the label app: webapp." Kubernetes then goes to work. It checks the current state of the cluster. If it sees fewer than 3 pods with that label, it starts creating new ones. If it sees more, it terminates excess ones. If a pod crashes, Kubernetes notices the discrepancy between the desired state (3 replicas) and the actual state (2 replicas) and starts a new one to bring it back to 3.
This "desired state" model is the core of Kubernetes. You declare what you want, and Kubernetes makes it happen. This solves the problem of managing applications in dynamic, distributed environments. Instead of manually starting, stopping, and monitoring individual containers, you tell Kubernetes the overall goal, and it handles the complex, low-level details of making sure that goal is met across your cluster.
Internally, Kubernetes has several key components working together. The kube-apiserver is the front door, exposing the Kubernetes API. etcd is the cluster’s brain, storing the desired state. kube-scheduler decides which node a new pod should run on. kube-controller-manager runs various controllers that watch the cluster state and try to reconcile it with the desired state (like the Deployment controller we saw). Finally, kubelet on each node is the agent that actually runs pods and reports their status back to the control plane.
The levers you control are primarily through Kubernetes objects: Pods, Deployments, StatefulSets, DaemonSets, Services, Ingresses, ConfigMaps, Secrets, and more. Each object represents a specific aspect of your application’s desired state. For example, a Service object provides a stable IP address and DNS name for a set of pods, abstracting away the ephemeral nature of individual pods. An Ingress object manages external access to services within the cluster, typically handling HTTP and HTTPS routing.
When you create a Deployment, you’re not just saying "run this container." You’re telling Kubernetes about the lifecycle of your application. You specify the container image, the number of replicas, how to perform rolling updates (e.g., maxUnavailable: 1, maxSurge: 1 to ensure zero downtime during updates), and rollback strategies. Kubernetes then takes on the responsibility of continuously ensuring that the live state of your application matches this declared lifecycle.
A common misconception is that Kubernetes "runs" your applications. It doesn’t. It ensures that the infrastructure is in a state where your applications can run as you’ve defined them. It’s a powerful abstraction, but it means understanding the declarative nature of its API is paramount. You declare the end state, and Kubernetes figures out the "how."
The system’s resilience comes from its controllers constantly striving to match the current state to the desired state. If a node fails, the kubelet on that node stops reporting. The control plane notices that the pods scheduled on that node are no longer running, compares this to the desired state in etcd, and schedules replacements on healthy nodes. This self-healing capability is a direct result of the desired state reconciliation loop.
If you have a Deployment with replicas: 3 and you delete one of the pods manually using kubectl delete pod <pod-name>, you’ll see Kubernetes immediately create a new pod to replace it, bringing the count back up to 3. This is the desired state model in action.
The next hurdle most people encounter after getting deployments to work is managing persistent storage for stateful applications.