Chapter 6. Adapter Operators

Consider the numerous steps it would take to write an Operator from scratch. You would have to create CRDs to specify the interface for end users. Kubernetes controllers would not only need to be written with the Operator’s domain-specific logic, but also be correctly hooked into a running cluster to receive the proper notifications. Roles and service accounts would need to be created to permit the Operator to function in the capacity it needs. An Operator is run as a pod inside of a cluster, so an image would need to be built, along with its accompanying deployment manifest.

Many projects have already invested in application deployment and configuration technologies. The Helm project allows users to define their cluster resources in a formatted text file and deploy them through the Helm command-line tools. Ansible is a popular automation engine for creating reusable scripts for provisioning and configuring a group of resources. Both projects have devoted followings of developers who may lack the resources to migrate to using Operators for their applications.

The Operator SDK provides a solution to both these problems through its Adapter Operators. Through the command-line tool, the SDK generates the code necessary to run technologies such as Helm and Ansible in an Operator. This allows you to rapidly migrate your infrastructure to an Operator model without needing to write the necessary supporting Operator code. The advantages of doing this include:

  • Provides a consistent interface through CRDs, regardless of whether the underlying technology is Helm, Ansible, or Go.

  • Allows the Helm and Ansible solutions to leverage the Operator deployment and lifecycle benefits that Operator Lifecycle Manager provides (see Chapter 8 for more information).

  • Enables hosting of those solutions on Operator repositories like OperatorHub.io (see Chapter 10 for more information).

In this chapter we demonstrate how to use the SDK to build and test Adapter Operators using the Visitors Site application introduced in the previous chapter.

Helm Operator

Helm is a package manager for Kubernetes. It makes it easier to deploy applications with multiple components, but each deployment is still a manual process. If you’re deploying many instances of a Helm-packaged app, it would be convenient to automate those deployments with an Operator.

Helm’s intricacies are outside the scope of this book—you can consult the documentation for more details—but a little background will help you understand the Helm Operator. Helm defines the Kubernetes resources that constitute an application, such as deployments and services, in a file called a chart. Charts support configuration variables, so you can customize application instances without needing to edit the chart itself. These configuration values are specified in a file named values.yaml. A Helm Operator can deploy each instance of an application with a different version of values.yaml.

The Operator SDK generates Kubernetes controller code for a Helm Operator when it is passed the --type=helm argument. You supply a Helm chart for your application, and the resulting Helm Operator watches for new CRs of its given type. When it finds one of these CRs, it constructs a Helm values.yaml file from the values defined in the resource. The Operator then creates the application resources specified in its Helm chart according to the settings in values.yaml. To configure another instance of the application, you create a new CR containing appropriate values for the instance.

The SDK provides two variations on how to build Helm-based Operators:

  • The project generation process builds a blank Helm chart structure within the Operator project code.

  • An existing chart is specified at Operator creation time, which the creation process uses to populate the generated Operator.

In the following sections we discuss each of these approaches. As a prerequisite, be sure to install the Helm command-line tools on your machine. You can find information on doing this in Helm’s install documentation.

Building the Operator

The SDK’s new command creates the skeleton project files for a new Operator. These files include all of the code necessary for a Kubernetes controller that invokes the appropriate Helm chart to field requests for CRs. We’ll discuss these files in greater detail later in this section.

Creating a new chart

To create an Operator with the skeleton for a new Helm chart, use the --type=helm argument. The following example creates the basis of a Helm Operator for the Visitors Site application (see Chapter 5):

$ OPERATOR_NAME=visitors-helm-operator
$ operator-sdk new $OPERATOR_NAME --api-version=example.com/v1 
  --kind=VisitorsApp --type=helm
INFO[0000] Creating new Helm operator 'visitors-helm-operator'.
INFO[0000] Created helm-charts/visitorsapp
INFO[0000] Generating RBAC rules
WARN[0000] 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.
INFO[0000] Created build/Dockerfile
INFO[0000] Created watches.yaml
INFO[0000] Created deploy/service_account.yaml
INFO[0000] Created deploy/role.yaml
INFO[0000] Created deploy/role_binding.yaml
INFO[0000] Created deploy/operator.yaml
INFO[0000] Created deploy/crds/example_v1_visitorsapp_crd.yaml
INFO[0000] Created deploy/crds/example_v1_visitorsapp_cr.yaml
INFO[0000] Project creation complete.

visitors-helm-operator is the name of the generated Operator. The other two arguments, --api-version and --kind, describe the CR this Operator manages. These arguments result in the creation of a basic CRD for the new type.

The SDK creates a new directory with the same name as $OPERATOR_NAME, which contains all of the Operator’s files. There are a few files and directories to note:

deploy/

This directory contains Kubernetes template files that are used to deploy and configure the Operator, including the CRD, the Operator deployment resource itself, and the necessary RBAC resources for the Operator to run.

helm-charts/

This directory contains a skeleton directory structure for a Helm chart with the same name as the CR kind. The files within are similar to those the Helm CLI creates when it initializes a new chart, including a values.yaml file. A new chart is added to this directory for each new CR type the Operator manages.

watches.yaml

This file maps each CR type to the specific Helm chart that is used to handle it.

At this point, everything is in place to begin to implement your chart. However, if you already have a chart written, there is an easier approach.

Use an existing chart

The process for building an Operator from an existing Helm chart is similar to that for creating an Operator with a new chart. In addition to the --type=helm argument, there are a few additional arguments to take into consideration:

--helm-chart

Tells the SDK to initialize the Operator with an existing chart. The value can be:

  • A URL to a chart archive

  • The repository and name of a remote chart

  • The location of a local directory

--helm-chart-repo

Specifies a remote repository URL for the chart (unless a local directory is otherwise specified).

--helm-chart-version

Tells the SDK to fetch a specific version of the chart. If this is omitted, the latest available version is used.

When using the --helm-chart argument, the --api-version and --kind arguments become optional. The api-version is defaulted to charts.helm.k8s.io/v1alpha1 and the kind name will be derived from the name of the chart. However, as the api-version carries information about the CR creator, we recommend that you explicitly populate these values appropriately. You can find an example Helm chart for deploying the Visitors Site application in this book’s GitHub repository.

The following example demonstrates how to build an Operator and initialize it using an archive of the Visitors Site Helm chart:

$ OPERATOR_NAME=visitors-helm-operator
$ wget https://github.com/kubernetes-operators-book/
  chapters/releases/download/1.0.0/visitors-helm.tgz  1
$ operator-sdk new $OPERATOR_NAME --api-version=example.com/v1 
  --kind=VisitorsApp --type=helm --helm-chart=./visitors-helm.tgz
INFO[0000] Creating new Helm operator 'visitors-helm-operator'.
INFO[0000] Created helm-charts/visitors-helm
INFO[0000] Generating RBAC rules
WARN[0000] 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.
INFO[0000] Created build/Dockerfile
INFO[0000] Created watches.yaml
INFO[0000] Created deploy/service_account.yaml
INFO[0000] Created deploy/role.yaml
INFO[0000] Created deploy/role_binding.yaml
INFO[0000] Created deploy/operator.yaml
INFO[0000] Created deploy/crds/example_v1_visitorsapp_crd.yaml
INFO[0000] Created deploy/crds/example_v1_visitorsapp_cr.yaml
INFO[0000] Project creation complete.
1

Due to an issue with how the Operator SDK handles redirects, you must manually download the chart tarball and pass it as a local reference.

The preceding example generates the same files as in the case of creating an Operator with a new Helm chart, with the notable exception that the chart files are populated from the specified archive:

$ ls -l $OPERATOR_NAME/helm-charts/visitors-helm/templates
_helpers.tpl
auth.yaml
backend-deployment.yaml
backend-service.yaml
frontend-deployment.yaml
frontend-service.yaml
mysql-deployment.yaml
mysql-service.yaml
tests

The SDK uses the values in the chart’s values.yaml file to populate the example CR template. For example, the Visitors Site Helm chart has the following values.yaml file:

$ cat $OPERATOR_NAME/helm-charts/visitors-helm/values.yaml
backend:
  size: 1

frontend:
  title: Helm Installed Visitors Site

The example CR generated by the SDK, found in the deploy/crds directory in the Operator project root directory, includes these same values in its spec section:

$ cat $OPERATOR_NAME/deploy/crds/example_v1_visitorsapp_cr.yaml
apiVersion: example.com/v1
kind: VisitorsApp
metadata:
  name: example-visitorsapp
spec:
  # Default values copied from <proj_dir>/helm-charts/visitors-helm/values.yaml

  backend:
    size: 1

  frontend:
    title: Helm Installed Visitors Site

Before running the chart, the Operator will map the values found in the custom resource’s spec field to the values.yaml file.

Fleshing Out the CRD

The generated CRD does not include specific details of the value input and state values of the CR type. Appendix B describes the steps you should take to finish defining the CR.

Reviewing Operator Permissions

The generated deployment files include the role the Operator will use to connect to the Kubernetes API. By default, this role is extremely permissive. Appendix C talks about how to fine-tune the role definition to limit the Operator’s permissions.

Running the Helm Operator

An Operator is delivered as a normal container image. However, during the development and testing cycle, it is often easier to skip the image creation process and simply run the Operator outside of the cluster. In this section we describe those steps (see Appendix A for information about running the Operator as a deployment inside the cluster). Run all the commands here from within the Operator project root directory:

  1. Create a local watches file. The generated watches.yaml file refers to a specific path where the Helm chart is found. This path makes sense in the deployed Operator scenario; the image creation process takes care of copying the chart to the necessary location. This watches.yaml file is still required when running the Operator outside of the cluster, so you need to manually make sure your chart can be found at that location.

    The simplest approach is to copy the existing watches.yaml file, which is located in the root of the Operator project:

    $ cp watches.yaml local-watches.yaml
    

    In the local-watches.yaml file, edit the chart field to contain the full path of the chart on your machine. Remember the name of the local watches file; you will need it later when you start the Operator process.

  2. Create the CRDs in the cluster using the kubectl command:

    $ kubectl apply -f deploy/crds/*_crd.yaml
    
  3. Once you have finished creating the CRDs, start the Operator using the following SDK command:

    $ operator-sdk up local --watches-file ./local-watches.yaml
    INFO[0000] Running the operator locally.
    INFO[0000] Using namespace default.  1
    
    1

    The Operator log messages will appear in this running process as it starts up and fields CR requests.

    This command starts a running process that behaves in the same way the Operator would if you had deployed it as a pod inside the cluster. (We’ll cover testing in more detail in “Testing an Operator”.)

Ansible Operator

Ansible is a popular management tool for automating the provisioning and configuration of commonly run tasks. Similar to a Helm chart, an Ansible playbook defines a series of tasks that are run on a set of servers. Reusable roles, which extend Ansible through custom functionality, may be used to enhance the set of tasks in a playbook.

One useful collection of roles is k8s, which provides tasks for interacting with a Kubernetes cluster. Using this module, you can write playbooks to handle the deployment of applications, including all of the necessary supporting Kubernetes resources.

The Operator SDK provides a way to build an Operator that will run Ansible playbooks to react to CR changes. The SDK supplies the code for the Kubernetes pieces, such as the controller, allowing you to focus on writing the playbooks themselves.

Building the Operator

As with its Helm support, the Operator SDK generates a project skeleton. When run with the --type=ansible argument, the project skeleton contains the structure for a blank Ansible role. The name of the role is derived from the specified CR type name.

The following example demonstrates creating an Ansible Operator that defines a CR for the Visitors Site application:

$ OPERATOR_NAME=visitors-ansible-operator
$ operator-sdk new $OPERATOR_NAME --api-version=example.com/v1 
  --kind=VisitorsApp --type=ansible
INFO[0000] Creating new Ansible operator 'visitors-ansible-operator'.
INFO[0000] Created deploy/service_account.yaml
INFO[0000] Created deploy/role.yaml
INFO[0000] Created deploy/role_binding.yaml
INFO[0000] Created deploy/crds/example_v1_visitorsapp_crd.yaml
INFO[0000] Created deploy/crds/example_v1_visitorsapp_cr.yaml
INFO[0000] Created build/Dockerfile
INFO[0000] Created roles/visitorsapp/README.md
INFO[0000] Created roles/visitorsapp/meta/main.yml
INFO[0000] Created roles/visitorsapp/files/.placeholder
INFO[0000] Created roles/visitorsapp/templates/.placeholder
INFO[0000] Created roles/visitorsapp/vars/main.yml
INFO[0000] Created molecule/test-local/playbook.yml
INFO[0000] Created roles/visitorsapp/defaults/main.yml
INFO[0000] Created roles/visitorsapp/tasks/main.yml
INFO[0000] Created molecule/default/molecule.yml
INFO[0000] Created build/test-framework/Dockerfile
INFO[0000] Created molecule/test-cluster/molecule.yml
INFO[0000] Created molecule/default/prepare.yml
INFO[0000] Created molecule/default/playbook.yml
INFO[0000] Created build/test-framework/ansible-test.sh
INFO[0000] Created molecule/default/asserts.yml
INFO[0000] Created molecule/test-cluster/playbook.yml
INFO[0000] Created roles/visitorsapp/handlers/main.yml
INFO[0000] Created watches.yaml
INFO[0000] Created deploy/operator.yaml
INFO[0000] Created .travis.yml
INFO[0000] Created molecule/test-local/molecule.yml
INFO[0000] Created molecule/test-local/prepare.yml
INFO[0000] Project creation complete.

This command produces a similar directory structure to the Helm Operator example. The SDK creates a deploy directory that contains the same set of files, including the CRD and deployment template.

There are a few notable differences from the Helm Operator:

watches.yaml
  • The purpose of this is the same as for the Helm Operator: it maps a CR type to the location of a file that is executed during its resolution. The Ansible Operator, however, supports two different types of files (these fields are mutually exclusive):

    • If the role field is included, it must point to the directory of an Ansible role that is executed during resource reconciliation.

    • If the playbook field is included, it must point to a playbook file that is run.

  • The SDK defaults this file to point to the role it created during generation.

roles/
  • This directory contains all of the Ansible roles that may be run by the Operator. The SDK generates the base files for a new role when the project is created.

  • If the Operator manages multiple CR types, multiple roles are added to this directory. Additionally, an entry for each type, and its associated role, is added to the watches file.

Next, you’ll implement the Ansible role for the CR. The details of what the role does will vary depending on the application: some common tasks include the creation of deployments and services to run the application’s containers. For more on writing Ansible roles, see the Ansible documentation.

You can find the Ansible role for deploying the Visitors Site in the book’s GitHub repository. For simplicity while following along with the example application, the role files are available as a release there. Similar to the previous Operator creation command, you can add the Visitors Site role with the following:

$ cd $OPERATOR_NAME/roles/visitorsapp
$ wget https://github.com/kubernetes-operators-book/
  chapters/releases/download/1.0.0/visitors-ansible.tgz
$ tar -zxvf visitors-ansible.tgz 1
$ rm visitors-ansible.tgz
1

This command overwrites the default generated role files with the files necessary to run the Visitors Site role.

We don’t cover writing Ansible roles in this book, but it’s important for you to understand how user-entered configuration values are propagated into an Ansible role.

As with the Helm Operator, configuration values come from the CR’s spec section. However, within the playbooks and roles, Ansible’s standard {{ variable_name }} syntax is used. Field names in Kubernetes typically use camel case (e.g., camelCase), so the Ansible Operator will convert the name of each field to snake case (e.g., snake_case) before passing the parameter to the Ansible role. That is, the field name serviceAccount would be converted to service_account. This allows the reuse of existing roles using the standard Ansible convention while also honoring the Kubernetes resource conventions. You can find the source for an Ansible role that deploys the Visitors Site in the book’s GitHub repository.

Fleshing Out the CRD

As with the Helm Operator, you’ll need to expand on the generated CRD to include the specifics of your CR. Consult Appendix B for more information.

Reviewing Operator Permissions

The Ansible Operator also includes a generated role the Operator uses to connect to the Kubernetes API. Check Appendix C for more on refining the default permissions.

Running the Ansible Operator

As with the Helm Operator, the easiest way to test and debug an Ansible Operator is to run it outside a cluster, avoiding the steps of building and pushing an image.

Before you can do this, however, there are a few extra steps you need to take:

  1. First, install Ansible on the machine running the Operator. Consult the Ansible documentation for specifics on how to install Ansible on your local OS.

  2. Additional Ansible-related packages must be installed as well, including the following (consult the documentation for details on installation):

  3. As with the Helm Operator, the watches.yaml file generated by the SDK refers to a specific directory for the Ansible role. So you’ll copy the watches file and modify it as necessary. Again, run these commands from within the Operator project root directory:

    $ cp watches.yaml local-watches.yaml
    

    In the local-watches.yaml file, change the role field to reflect the directory structure on your machine.

  4. Create the CRDs in the cluster using the kubectl command:

    $ kubectl apply -f deploy/crds/*_crd.yaml
    
  5. Once the CRDs are deployed in the cluster, run the Operator using the SDK:

    $ operator-sdk up local --watches-file ./local-watches.yaml
    INFO[0000] Running the operator locally.
    INFO[0000] Using namespace default.  1
    
    1

    The Operator log messages will appear in this running process as it starts up and fields CR requests.

    This command starts a running process that behaves as the Operator would if it was deployed as a pod inside the cluster.

Now let’s walk through the steps of how to test an Operator.

Testing an Operator

You can test both of the Adapter Operators using the same approach: by deploying a CR. Kubernetes notifies the Operator of the CR, which then executes the underlying files (either Helm charts or Ansible roles). The SDK generates a sample CR template in the deploy/crds directory that you can use, or you can create one manually.

Follow these steps to test both types of Operators discussed in this chapter:

  1. Edit the spec section of the example CR template (in the Visitors Site example, this is named example_v1_visitorsapp_cr.yaml) with whatever values are relevant to your CR.

  2. Create the resource (in the Operator project root directory) using the Kubernetes CLI:

    $ kubectl apply -f deploy/crds/*_cr.yaml
    

    The output for the Operator will appear in the same terminal where you ran the operator-sdk up local command. Once the test is complete, end the running process by pressing Ctrl-C.

  3. Navigate to the Visitors Site as described in Chapter 5 to verify the application works as expected.

  4. Once the test is complete, delete the CR using the kubectl delete command:

    $ kubectl delete -f deploy/crds/*_cr.yaml
    

During development, repeat this process to test changes. On each iteration, be sure to restart the Operator process to pick up any changes to the Helm or Ansible files.

Summary

You don’t need to be a programmer to write an Operator. The Operator SDK facilitates the packaging of two existing provisioning and configuration technologies, Helm and Ansible, as Operators. The SDK also provides a way to rapidly test and debug changes by running an Operator outside of the cluster, skipping the time-consuming image building and hosting steps.

In the next chapter, we’ll look at a more powerful and flexible way of implementing Operators by using the Go language.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.79.59