A Guide to Creating Kubernetes Operators with Go
A Guide to Creating Kubernetes Operators with Go
Kubernetes operators are custom software extensions that manage and automate tasks for applications running on Kubernetes clusters. This article demonstrates creating a Kubernetes operator using the Go programming language and the Kubernetes Operator SDK.
Prerequisites
To follow this guide, you need:
- A basic understanding of Kubernetes and Go programming language
- A Kubernetes cluster (e.g., Minikube, kind, or any cloud-based Kubernetes service)
- The
kubectl
command-line tool installed - The Go programming language (version 1.16+) installed
- The Operator SDK installed
Table of Contents
- What are Kubernetes Operators?
- Setting Up the Development Environment
- Creating a Custom Resource Definition (CRD)
- Implementing the Operator
- Deploying the Operator
- Testing the Operator
- Conclusion
What are Kubernetes Operators?
Kubernetes operators are custom controllers that extend the Kubernetes API to manage complex applications. They allow you to define custom resources and provide the necessary automation to handle them.
Setting Up the Development Environment
First, create a new directory for your operator:
$ mkdir my-operator && cd my-operator
Initialize the project with the operator-sdk
:
$ operator-sdk init --domain example.com --repo github.com/example/my-operator
This command creates your operator’s necessary files and directories, including the Dockerfile
, Makefile
, and the Go module.
Creating a Custom Resource Definition (CRD)
Now, create a custom resource definition (CRD) for your operator:
$ operator-sdk create api --group apps --version v1alpha1 --kind MyApp --resource --controller
This command generates the following files:
api/v1alpha1/myapp_types.go
: Defines the MyApp custom resource (CR)controllers/myapp_controller.go
: Implements the MyApp controller
Edit api/v1alpha1/myapp_types.go
to define the MyApp CR spec and status:
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// MyAppSpec defines the desired state of MyApp
type MyAppSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Replicas is the number of replicas for the application
Replicas int32 `json:"replicas"`
}
// MyAppStatus defines the observed state of MyApp
type MyAppStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
// ReadyReplicas is the number of ready replicas for the application
ReadyReplicas int32 `json:"readyReplicas"`
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// MyApp is the Schema for the myapps API
type MyApp struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec MyAppSpec `json:"spec,omitempty"`
Status MyAppStatus `json:"status,omitempty"`
}
//+kubebuilder:object:
//+root=true
// MyAppList contains a list of MyApp
type MyAppList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []MyApp `json:"items"`
}
func init() {
SchemeBuilder.Register(&MyApp{}, &MyAppList{})
}
After defining the CRD, run the following command to update the generated code:
$ make generate
Implementing the Operator
Next, open the controllers/myapp_controller.go
file to implement the MyApp
controller. Start by importing the necessary packages at the top of the file:
import (
"context"
"fmt"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
appsv1alpha1 "github.com/example/my-operator/api/v1alpha1"
)
Implement the Reconcile
method in the MyAppReconciler
struct:
func (r *MyAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = r.Log.WithValues("myapp", req.NamespacedName)
// Fetch the MyApp instance
myApp := &appsv1alpha1.MyApp{}
err := r.Get(ctx, req.NamespacedName, myApp)
if err != nil {
if errors.IsNotFound(err) {
// Request object not found, could have been deleted after reconcile request.
return ctrl.Result{}, nil
}
// Error reading the object - requeue the request.
return ctrl.Result{}, err
}
// Implement your logic here
// ...m
}
Now, implement the logic to create and manage your application’s Deployment
and a Service
. You can find examples in the official Kubernetes client-go example repository.
Deploying the Operator
Build and push the operator image:
$ make docker-build docker-push IMG=my-operator:latest
Deploy the operator to your cluster:
$ make deploy IMG=my-operator:latest
Testing the Operator
Create a sample MyApp
custom resource:
apiVersion: apps.example.com/v1alpha1
kind: MyApp
metadata:
name: my-app
spec:
replicas: 3
Save this as myapp-sample.yaml
, then apply it to your cluster:
$ kubectl apply -f myapp-sample.yaml
Check if the operator creates the Deployment
and Service
for your application:
$ kubectl get deployments
$ kubectl get services
Conclusion
In this guide, you have learned how to create a Kubernetes operator using the Go programming language and the Kubernetes Operator SDK. You can now extend the Kubernetes API to manage complex applications and automate tasks on your cluster.
Subscribe to Faizan Bashir
Get the latest posts delivered right to your inbox