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:

  1. Promote a replica to primary.
  2. Reconfigure the other replicas.
  3. 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

  1. Observe: Watch for changes in Custom Resources (CRs).
  2. Analyze: Check the difference between spec (desired) and status (current).
  3. 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)

Reconciling...

Current State (Status)

Synced

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.