Build Your First Kubernetes Operator with Kubebuilder: A Step‑by‑Step Guide
This tutorial explains what a Kubernetes Operator is, walks through setting up the development environment, creating a simple Foo operator with Kubebuilder, defining CRDs and controllers in Go, testing the operator on a local Kind cluster, and suggests further enhancements.
What is an Operator?
An Operator extends Kubernetes by embedding custom logic that automates tasks beyond the native resources, allowing engineers to encode their operational knowledge as code. It consists of a Custom Resource Definition (CRD) and a controller that continuously reconciles the desired state.
Building the Operator
The tutorial uses the Kubebuilder framework (built on controller‑runtime) to create a simple Foo operator. First, install the required tools and Kubebuilder:
curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH) && chmod +x kubebuilder && mv kubebuilder /usr/local/bin/Verify the installation:
kubebuilder version
Version: main.version{KubeBuilderVersion:"3.4.1", KubernetesVendor:"1.23.5", GitCommit:"d59d7882ce95ce5de10238e135ddff31d8ede026", BuildDate:"2022-05-06T13:58:56Z", GoOs:"darwin", GoArch:"amd64"}Initialize a new project and create the API:
kubebuilder init --domain my.domain --repo my.domain/tutorial
kubebuilder create api --group tutorial --version v1 --kind Foo
# answer "y" to create the resource and controllerThe generated project structure includes main.go, the config manifests, and a Dockerfile. The Foo CRD defines a spec.name field and a status.happy boolean that becomes true when a Pod with the same name exists.
package v1
import ("k8s.io/apimachinery/pkg/apis/meta/v1")
type FooSpec struct { Name string `json:"name"` }
type FooStatus struct { Happy bool `json:"happy,omitempty"` }
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
type Foo struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec FooSpec `json:"spec,omitempty"` Status FooStatus `json:"status,omitempty"` }
//+kubebuilder:object:root=true
type FooList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` Items []Foo `json:"items"` }
func init() { SchemeBuilder.Register(&Foo{}, &FooList{}) }The controller watches both Foo resources and Pods, updating status.happy based on whether a matching Pod exists:
func (r *FooReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var foo tutorialv1.Foo
if err := r.Get(ctx, req.NamespacedName, &foo); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) }
var podList corev1.PodList
friendFound := false
if err := r.List(ctx, &podList); err == nil {
for _, pod := range podList.Items {
if pod.Name == foo.Spec.Name { friendFound = true; break }
}
}
foo.Status.Happy = friendFound
if err := r.Status().Update(ctx, &foo); err != nil { return ctrl.Result{}, err }
return ctrl.Result{}, nil
}
func (r *FooReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&tutorialv1.Foo{}).
Watches(&source.Kind{Type: &corev1.Pod{}}, handler.EnqueueRequestsFromMapFunc(r.mapPodsReqToFooReq)).
Complete(r)
}After building the manifests ( make manifests) and installing the CRD ( make install), run the operator locally ( make run). The manager starts the Foo controller, which begins reconciling events.
Testing the Controller
Create two Foo resources and a Pod named jack:
apiVersion: tutorial.my.domain/v1
kind: Foo
metadata:
name: foo-01
spec:
name: jack
---
apiVersion: tutorial.my.domain/v1
kind: Foo
metadata:
name: foo-02
spec:
name: joe apiVersion: v1
kind: Pod
metadata:
name: jack
spec:
containers:
- name: ubuntu
image: ubuntu:latest
command: ["sleep"]
args: ["infinity"]Applying these manifests triggers reconciliation loops that set status.happy to true for the Foo whose spec.name matches the Pod. Updating the second Foo’s spec.name to jack also updates its status.
More Work
Further improvements include optimizing event filtering, refining RBAC permissions, enhancing logging, emitting Kubernetes events on updates, adding custom fields to Foo, and writing unit and end‑to‑end tests.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
