Ansible Roles and Playbooks manage Custom Resources
Why an Operator?
Operators manage complex stateful applications on Kubernetes. Writing an operator is often a full-fledged software project, using low level APIs and writing boilerplate code. The Operator SDK uses the Kubernetes controller-runtime
library to make writing operators easier by providing:
- High level APIs and abstractions to write the operational logic more intuitively
- Tools for scaffolding and code generation to bootstrap a new project fast
- Extensions to cover common operator use cases
What is an Ansible Operator?
A collection of building blocks from Operator SDK that enables Ansible to handle the reconciliation logic for an Operator.
Included in Operator Framework
Ansible Operator is one of the available types of Operators that Operator SDK is able to generate. Operator SDK can create an operator using Golang, Helm, or Ansible.
How do I use it?
Build your Ansible code on top of a provided base image along with some metadata to map Kubernetes events to Ansible Playbooks or Roles.
The workflow for a new Ansible Operator looks like this:
- Create a new Operator project using the Operator SDK Command Line Interface (CLI)
- Write the reconciling logic for your object using Ansible Playbooks and Roles
- Use the SDK CLI to build and generate the operator deployment manifests
- Optionally add additional CRD's using the SDK CLI and repeat steps 2 and 3
Now that we have demonstrated the Ansible k8s modules, we want to trigger Ansible logic when a Custom Resource changes. The Ansible Operator uses a Watches file, written in YAML, that maps Custom Resources to Ansible Roles/Playbooks.
NOTE: It is very important that the Ansible Roles and Playbooks in an Ansible Operator are idempotent, as these tasks will be repeated frequently to ensure the application is in its proper state.
Structure of a Watches file
The Watches file maps Custom Resources identified by Group, Version, and Kind ("GVK") to Ansible Roles and Playbooks. The Operator expects to find the Watches file at the path /opt/ansible/watches.yaml
. Each mapping in the Watches file must specify at least the mandatory fields:
- group: Group of the Custom Resource that you will be watching.
- version: Version of the Custom Resource that you will be watching.
- kind: Kind of the Custom Resource that you will be watching.
- role (_default_): Path to the Role that should be run by the Operator for a particular Group-Version-Kind (GVK). This field is mutually exclusive with the "playbook" field.
- playbook (_optional_): Path to the Playbook that should be run by the Operator for a particular Group-Version-Kind (GVK). A Playbook can be used to invoke multiple Roles. This field is mutually exclusive with the "role" field.
Examine a sample watches.yaml
:
---
- version: v1alpha1
group: foo.example.com
kind: Foo
# associates GVK with Role
role: /opt/ansible/roles/Foo
By default, operator-sdk new --type ansible
creates watches.yaml
configured to execute an Ansible role in response to a Custom Resource state change. This works well for smaller projects, but for more complex Ansible logic, we might not want it all in a single role.
Triggering a Playbook instead of a Role
Configuring the *Watches file* to run a Playbook allows the developer more flexibility in consuming other Ansible roles and enabling more customized application deployments.
To use a Playbook in your operator, you would modify `watches.yaml` as shown below:
---
- version: v1alpha1
group: foo.example.com
kind: Foo
# associating a Custom Resource GVK with a Playbook
playbook: /opt/ansible/playbook.yaml
For this to work, we would need to copy playbook.yaml
into the container image.
You would accomplish this by modifying the Operator Dockerfile
to COPY
playbook.yaml
into the container as shown below:
# Dockerfile at <project-name>/build/Dockerfile
FROM quay.io/operator-framework/ansible-operator
COPY roles/ ${HOME}/roles
COPY watches.yaml ${HOME}/watches.yaml
# New 'COPY' build step for playbook.yaml
COPY playbook.yaml ${HOME}/playbook.yaml
Preconfiguration for a Playbook
If you know from the start that you want your Operator to use a Playbook instead of a Role, you can generate your project scaffolding with the --generate-playbook
option:
operator-sdk init --plugins=ansible --domain example.com
operator-sdk create api --group cache --version v1alpha1 --kind Memcached --generate-playbook