The Core Concept: Desired vs. Observed State
Imagine you’ve deployed three instances of your critical application. While you’re away, one instance inexplicably crashes. You’re left with two running, but your system needs three. Something must detect this discrepancy and take action. This is the fundamental problem that container orchestrators like Kubernetes solve, and it’s a perfect starting point for learning.
At its heart, orchestration involves comparing what you want (the desired state) with what is actually happening (the observed state). The process of identifying and rectifying differences is called reconciliation. In our simple example, the reconciliation step would be to notice one instance is missing and automatically start a replacement.

Building Your Own Orchestrator: A Step-by-Step Approach
To truly understand Kubernetes, building a simplified version provides invaluable insight. This isn't about reinventing Kubernetes, but about grasping its core principles through practical application. We’ll outline a conceptual build process, focusing on the logic rather than specific code implementations.
1. Defining the Desired State
The first step is to define what your system should look like. For a simple orchestrator, this could be a configuration file (e.g., YAML or JSON) specifying the application image to run, the number of replicas required, and perhaps resource limits.
For instance, you might declare:
{
"appName": "my-web-app",
"replicas": 3,
"image": "nginx:latest"
}
This JSON object represents the desired state for your application deployment.
2. Observing the Current State
Next, your orchestrator needs to know what is *actually* running. This involves querying your container runtime (like Docker) to list all running containers. For each container, you'd extract relevant information such as its image, status, and any associated metadata.
A simplified observed state might look like this:
[
{
"containerId": "abc123xyz",
"image": "nginx:latest",
"status": "running",
"appName": "my-web-app"
},
{
"containerId": "def456uvw",
"image": "nginx:latest",
"status": "running",
"appName": "my-web-app"
}
]
In this scenario, you have two running instances of `my-web-app`, which deviates from our desired state of three.
3. The Reconciliation Loop
This is the core of any orchestrator. A continuous loop runs in the background, performing the following steps:
- Fetch Desired State: Load the desired state configuration.
- Fetch Observed State: Query the container runtime for the current state.
- Compare: Identify discrepancies between desired and observed states. For our example, we see `desiredReplicas (3) - observedReplicas (2) = 1` missing replica.
- Act: Based on the comparison, take corrective action. In this case, the orchestrator would instruct the container runtime to start one new container using the `nginx:latest` image, tagged appropriately to be recognized as part of `my-web-app`.
This loop repeats constantly. If a running container crashes, the observed state changes, and the loop detects the difference, triggering the creation of a new container to maintain the desired replica count. This constant vigilance is what makes orchestration powerful.
4. Handling Failures and Updates
A more advanced orchestrator would need to handle various failure scenarios. What if the container runtime itself fails? What if the image specified in the desired state is invalid? What about updating an application to a new version? Each of these requires specific logic within the reconciliation loop.
For instance, handling an update might involve a rolling update strategy: gradually replacing old containers with new ones to minimize downtime. This requires more complex state management and coordination.
Why This Matters for Understanding Kubernetes
Kubernetes operates on the same fundamental principle: a control plane constantly works to ensure the cluster’s actual state matches the desired state defined in manifests. Controllers within Kubernetes (like the Deployment controller or ReplicaSet controller) are responsible for monitoring specific resources and performing reconciliation.
When you define a Deployment in Kubernetes, you specify the desired number of pods. The Deployment controller creates a ReplicaSet, and the ReplicaSet controller ensures the specified number of pods are running. If a pod dies, the ReplicaSet controller notices the discrepancy and schedules a new pod to replace it. This is the same reconciliation loop we built conceptually.
By building a simple orchestrator, you gain a concrete understanding of:
- State Management: How to track and compare desired versus actual configurations.
- Control Loops: The iterative process of observation and action.
- Abstraction: How higher-level concepts (like Deployments) are managed by lower-level controllers.
This hands-on approach demystifies complex systems by breaking them down into understandable components. It transforms abstract concepts into tangible mechanisms, providing a solid foundation for anyone looking to master Kubernetes or other distributed systems.
What nobody has addressed yet is the complexity of transitioning from such a manual, small-scale orchestrator to the sophisticated, distributed nature of Kubernetes. The leap involves understanding networking, distributed consensus, storage, and a vast API surface – challenges far beyond simple state reconciliation.
