Master Kubernetes CRDs and Controller-Runtime to build powerful Kubernetes Operators. Learn how to implement custom automation with the Go Operator SDK today.
If you’ve ever found yourself manually syncing configurations or orchestrating complex state across your cluster, you’ve hit the ceiling of standard Kubernetes objects. That’s where Kubernetes CRDs and the Controller-Runtime library come in, allowing you to codify your operational knowledge into a custom operator.
In this guide, we’ll move beyond basic YAML manifests. We’ll build a custom automation engine using the Go Operator SDK to manage resources exactly how your infrastructure requires.
Standard controllers handle things like Deployments and Services, but they don't know your business logic. When you need to manage Kubernetes Secret Management, you might rely on existing operators, but for internal platform requirements, custom automation is often the only way to maintain consistency.
Think of an operator as a control loop. It watches the state of your cluster, compares it to your desired state, and executes actions to bridge the gap. By using the Controller-Runtime library, you get battle-tested scaffolding that handles the heavy lifting of caching, leader election, and event filtering.
First, ensure you have the Operator SDK (I'm using v1.30.0) and Go (1.21+) installed. We’ll initialize a project to define our custom API.
Bashmkdir my-operator && cd my-operator operator-sdk init --domain mahamudul.dev --repo github.com/mhrubel/my-operator operator-sdk create api --group apps --version v1alpha1 --kind AppConfig
This command generates the boilerplate for your Kubernetes CRDs and the controller scaffolding. The api/v1alpha1/appconfig_types.go file is where you define your schema.
The heart of your operator is the Reconcile function. This is where the magic happens. You don't just "do" things; you reconcile the state.
Gofunc (r *AppConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := log.FromContext(ctx) // 1. Fetch the AppConfig instance appConfig := &appsv1alpha1.AppConfig{} if err := r.Get(ctx, req.NamespacedName, appConfig); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } // 2. Add your custom automation logic here log.Info("Reconciling AppConfig", "name", appConfig.Name) // 3. Update status or create dependent resources return ctrl.Result{}, nil }
The key here is idempotency. Your logic should be able to run hundreds of times without causing side effects. If your operator manages complex node lifecycle tasks, you might consider integrating Kubernetes Cluster API patterns to ensure you aren't fighting with existing cluster state controllers.
Once your basic controller is working, you need to think about observability and stability. You can’t debug a distributed system by guessing. I strongly recommend setting up Kubernetes Observability early in your development process so you can trace reconciliation requests through your custom controllers.
Also, consider how your CRDs interact with the rest of the cluster. If you’re managing resources that cross namespace boundaries, look at how Kubernetes Multi-Tenancy handles hierarchy to avoid permission nightmares.
builder.Owns(&appsv1.Deployment{}) in your controller setup to trigger a reconciliation whenever a managed resource changes.Building Kubernetes Operators is the ultimate way to level up your platform engineering game. By leveraging Controller-Runtime and the Go Operator SDK, you move from manual scripts to a declarative, self-healing system. Start small, focus on idempotency, and always prioritize observability to ensure your custom automation stays reliable under pressure.
Master Kubernetes logging by implementing Grafana Loki and Promtail. Learn how to centralize your cluster logs and improve cloud-native observability today.