The Operator Pattern
Kubernetes is great at managing stateless applications (Deployments). But what about complex, stateful applications like databases?
If a PostgreSQL primary node fails, you need a human to:
- Promote a replica to primary.
- Reconfigure the other replicas.
- Update the application to point to the new primary.
[!IMPORTANT] An Operator is a software extension to Kubernetes that makes use of Custom Resources to manage applications and their components. It encodes human operational knowledge into software.
1. How It Works: The Reconciliation Loop
The core of any Operator is the Reconciliation Loop (or Controller Loop). It constantly compares the Desired State (declared in YAML) with the Current State (running in the cluster) and takes action to make them match.
The Loop Logic
- Observe: Watch for changes in Custom Resources (CRs).
- Analyze: Check the difference between
spec(desired) andstatus(current). - Act: Create/Delete Pods, update configurations, or trigger external APIs.
2. Interactive: The Reconciliation Loop
Set a Desired State and watch the Controller work to achieve it.
Desired State (Spec)
Current State (Status)
3. Implementing a Controller (Java & Go)
Operators are built using SDKs that handle the low-level API communication.
Go: Using Kubebuilder
Kubebuilder is the standard framework for building Operators in Go.
// controllers/mykind_controller.go
func (r *MyKindReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("mykind", req.NamespacedName)
// 1. Fetch the CR instance
var myKind mygroupv1.MyKind
if err := r.Get(ctx, req.NamespacedName, &myKind); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 2. Define the desired Deployment
dep := r.deploymentForMyKind(&myKind)
// 3. Create or Update the Deployment
// Logic to check if deployment exists, and if not create it
// Logic to check if spec matches, and if not update it
return ctrl.Result{}, nil
}
Java: Using Java Operator SDK
For Java developers, the Java Operator SDK provides a similar abstraction.
// MyKindReconciler.java
@ControllerConfiguration
public class MyKindReconciler implements Reconciler<MyKind> {
@Override
public UpdateControl<MyKind> reconcile(MyKind resource, Context context) {
// 1. Logic to inspect the resource (CR)
int desiredReplicas = resource.getSpec().getReplicas();
// 2. Logic to check the Deployment via Kubernetes Client
// client.apps().deployments().inNamespace(...).createOrReplace(...)
return UpdateControl.noUpdate();
}
}
4. Helm vs. Operators
| Feature | Helm | Operator |
|---|---|---|
| Purpose | Packaging & Templating | Lifecycle Automation |
| Scope | Day 0 (Install) & Day 1 (Upgrade) | Day 2 (Run, Heal, Scale, Backup) |
| Intelligence | Static Templates | Dynamic Logic (Code) |
| Best For | Stateless Apps (Nginx, API) | Stateful Apps (Postgres, Kafka, Redis) |
5. Summary
- CRDs allow you to create your own Kubernetes objects.
- Controllers run a continuous loop to enforce the desired state.
- Operators combine CRDs and Controllers to automate complex application logic.
- Use Helm for installation, use Operators for ongoing management of stateful services.