Operator example: deploying PHP message board application using Redis

Kubernetes official example: deploying PHP message board application using Redis Operator.

Source code warehouse: github.com/jxlwqq/guestbook-operat...


  • Install Docker Desktop and start the built-in Kubernetes cluster
  • Register a hub.docker.com Account, you need to push the locally built image to the public warehouse
  • Installing the operator SDK cli: brew install operator SDK
  • Install Go: brew install go

Recommended dependent versions of this example:

  • Docker Desktop: >= 4.0.0
  • Kubernetes: >= 1.21.4
  • Operator-SDK: >= 1.11.0
  • Go: >= 1.17

jxlwqq is the author's ID, and the personal ID involved in the command line and code should be replaced with the reader's own, including

  • --domain=
  • --repo=
  • //+kubebuildergroups=

Create project

Create a project named guestbook operator using the Operator SDK CLI.

mkdir -p $HOME/projects/guestbook-operator
cd $HOME/projects/guestbook-operator
go env -w GOPROXY=https://goproxy.cn,direct

operator-sdk init \
--domain=jxlwqq.github.io \
--repo=github.com/jxlwqq/guestbook-operator \

Creating API s and controllers

Use the Operator SDK CLI to create custom resource definition (CRD) API s and controllers.

Run the following command to create an API with the group app, version v1alpha1, and kind Guestbook:

operator-sdk create api \
--resource=true \
--controller=true \
--group=app \
--version=v1alpha1 \

Defines the API for the Guestbook custom resource (CR).

Modify API / v1alpha1 / Guestbook_ Go type definition in types.go with the following spec and status

type GuestbookSpec struct {
    FrontendSize int32 `json:"frontendSize"`
    RedisFollowerSize int32 `json:"redisFollowerSize"`

Generated code for resource type update:

make generate

Run the following command to generate and update the CRD manifest:

make manifests

Implementation controller

Because the logic is complex and the code is huge, it cannot be displayed here. For the complete operator code, please refer to the controllers directory.
In this example, the generated controller file Controllers / Guestbook_ Replace controller.go with the following example implementation:

Copyright 2021.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at


Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and
limitations under the License.

package controllers

import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"

ctrl "sigs.k8s.io/controller-runtime"

appv1alpha1 "github.com/jxlwqq/guestbook-operator/api/v1alpha1"


// GuestbookReconciler reconciles a Guestbook object
type GuestbookReconciler struct {
Scheme *runtime.Scheme


// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Guestbook object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
// For more details, check Reconcile and its Result here:
// - runtime@v0.9.2/pkg/reconcile"">https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile
func (r *GuestbookReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
reqLogger := log.FromContext(ctx)
reqLogger.Info("Reconciling Guestbook")

guestbook := &appv1alpha1.Guestbook{}
err := r.Client.Get(context.TODO(), req.NamespacedName, guestbook)
if err != nil {
    if errors.IsNotFound(err) {
        return ctrl.Result{}, nil
    return ctrl.Result{}, err

var result = &ctrl.Result{}

result, err = r.ensureDeployment(r.redisLeaderDeployment(guestbook))
if result != nil {
    return *result, err
result, err = r.ensureService(r.redisLeaderService(guestbook))
if result != nil {
    return *result, err

result, err = r.ensureDeployment(r.redisFollowerDeployment(guestbook))
if result != nil {
    return *result, err
result, err = r.ensureService(r.redisFollowerService(guestbook))
if result != nil {
    return *result, err
result, err = r.handleRedisFollowerChanges(guestbook)
if result != nil {
    return *result, err

result, err = r.ensureDeployment(r.frontendDeployment(guestbook))
if result != nil {
    return *result, err
result, err = r.ensureService(r.frontendService(guestbook))
if result != nil {
    return *result, err
result, err = r.handleFrontendChanges(guestbook)
if result != nil {
    return *result, err

return ctrl.Result{}, nil


// SetupWithManager sets up the controller with the Manager.
func (r *GuestbookReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).

Run the following command to generate and update CRD detailed list:
make manifests

Run the Operator

Bundle the Operator and deploy it in the cluster using Operator Lifecycle Manager (OLM).

Modify image in Makefile_ TAG_ Base and IMG:

IMAGE_TAG_BASE ?= docker.io/jxlwqq/guestbook-operator
IMG ?= $(IMAGE_TAG_BASE):latest

To build a mirror:

make docker-build

Push the image to the image warehouse:

make docker-push

Access after success: hub.docker.com/r/jxlwqq/guestbook-...

Run the make bundle command to create the Operator bundle list, and fill in the name, author and other necessary information in turn:

make bundle

Build bundle image:

make bundle-build

Push bundle image:

make bundle-push

Access after success: hub.docker.com/r/jxlwqq/guestbook-...

Deploying an Operator using Operator Lifecycle Manager:

# Switch to local cluster
kubectl config use-context docker-desktop
# Install olm
operator-sdk olm install
# Use the OLM integration in the Operator SDK to run the Operator in the cluster
operator-sdk run bundle docker.io/jxlwqq/guestbook-operator-bundle:v0.0.1

Create a custom resource

Edit config / samples / APP_ v1alpha1_ Guestbook CR listing example on guestbook.yaml to include the following specifications:

apiVersion: app.jxlwqq.github.io/v1alpha1
kind: Guestbook
  name: guestbook-sample
  # Add fields here
  frontendSize: 2
  redisFollowerSize: 2

Create CR:

kubectl apply -f config/samples/app_v1alpha1_guestbook.yaml

View Pod:

NAME                              READY   STATUS    RESTARTS   AGE
frontend-85595f5bf9-jrcp4         1/1     Running   0          9s
frontend-85595f5bf9-q8fkl         1/1     Running   0          9s
redis-follower-76c5cc5b79-fxxlq   1/1     Running   0          9s
redis-follower-76c5cc5b79-g8vnf   1/1     Running   0          9s
redis-leader-6666df964-vjhp2      1/1     Running   0          9s

View Service:

NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
frontend         NodePort   <none>        80:30693/TCP   24s
kubernetes       ClusterIP        <none>        443/TCP        4m58s
redis-follower   ClusterIP    <none>        6379/TCP       24s
redis-leader     ClusterIP   <none>        6379/TCP       24s

Browser access: localhost:30693

The form page of Guestbook will be displayed on the web page.

Update CR:

# Modify the number of frontend and redis replicas
kubectl patch guestbook guestbook-sample -p '{"spec":{"frontendSize": 3, "redisFollowerSize": 3}}' --type=merge

View Pod:

NAME                              READY   STATUS    RESTARTS   AGE
frontend-85595f5bf9-4pmfj         1/1     Running   0          4s
frontend-85595f5bf9-jrcp4         1/1     Running   0          50s
frontend-85595f5bf9-q8fkl         1/1     Running   0          50s
redis-follower-76c5cc5b79-bxbb4   1/1     Running   0          4s
redis-follower-76c5cc5b79-fxxlq   1/1     Running   0          50s
redis-follower-76c5cc5b79-g8vnf   1/1     Running   0          50s
redis-leader-6666df964-vjhp2      1/1     Running   0          50s

Clean up

operator-sdk cleanup guestbook-operator
operator-sdk olm uninstall


For more classic examples, please refer to: github.com/jxlwqq/kubernetes-examp...

Tags: Go Kubernetes

Posted on Mon, 13 Sep 2021 23:24:25 -0400 by drayarms