DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Manage Microservices With Docker Compose
  • Securing Container Base Images Using Kyverno Policies
  • Containerize Gradle Apps and Deploy to Kubernetes With JKube Kubernetes Gradle Plugin
  • Taking KubeMQ Build & Deploy for a Test Drive: My Thoughts and Impressions

Trending

  • How the Go Runtime Preempts Goroutines for Efficient Concurrency
  • Transforming AI-Driven Data Analytics with DeepSeek: A New Era of Intelligent Insights
  • Why High-Performance AI/ML Is Essential in Modern Cybersecurity
  • Docker Model Runner: Streamlining AI Deployment for Developers
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. How to Build a Kubernetes Operator

How to Build a Kubernetes Operator

By 
Sudip Sengupta user avatar
Sudip Sengupta
DZone Core CORE ·
Sep. 17, 20 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
3.4K Views

Join the DZone community and get the full member experience.

Join For Free

This is the second part of our series focusing on Kubernetes Operators, and it shows how you can build a Kubernetes Operator based on the Bitnami Apache Helm chart. Note that you can refer to the steps in this tutorial to build an operator for your own applications.

Prerequisites

  1. We assume you followed the first part of the series. Thus, you should have a Kubernetes cluster (v1.7 or newer) with a control plane and two workers running on your computer. Also, the Operator Lifecycle Manager should be installed on your system. You can enter the following command to verify that everything is set up:
Shell
x
 
1
kubectl get all --namespace olm


Shell
xxxxxxxxxx
1
20
 
1
NAME                                    READY   STATUS    RESTARTS   AGE
2
pod/catalog-operator-64b6b59c4f-brck9   1/1     Running   0          3m28s
3
pod/olm-operator-844fb69f58-fn57f       1/1     Running   0          3m28s
4
pod/operatorhubio-catalog-5s8k2         1/1     Running   0          3m4s
5
pod/packageserver-65df5d5cc9-nz26h      1/1     Running   0          3m2s
6
pod/packageserver-65df5d5cc9-x7hwc      1/1     Running   0          3m2s
7

          
8
NAME                                       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)     AGE
9
service/operatorhubio-catalog              ClusterIP   10.103.1.120   <none>        50051/TCP   3m3s
10
service/v1-packages-operators-coreos-com   ClusterIP   10.99.75.171   <none>        443/TCP     3m3s
11

          
12
NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
13
deployment.apps/catalog-operator   1/1     1            1           3m28s
14
deployment.apps/olm-operator       1/1     1            1           3m28s
15
deployment.apps/packageserver      2/2     2            2           3m2s
16

          
17
NAME                                          DESIRED   CURRENT   READY   AGE
18
replicaset.apps/catalog-operator-64b6b59c4f   1         1         1       3m28s
19
replicaset.apps/olm-operator-844fb69f58       1         1         1       3m28s
20
replicaset.apps/packageserver-65df5d5cc9      2         2         2       3m2s


  1. The Operator SDK is installed on your machine. For details about installing the Operator SDK, refer to the Install the Operator SDK CLI page.
  2. Helm CLI is installed on your computer. To install Helm CLI, follow the instructions from the Installing Helm page.
  3. Docker. For details about installing Docker, refer to the Install Docker page.
  4. You need a quay.io account.

Set Up The Apache Operator

In this section, we'll walk you through the process of setting up the Apache Operator using Bitnami's Helm chart.

  1. Add the Bitnami Helm repository to your Helm client by running the following command:
Shell
xxxxxxxxxx
1
 
1
helm repo add bitnami https://charts.bitnami.com


Shell
xxxxxxxxxx
1
 
1
"bitnami" has been added to your repositories
  1. Create a Helm-based Operator by running the operator-sdk new command, and passing it the following arguments:
  • The name of your operator (apache-operator).
  • The --api-version flag with the Kubernetes apiVersion. The format is $GROUP_NAME/$VERSION. In this tutorial, we'll be using appfleet.com/v1alpha1.
  • The --kind flag with the name of the Kubernetes CRD (Apache)
  • The --type flag with the type of operator. We'll be using helm. Other valid types are Go and Ansible
  • The helm-chart flag with the name of the Helm chart (bitnami/apache)
Shell
xxxxxxxxxx
1
 
1
operator-sdk new apache-operator --api-version=appfleet.com/v1alpha1 --kind=Apache --type=helm --helm-chart=bitnami/apache


Shell
xxxxxxxxxx
1
13
 
1
INFO[0000] Creating new Helm operator 'apache-operator'.
2
INFO[0001] Created helm-charts/apache
3
INFO[0001] Generating RBAC rules
4
WARN[0001] The RBAC rules generated in deploy/role.yaml are based on the chart's default manifest. Some rules may be missing for resources that are only enabled with custom values, and some existing rules may be overly broad. Double check the rules generated in deploy/role.yaml to ensure they meet the operator's permission requirements.
5
INFO[0001] Created build/Dockerfile
6
INFO[0001] Created watches.yaml
7
INFO[0001] Created deploy/service_account.yaml
8
INFO[0001] Created deploy/role.yaml
9
INFO[0001] Created deploy/role_binding.yaml
10
INFO[0001] Created deploy/operator.yaml
11
INFO[0001] Created deploy/crds/appfleet.com_v1alpha1_apache_cr.yaml
12
INFO[0001] Generated CustomResourceDefinition manifests.
13
INFO[0001] Project creation complete.

This command creates the following directory structure:

Shell
xxxxxxxxxx
1
 
1
tree apache-operator -L 2


Shell
xxxxxxxxxx
1
12
 
1
apache-operator
2
├── build
3
│   └── Dockerfile
4
├── deploy
5
│   ├── crds
6
│   ├── operator.yaml
7
│   ├── role.yaml
8
│   ├── role_binding.yaml
9
│   └── service_account.yaml
10
├── helm-charts
11
│   └── apache
12
└── watches.yaml

Things to note:

  • Kubernetes compares the actual state of the cluster with the desired state. Then, it takes action to match these states.  The Operators extend this pattern by watching a specific custom resource type and taking actions to match the spec in that resource. In this example, the Operator watches the Apache resource as defined in the watches.yaml file:
Shell
xxxxxxxxxx
1
 
1
cat apache-operator/watches.yaml


Shell
xxxxxxxxxx
1
 
1
---
2
- version: v1alpha1
3
  group: appfleet.com
4
  kind: Apache
5
  chart: helm-charts/apache
  • The Dockerfile uses the quay.io/operator-framework/helm-operator:v0.15.1 image as the base, and then it copies watches.yaml file and the Helm charts:
Shell
xxxxxxxxxx
1
 
1
cat apache-operator/build/Dockerfile


Shell
xxxxxxxxxx
1
 
1
FROM quay.io/operator-framework/helm-operator:v0.15.1
2

          
3
COPY watches.yaml ${HOME}/watches.yaml
4
COPY helm-charts/ ${HOME}/helm-charts/
  1. Now you can build the Apache Operator by moving into the apache-operator directory, and then entering the following operator-sdk build command:
Shell
xxxxxxxxxx
1
 
1
operator-sdk build apache-operator:v0.1


Shell
xxxxxxxxxx
1
12
 
1
INFO[0000] Building OCI image apache-operator:v0.1
2
Sending build context to Docker daemon  64.51kB
3
Step 1/3 : FROM quay.io/operator-framework/helm-operator:v0.15.1
4
 ---> 450a3ca2d02d
5
Step 2/3 : COPY watches.yaml ${HOME}/watches.yaml
6
 ---> Using cache
7
 ---> db5c285c02fb
8
Step 3/3 : COPY helm-charts/ ${HOME}/helm-charts/
9
 ---> 50255ede17de
10
Successfully built 50255ede17de
11
Successfully tagged apache-operator:v0.1
12
INFO[0003] Operator build complete.


  1. Verify that the Docker image was created with:
Shell
xxxxxxxxxx
1
 
1
docker images | grep apache


Shell
xxxxxxxxxx
1
 
1
apache-operator                            v0.1                50255ede17de        38 seconds ago      174MB
2

          


You can think of Quay as something similar to GitHub, but for Docker images. It's a registry where you can host images and share them. There are a couple of ways to set up quay.io with Docker. For the sake of simplicity, you'll use the docker login command. It's important to note that the docker login command stores the password you enter as plain-text. Thus, you should first generate an encrypted password.

  1. Point your browser to https://quay.io/, and then navigate to Account Settings:

    Quay.io on RedHat

  1. From the Account Settings page select Generate Encrypted Password:

    Generate Encrypted Password

  1. You will be prompted to enter your quay.io password:

    Generating encrypted version

  1. Select Docker Login and then copy the command containing your Docker encrypted password:

    Generated credentials

  1. In a terminal window, log in to quay.io by entering the following command:
Shell
xxxxxxxxxx
1
 
1
docker login -u="<YOUR_USERNAME>" -p="<YOUR_ENCRYPTED_PASSWORD>" quay.io


Shell
xxxxxxxxxx
1
 
1
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
2
Login Succeeded

Push the Apache Operator Image to quay.io

To do this, you first need to properly tag the image with the hostname of the registry, and the name of your repository.  Then, you can push the image.

  1. Enter the following command to tag the local image named apache-operator into the quay.io/andreipope repository:
Shell
 
xxxxxxxxxx
1
 
1
docker tag apache-operator:v0.1 quay.io/andreipope/apache-operator:v0.1


Note that the image name is comprised by a slash-separated list of the:

  • Registry hostname (quay.io)
  • Repository (andreipope)
  • Operator name (apache-version).

☞ The name of our repository is andreipope, but yours will be different.

  1. To push the image that we created in the previous section, run the following command docker push command:
Shell
xxxxxxxxxx
1
 
1
docker push quay.io/andreipope/apache-operator:v0.1


Shell
xxxxxxxxxx
1
 
1
b8325e5fabd7: Pushed
2
2f4354fc6a73: Pushed
3
b496b494f6f9: Pushed
4
9fd48ecc1227: Pushed
5
0141daa77f22: Pushed
6
27cd2023d60a: Pushed
7
4b52dfd1f9d9: Pushed
8
v0.1: digest: sha256:7230926984fc3d688e02764748441a74907eeb772e3b51eb06b1bac225ba9f98 size: 1778


  1. Point your browser to https://quay.io/, navigate to the apache-operator repository, and make repository public:

    making repository public

Deploy the Apache Operator

You are now ready to deploy the Apache Operator. Before that, you must customize the specs.

  1. Open the deploy/operator.yaml file in a plain-text editor and update the placeholder image: REPLACE_IMAGE with the location of your image (quay.io/andreipope/apache-operator:v0.1)

Your deploy.operator.yaml file should look similar to the following:

Shell
xxxxxxxxxx
1
31
 
1
apiVersion: apps/v1
2
kind: Deployment
3
metadata:
4
  name: apache-operator
5
spec:
6
  replicas: 1
7
  selector:
8
    matchLabels:
9
      name: apache-operator
10
  template:
11
    metadata:
12
      labels:
13
        name: apache-operator
14
    spec:
15
      serviceAccountName: apache-operator
16
      containers:
17
        - name: apache-operator
18
          # Replace this with the built image name
19
          image: quay.io/andreipope/apache-operator:v0.1
20
          imagePullPolicy: Always
21
          env:
22
            - name: WATCH_NAMESPACE
23
              valueFrom:
24
                fieldRef:
25
                  fieldPath: metadata.namespace
26
            - name: POD_NAME
27
              valueFrom:
28
                fieldRef:
29
                  fieldPath: metadata.name
30
            - name: OPERATOR_NAME
31
              value: "apache-operator"
  1. Enter these kubectl create commands to deploy the Apache Operator:
Shell
xxxxxxxxxx
1
 
1
kubectl create -f deploy/service_account.yaml
2
kubectl create -f deploy/role.yaml
3
kubectl create -f deploy/role_binding.yaml
4
kubectl create -f deploy/operator.yaml


Shell
xxxxxxxxxx
1
 
1
serviceaccount/expressjs-operator created
2
role.rbac.authorization.k8s.io/expressjs-operator created
3
rolebinding.rbac.authorization.k8s.io/expressjs-operator created
4
deployment.apps/expressjs-operator created
  1. Check the status of the deployment:
Shell
 
xxxxxxxxxx
1
 
1
kubectl get deployment
2
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
3
apache-operator   1/1     1            1           24s


☞ Note that the deployment doesn't define the spec for the Apache cluster. You’ll describe the Apache cluster in the next section, once the Operator is running.

  1. The Operator is a pod running in this deployment. To see it, type the following command:
Shell
xxxxxxxxxx
1
 
1
kubectl get pods


Shell
xxxxxxxxxx
1
 
1
kubectl get pods
2
NAME                               READY   STATUS    RESTARTS   AGE
3
apache-operator-6d5795f879-np6pr   1/1     Running   1          38s

Deploy the Apache Cluster

In the Set Up the Apache Operator section, you created a CRD  defining a new kind of resource, an Apache cluster. Now, you will apply that spec so that the Operator starts watching the Apache resources. Then, you will deploy the Apache cluster itself.

  1. First, let's deploy the CRD that defines the resources the Operator will monitor:
Shell
xxxxxxxxxx
1
 
1
kubectl apply -f deploy/crds/appfleet.com_apaches_crd.yaml
2
customresourcedefinition.apiextensions.k8s.io/apaches.appfleet.com created


Shell
xxxxxxxxxx
1
 
1
customresourcedefinition.apiextensions.k8s.io/apaches.appfleet.com created
  1. At this point, you are ready to deploy the Apache cluster:
Shell
xxxxxxxxxx
1
 
1
kubectl apply -f deploy/crds/appfleet.com_v1alpha1_apache_cr.yaml


Shell
xxxxxxxxxx
1
 
1
apache.appfleet.com/example-apache created


  1. The deployment takes a bit of time to complete. Once everything is ready, you should see a new pod running Apache:
Shell
xxxxxxxxxx
1
 
1
kubectl get pods


Shell
xxxxxxxxxx
1
 
1
NAME                               READY   STATUS    RESTARTS   AGE
2
apache-operator-6d5795f879-np6pr   1/1     Running   4          2m53s
3
example-apache-7cf789fc98-462dr    1/1     Running   0          39s
  1. You can also check that the deployment was created by entering the following command:
Shell
xxxxxxxxxx
1
 
1
kubectl get deployment


Shell
xxxxxxxxxx
1
 
1
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
2
apache-operator   1/1     1            1           5m43s
3
example-apache    2/2     2            2           3m29s

Scaling Up

At this point, you have a running Apache cluster. To add another instance, you must modify the replicaCount field  in the deploy/crds/appfleet.com_v1alpha1_apache_cr.yamlapache.appfleet.com/example-apache  file. Then, you need to apply the new spec.

  1. Open the deploy/crds/appfleet.com_v1alpha1_apache_cr.yamlapache.appfleet.com/example-apache file in a plain-text editor, and specify spec.replicaCount: 2.
    The updated file should look similar to the following:
Shell
xxxxxxxxxx
1
80
 
1
apiVersion: appfleet.com/v1alpha1
2
kind: Apache
3
metadata:
4
  name: example-apache
5
spec:
6
  # Default values copied from <project_dir>/helm-charts/apache/values.yaml
7
8
  affinity: {}
9
  cloneHtdocsFromGit:
10
    enabled: false
11
    interval: 60
12
  git:
13
    pullPolicy: IfNotPresent
14
    registry: docker.io
15
    repository: bitnami/git
16
    tag: 2.25.0-debian-10-r0
17
  image:
18
    debug: false
19
    pullPolicy: IfNotPresent
20
    registry: docker.io
21
    repository: bitnami/apache
22
    tag: 2.4.41-debian-10-r0
23
  ingress:
24
    annotations: {}
25
    certManager: false
26
    enabled: false
27
    hostname: example.local
28
    secrets: null
29
    tls:
30
    - hosts:
31
      - example.local
32
      secretName: example.local-tls
33
  livenessProbe:
34
    enabled: true
35
    failureThreshold: 6
36
    initialDelaySeconds: 180
37
    path: /
38
    periodSeconds: 20
39
    port: http
40
    successThreshold: 1
41
    timeoutSeconds: 5
42
  metrics:
43
    enabled: false
44
    image:
45
      pullPolicy: IfNotPresent
46
      registry: docker.io
47
      repository: bitnami/apache-exporter
48
      tag: 0.7.0-debian-10-r0
49
    podAnnotations:
50
      prometheus.io/port: "9117"
51
      prometheus.io/scrape: "true"
52
    resources:
53
      limits: {}
54
      requests: {}
55
  nodeSelector: {}
56
  podAnnotations: {}
57
  readinessProbe:
58
    enabled: true
59
    failureThreshold: 6
60
    initialDelaySeconds: 30
61
    path: /
62
    periodSeconds: 10
63
    port: http
64
    successThreshold: 1
65
    timeoutSeconds: 5
66
  replicaCount: 2
67
  resources:
68
    limits: {}
69
    requests: {}
70
  service:
71
    annotations: {}
72
    externalTrafficPolicy: Cluster
73
    httpsPort: 443
74
    nodePorts:
75
      http: ""
76
      https: ""
77
    port: 80
78
    type: LoadBalancer
79
  tolerations: {}
80


  1. You can apply the updated spec with:
Shell
 




xxxxxxxxxx
1


 
1
kubectl apply -f deploy/crds/appfleet.com_v1alpha1_apache_cr.yaml



Shell
xxxxxxxxxx
1
 
1
apache.appfleet.com/example-apache configured


  1. After applying the updated spec, the state of the cluster differs from the desired state. The Operator starts a new instance of the Apache webserver to reconcile the two, scaling up the cluster:
Shell
xxxxxxxxxx
1
 
1
kubectl get pods


Shell
xxxxxxxxxx
1
 
1
NAME                               READY   STATUS              RESTARTS   AGE
2
apache-operator-6d5795f879-np6pr   1/1     Running             4          4m10s
3
example-apache-7cf789fc98-462dr    1/1     Running             0          116s
4
example-apache-7cf789fc98-fvttm    0/1     ContainerCreating   0          1s


Wait a bit until the second container is created:

Shell
xxxxxxxxxx
1
 
1
kubectl get pods


Shell
xxxxxxxxxx
1
 
1
NAME                               READY   STATUS    RESTARTS   AGE
2
apache-operator-6d5795f879-np6pr   1/1     Running   0          5m2s
3
example-apache-7cf789fc98-462dr    1/1     Running   0          2m48s
4
example-apache-7cf789fc98-fvttm    1/1     Running   0          53s


In the above output, note that two example-apache pods are running.

Verify Your Installation

The Apache Operator creates a Kubernetes service which is basically an endpoint where clients can obtain access to the group of pods running Apache. You will use this service to access your cluster using a web browser.

  1. Make sure that the service is up with:
Shell
xxxxxxxxxx
1
 
1
kubectl get service


Shell
xxxxxxxxxx
1
 
1
NAME                      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
2
apache-operator-metrics   ClusterIP      10.104.41.83   <none>        8686/TCP,8383/TCP            5m46s
3
example-apache            LoadBalancer   10.99.54.115   <pending>     80:31058/TCP,443:30362/TCP   3m51s
4
kubernetes                ClusterIP      10.96.0.1      <none>        443/TCP       
  1. Forward the connections made to http://localhost:80 to the pod running the service/example-apache service:
Shell
xxxxxxxxxx
1
 
1
kubectl port-forward service/example-apache 80:80


Shell
xxxxxxxxxx
1
 
1
Forwarding from 127.0.0.1:80 -> 8080
2
Forwarding from [::1]:80 -> 8080


  1. Point your browser to http://localhost:80. If everything worked well, you should see something like the following:

    Working operator


Now you should have a good understanding of how Kubernetes Operators work. Furthermore, by completing this tutorial you learned how create an operator for your application using a Helm chart.

Thanks for reading!

Operator (extension) Kubernetes shell Docker (software) Build (game engine)

Published at DZone with permission of Sudip Sengupta. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Manage Microservices With Docker Compose
  • Securing Container Base Images Using Kyverno Policies
  • Containerize Gradle Apps and Deploy to Kubernetes With JKube Kubernetes Gradle Plugin
  • Taking KubeMQ Build & Deploy for a Test Drive: My Thoughts and Impressions

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!