Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Managing stateful applications with Kubernetes Operators in Golang

August 4, 2021
Priyanka Jiandani
Related topics:
Event-DrivenGoKubernetesOperators
Related products:
Red Hat OpenShift

Share:

    You can use Kubernetes Operators to set up automatic resource management for services in your applications. In a previous article, I described this pattern and how to create an operator in the Go language. In this article, we will continue to explore the pattern, this time by creating a Kubernetes Operator in Go to keep a WordPress site up to date. I recommend reading or reviewing the earlier article before starting this one.

    Example prerequisites

    I use the following tools in this article's example:

    • Golang v1.15+
    • The operator-sdk command-line interface, version 1 or higher
    • minikube
    • kubectl

    In addition, set this environment variable in your terminal:

    $ export GO111MODULE="on"

    You can see the source code for this article on my GitHub repository.

    Set up the environment

    Before coding our automation, we need to set up the environment.

    Enter the following command to generate boilerplate code that accomplishes much of the setup:

    $ operator-sdk init --domain example.com --repo github.com/<git user name>/wordpress-operator
    

    Creating the API and controller

    Enter the following to set up the environment for WordPress:

    $ operator-sdk create api --group wordpress --version v1 --kind Wordpress --resource --controller
    

    The command creates the following files:

    api/v1/wordpress_types.go
    controllers/wordpress_controller.go

    Defining the API

    You can make changes to api/v1/wordpress_types.go to include the attributes that you would like in the WordpressSpec struct and WordpressStatus status.

    For the sake of simplicity, we will set only one requirement. It places a root password for database access in the WordpressSpec struct:

    apiVersion: wordpress.example.com/v1
    kind: Wordpress
    metadata:
      name: mysite
    spec:
      sqlRootPassword: plaintextpassword

    After you make the change in api/v1/wordpress_types.go, run the following commands to ensure that the changes are reflected in the. custom resource definitions (CRDs) in the config directory:

    $ make generate
    $ make manifests
    

    Implement the controller

    Now we can enter the Go code of the example to configure our controller. The WordPress controller struct is named WordpressReconciler in the current operator-sdk. In earlier versions, it was named ReconcileWordpress.

    Watching resources

    The boiler-plate code generated automatically creates a watch for the primary resource in the controllers/wordpress_controller.go file, in the SetupWithmanager function:

    For(&v1.Wordpress{})

    The preceding line is equivalent to the following code in previous versions of the operator-sdk:

    Watch(&source.Kind{Type: &v1.Wordpress{}}, &handler.EnqueueRequestForObject{})

    The additional resources we watch are the service, the deployment, and the persistent volume claim (PVC):

    Owns(&appsv1.Deployment{}).
    Owns(&corev1.Service{}).
    Owns(&corev1.PersistentVolumeClaim{})

    The preceding code is equivalent to the following code in previous versions of the operator-sdk:

    Watch(&source.Kind{Type: &appsv1.Deployment{}}, &handler.EnqueueRequestForOwner{
          IsController: True,
          OwnerType: &v1.Wordpress{},
    })
    Watch(&source.Kind{Type: &corev1.Service{}}, &handler.EnqueueRequestForOwner{
        IsController: true,
        OwnerType: &v1.Wordpress{},
    })
    Watch(&source.Kind{Type: &corev1.PersistentVolumeClaim{}}, &handler.EnqueueRequestForOwner{
        IsController: true,
        OwnerType: &v1.Wordpress{},
    })

    To sum up, all the watches in the application can be written as:

    For(&v1.Wordpress{}).
    Owns(&appsv1.Deployment{}).
    Owns(&corev1.Service{}).
    Owns(&corev1.PersistentVolumeClaim{}).
    Complete(r)

    Note: To learn more about establishing watches in the operator-sdk, refer to Builder docs and Controller docs.

    The reconcile loop

    I introduced the reconcile logic in my previous article. The new version of the operator-sdk adds a few enhancements. It defines a few role-based access control (RBAC) rules by default:

    //+kubebuilder:rbac:groups=wordpress.example.com,resources=wordpresses,verbs=get;list;watch;create;update;patch;delete
    //+kubebuilder:rbac:groups=wordpress.example.com,resources=wordpresses/status,verbs=get;update;patch
    //+kubebuilder:rbac:groups=wordpress.example.com,resources=wordpresses/finalizers,verbs=update
    

    These are generated in the configuration directory when you run the make manifests command.

    Logging

    The current operator-sdk already defines a logger in the WordpressReconciler struct. As a result, you don’t need to explicitly define your own logger.

    Adding the WordPress controller to the manager

    The current operator-sdk adds the controller in main.go:

    if err = (&controllers.WordpressReconciler{
                Client: mgr.GetClient(),
                Log: ctrl.Log.Withname(controllers”).Withname(“wordpress”),
                Scheme: mgr.GetScheme(),
              }).SetupWithManager(mgr); err!=nil{
                    // log error message
                       os.Exit(1)
             }
    

    Note that this code is equivalent to the following code in previous versions of the operator-sdk:

     if err := controllers.AddToManager(mgr); err != nil {
                    log.Error(err, "")
                     os.Exit(1)
    }
    

    Run the Kubernetes Operator

    You need a Kubernetes cluster for this part of the example. Enter the following command :

    $ make install run
    

    Open another terminal window and apply the custom resource for WordPress:

    $ kubectl create -f config/samples/wordpress_v1_wordpress.yaml
    

    Your config/samples/wordpress_v1_wordpress.yaml must look something like this:

    apiVersion: wordpress.example.com/v1
    kind: Wordpress
    metadata:
      name: mysite
    spec:
      sqlrootpassword: "abc"
    

    Output from make install run should be similar to the following:

    /home/pjiandan/go/src/wordpress-operator/bin/controller-gen "crd:trivialVersions=true,preserveUnknownFields=false" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
    /home/pjiandan/go/src/wordpress-operator/bin/kustomize build config/crd | kubectl apply -f -
    customresourcedefinition.apiextensions.k8s.io/wordpresses.wordpress.example.com created
    /home/pjiandan/go/src/wordpress-operator/bin/controller-gen "crd:trivialVersions=true,preserveUnknownFields=false" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
    /home/pjiandan/go/src/wordpress-operator/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
    go fmt ./...
    go vet ./...
    go run ./main.go
    2021-07-05T14:15:59.495+0530    INFO    controller-runtime.metrics      metrics server is starting to listen    {"addr": ":8080"}
    2021-07-05T14:15:59.496+0530    INFO    setup   starting manager
    2021-07-05T14:15:59.497+0530    INFO    controller-runtime.manager      starting metrics server {"path": "/metrics"}
    2021-07-05T14:15:59.497+0530    INFO    controller-runtime.manager.controller.wordpress Starting EventSource    {"reconciler group": "wordpress.example.com", "reconciler kind": "Wordpress", "source": "kind source: /, Kind="}
    2021-07-05T14:15:59.598+0530    INFO    controller-runtime.manager.controller.wordpress Starting EventSource    {"reconciler group": "wordpress.example.com", "reconciler kind": "Wordpress", "source": "kind source: /, Kind="}
    2021-07-05T14:15:59.698+0530    INFO    controller-runtime.manager.controller.wordpress Starting EventSource    {"reconciler group": "wordpress.example.com", "reconciler kind": "Wordpress", "source": "kind source: /, Kind="}
    2021-07-05T14:15:59.799+0530    INFO    controller-runtime.manager.controller.wordpress Starting EventSource    {"reconciler group": "wordpress.example.com", "reconciler kind": "Wordpress", "source": "kind source: /, Kind="}
    2021-07-05T14:15:59.900+0530    INFO    controller-runtime.manager.controller.wordpress Starting Controller     {"reconciler group": "wordpress.example.com", "reconciler kind": "Wordpress"}
    2021-07-05T14:15:59.900+0530    INFO    controller-runtime.manager.controller.wordpress Starting workers        {"reconciler group": "wordpress.example.com", "reconciler kind": "Wordpress", "worker count": 1}
    2021-07-05T14:15:59.900+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:15:59.900+0530    INFO    controllers.Wordpress   Creating a new PVC      {"PVC.Namespace": "default", "PVC.Name": "mysql-pv-claim"}
    2021-07-05T14:15:59.947+0530    INFO    controllers.Wordpress   Creating a new Deployment  {"Deployment.Namespace": "default", "Deployment.Name": "wordpress-mysql"}
    2021-07-05T14:15:59.969+0530    INFO    controllers.Wordpress   Creating a new Service  {"Service.Namespace": "default", "Service.Name": "wordpress-mysql"}
    2021-07-05T14:15:59.977+0530    INFO    controllers.Wordpress   MySQL isn't running, waiting for 5s
    2021-07-05T14:15:59.978+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:15:59.978+0530    INFO    controllers.Wordpress   MySQL isn't running, waiting for 5s
    2021-07-05T14:15:59.998+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:15:59.998+0530    INFO    controllers.Wordpress   MySQL isn't running, waiting for 5s
    2021-07-05T14:16:00.044+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:16:00.044+0530    INFO    controllers.Wordpress   MySQL isn't running, waiting for 5s
    2021-07-05T14:16:00.058+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:16:00.059+0530    INFO    controllers.Wordpress   MySQL isn't running, waiting for 5s
    2021-07-05T14:17:09.986+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:17:09.987+0530    INFO    controllers.Wordpress   MySQL isn't running, waiting for 5s
    2021-07-05T14:17:24.988+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:17:24.988+0530    INFO    controllers.Wordpress   MySQL isn't running, waiting for 5s
    2021-07-05T14:18:09.993+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:18:09.993+0530    INFO    controllers.Wordpress   MySQL isn't running, waiting for 5s
    2021-07-05T14:18:14.993+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:18:14.994+0530    INFO    controllers.Wordpress   MySQL isn't running, waiting for 5s
    2021-07-05T14:19:10.278+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:19:10.278+0530    INFO    controllers.Wordpress   Creating a new PVC      {"PVC.Namespace": "default", "PVC.Name": "wp-pv-claim"}
    2021-07-05T14:19:10.292+0530    INFO    controllers.Wordpress   Creating a new Deployment  {"Deployment.Namespace": "default", "Deployment.Name": "wordpress"}
    2021-07-05T14:19:10.309+0530    INFO    controllers.Wordpress   Creating a new Service  {"Service.Namespace": "default", "Service.Name": "wordpress"}
    2021-07-05T14:19:10.341+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:19:10.355+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:19:10.361+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:19:10.371+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    2021-07-05T14:19:15.001+0530    INFO    controllers.Wordpress   Reconciling Wordpress
    

    From the second terminal window, check for the resources created as follows:

    [pjiandan@localhost wordpress-operator]$ kubectl get all
    NAME                                   READY   STATUS    RESTARTS   AGE
    pod/wordpress-7446b985d9-vc7w2         1/1     Running   0          128m
    pod/wordpress-mysql-5cd8987844-4p6jp   1/1     Running   0          128m
    
    NAME                      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
    service/kubernetes        ClusterIP   10.96.0.1       <none>        443/TCP        3h58m
    service/wordpress         NodePort    10.110.12.200   <none>        80:32324/TCP   128m
    service/wordpress-mysql   ClusterIP   None            <none>        3306/TCP       128m
    
    NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/wordpress         1/1     1            1           128m
    deployment.apps/wordpress-mysql   1/1     1            1           128m
    
    NAME                                         DESIRED   CURRENT   READY   AGE
    replicaset.apps/wordpress-7446b985d9         1         1         1       128m
    replicaset.apps/wordpress-mysql-5cd8987844   1         1         1       128m
    

    Next, run the following command to return the IP address for the WordPress service:

    [pjiandan@localhost wordpress-operator]$ minikube service wordpress --url
    http://192.168.99.143:32324
    

    Verify that WordPress is running

    Open the URL in your browser to verify that WordPress is running, as shown in Figure 1.

    A running instance of WordPress shows that the Operator was successful.
    Figure 1: A running instance of WordPress.
    Created by ,

    Conclusion

    Visit my GitHub repository to see all of the code used in this example.

    Last updated: September 19, 2023

    Related Posts

    • Create a Kubernetes Operator in Golang to automatically manage a simple, stateful application

    • 5 tips for developing Kubernetes Operators with the new Operator SDK

    • Deploy and bind enterprise-grade microservices with Kubernetes Operators

    • Deploying Kubernetes Operators with Operator Lifecycle Manager bundles

    Recent Posts

    • How Kafka improves agentic AI

    • How to use service mesh to improve AI model security

    • How to run AI models in cloud development environments

    • How Trilio secures OpenShift virtual machines and containers

    • How to implement observability with Node.js and Llama Stack

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue