Cloud Native 16 min read

Understanding the Operator Pattern and Building a Kubernetes Operator with Operator SDK

This article introduces the Kubernetes Operator pattern, explains its purpose for automating complex tasks, and provides a step‑by‑step tutorial on creating, configuring, and deploying a custom Operator using the Operator Framework, KubeBuilder, and Go, including code examples and deployment instructions.

DevOps Cloud Academy
DevOps Cloud Academy
DevOps Cloud Academy
Understanding the Operator Pattern and Building a Kubernetes Operator with Operator SDK

Automation tasks in Kubernetes often require reacting to events that are not easily observable. The Operator pattern addresses this by extending Kubernetes with custom resources and controllers that follow the control loop principle.

Operator Pattern Overview

An Operator is a software extension for Kubernetes that uses Custom Resource Definitions (CRDs) to manage applications and their components. It leverages the control loop to monitor and reconcile resources.

What Is the Operator Pattern?

The pattern allows Kubernetes users to create their own resource controllers to automate management of application stacks. It uses CRDs such as the Strimzi Kafka example to define resources.

apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
  name: my-cluster
spec:
  kafka:
    version: 3.4.0
    replicas: 3
    listeners:
      - name: plain
        port: 9092
        type: internal
        tls: false
      - name: tls
        port: 9093
        type: internal
        tls: true
    config:
      offsets.topic.replication.factor: 3
      transaction.state.log.replication.factor: 3
      transaction.state.log.min.isr: 2
      default.replication.factor: 3
      min.insync.replicas: 2
      inter.broker.protocol.version: "3.4"
    storage:
      type: jbod
      volumes:
      - id: 0
        type: persistent-claim
        size: 100Gi
        deleteClaim: false
  zookeeper:
    replicas: 3
    storage:
      type: persistent-claim
      size: 100Gi
      deleteClaim: false
  entityOperator:
    topicOperator: {}
    userOperator: {}

The Operator can watch the resources it creates and react to changes, enabling automation such as avoiding repetitive deployments, preventing human errors, and updating configurations automatically.

Examples

1. Automate Repeated Deployments

Instead of manually deploying Prometheus, OpenTelemetry, Grafana, and Postgres for each team, define a CRD (e.g., CRD ObservabilityStack ) that the Operator uses to create all required resources.

2. Automate to Avoid Human Errors

When managing StatefulSets or other resources, an Operator can orchestrate creation, update, or deletion of dozens of resources, reducing the risk of mistakes.

3. Automate Configuration Updates

An Operator can watch for changes in APIs exposed via Nginx and automatically update configuration files across environments.

Creating a Kubernetes Operator

Use the Operator Framework and KubeBuilder. Install the SDK via Homebrew or download from GitHub releases.

brew install operator-sdk
# Define platform information
export ARCH=$(case $(uname -m) in x86_64) echo -n amd64 ;; aarch64) echo -n arm64 ;; *) echo -n $(uname -m) ;; esac)
export OS=$(uname | awk '{print tolower($0)}')
# Download the binary
export OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/v1.28.0
curl -LO ${OPERATOR_SDK_DL_URL}/operator-sdk_${OS}_${ARCH}
chmod +x operator-sdk_${OS}_${ARCH} && sudo mv operator-sdk_${OS}_${ARCH} /usr/local/bin/operator-sdk

Initialize Project

operator-sdk init --domain adaendra.org --repo github.com/adaendra/test-operator

This generates a scaffold with common files such as Makefile, Dockerfile, and main.go .

Create API, Controller, and CRD

operator-sdk create api --group gateway --version v1alpha1 --kind MyProxy --resource --controller

The command creates api and controllers directories. Edit myproxy_types.go to define the Spec and Status, then run make manifests && make generate .

Controller Skeleton

// SetupWithManager sets up the controller with the Manager.
func (r *MyProxyReconciler) SetupWithManager(mgr ctrl.Manager) error {
    return ctrl.NewControllerManagedBy(mgr).
        For(&gatewayv1alpha1.MyProxy{}).
        Owns(&appsv1.Deployment{}).
        Complete(r)
}

RBAC permissions are declared with // +kubebuilder:rbac comments.

Reconcile Logic

The Reconcile function retrieves the custom resource, fetches the managed Deployment, creates it if missing, or updates it when necessary. Example snippets:

// Retrieve the resource
myProxy := &gatewayv1alpha1.MyProxy{}
err := r.Get(ctx, req.NamespacedName, myProxy)
if err != nil {
    if errors.IsNotFound(err) {
        log.Info("Resource not found. Ignoring.")
        return ctrl.Result{}, nil
    }
    log.Error(err, "Failed to get MyProxy")
    return ctrl.Result{}, err
}

// Create a new Deployment if not found
if err != nil && errors.IsNotFound(err) {
    dep := r.deploymentForExample(myProxy)
    err = r.Create(ctx, dep)
    if err != nil {
        log.Error(err, "Failed to create Deployment")
        return ctrl.Result{}, err
    }
    return ctrl.Result{Requeue: true}, nil
}

Build the Operator

Build the container image:

make docker-build

Push to a registry if needed:

make docker-push

Deploy the Operator

make install   # install CRDs
make deploy    # deploy the Operator

Test the Operator

Create an instance of the custom resource:

apiVersion: gateway.example.com/v1alpha1
kind: MyProxy
metadata:
  name: myproxy-sample
spec:
  name: toto

After applying, an Nginx Deployment will appear, demonstrating the Operator in action.

automationkubernetesGoOperator SDKKubeBuilderOperator Pattern
DevOps Cloud Academy
Written by

DevOps Cloud Academy

Exploring industry DevOps practices and technical expertise.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.