Ansible Operator Tutorial
An in-depth walkthough of building and running an Ansible-based operator.
NOTE: If your project was created with an operator-sdk
version prior to v1.0.0
please migrate, or consult the legacy docs.
Prerequisites
- Go through the installation guide.
- User authorized with
cluster-admin
permissions.
Overview
We will create a sample project to let you know how it works and this sample will:
- Create a Memcached Deployment if it doesn’t exist
- Ensure that the Deployment size is the same as specified by the Memcached CR spec
- Update the Memcached CR status using the status writer with the names of the memcached pods
Create a new project
Use the CLI to create a new memcached-operator project:
$ mkdir memcached-operator
$ cd memcached-operator
$ operator-sdk init --plugins=ansible --domain example.com
Among the files generated by this command is a Kubebuilder PROJECT
file. Subsequent operator-sdk
commands (and help text) run from the project root read this file and are aware that the project type is Ansible.
# Since this is an Ansible-based project, this help text is Ansible specific.
$ operator-sdk create api -h
Next, we will create a Memcached
API.
$ operator-sdk create api --group cache --version v1alpha1 --kind Memcached --generate-role
The scaffolded operator has the following structure:
Memcached
Custom Resource Definition, and a sampleMemcached
resource.- A “Manager” that reconciles the state of the cluster to the desired state
- A reconciler, which is an Ansible Role or Playbook.
- A
watches.yaml
file, which connects theMemcached
resource to thememcached
Ansible Role.
See scaffolded files reference and watches reference for more detailed information
Modify the Manager
Now we need to provide the reconcile logic, in the form of an Ansible Role, which will run every time a Memcached
resource is created, updated, or deleted.
Update roles/memcached/tasks/main.yml
:
---
- name: start memcached
community.kubernetes.k8s:
definition:
kind: Deployment
apiVersion: apps/v1
metadata:
name: '{{ ansible_operator_meta.name }}-memcached'
namespace: '{{ ansible_operator_meta.namespace }}'
spec:
replicas: "{{size}}"
selector:
matchLabels:
app: memcached
template:
metadata:
labels:
app: memcached
spec:
containers:
- name: memcached
command:
- memcached
- -m=64
- -o
- modern
- -v
image: "docker.io/memcached:1.4.36-alpine"
ports:
- containerPort: 11211
This memcached role will:
- Ensure a memcached Deployment exists
- Set the Deployment size
It is good practice to set default values for variables used in Ansible Roles, so edit roles/memcached/defaults/main.yml
:
---
# defaults file for Memcached
size: 1
Finally, update the Memcached
sample, config/samples/cache_v1alpha1_memcached.yaml
:
apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
name: memcached-sample
spec:
size: 3
The key-value pairs in the Custom Resource spec are passed to Ansible as extra variables.
Note: The names of all variables in the spec field are converted to snake_case by the operator before running ansible. For example, serviceAccount in the spec becomes service_account in ansible. You can disable this case conversion by setting the snakeCaseParameters
option to false
in your watches.yaml
. It is recommended that you perform some type validation in Ansible on the variables to ensure that your application is receiving expected input.
Finishing up
All that remains is building and pushing the operator container to your favorite registry.
$ make docker-build docker-push IMG=<some-registry>/<project-name>:tag
NOTE: To allow the cluster pull the image the repository needs to be set as public or you must configure an image pull secret
Run the Operator
There are three ways to run the operator:
- As Go program outside a cluster
- As a Deployment inside a Kubernetes cluster
- Managed by the Operator Lifecycle Manager (OLM) in bundle format
1. Run locally outside the cluster
Execute the following command, which install your CRDs and run the manager locally:
make install run
2. Run as a Deployment inside the cluster
Build and push the image
Build and push the image:
export USERNAME=<quay-namespace>
make docker-build docker-push IMG=quay.io/$USERNAME/memcached-operator:v0.0.1
Note: The name and tag of the image (IMG=<some-registry>/<project-name>:tag
) in both the commands can also be set in the Makefile. Modify the line which has IMG ?= controller:latest
to set your desired default image name.
Deploy the operator
By default, a new namespace is created with name <project-name>-system
, i.e. memcached-operator-system and will be used for the deployment.
Run the following to deploy the operator. This will also install the RBAC manifests from config/rbac
.
make deploy IMG=quay.io/$USERNAME/memcached-operator:v0.0.1
Verify that the memcached-operator is up and running:
$ kubectl get deployment -n memcached-operator-system
NAME READY UP-TO-DATE AVAILABLE AGE
memcached-operator-controller-manager 1/1 1 1 8m
3. Deploy your Operator with OLM
First, install OLM:
operator-sdk olm install
Then bundle your operator and push the bundle image:
make bundle IMG=$OPERATOR_IMG
# Note the "-bundle" component in the image name below.
export BUNDLE_IMG="quay.io/$USERNAME/memcached-operator-bundle:v0.0.1"
make bundle-build BUNDLE_IMG=$BUNDLE_IMG
make docker-push IMG=$BUNDLE_IMG
Finally, run your bundle:
operator-sdk run bundle $BUNDLE_IMG
Check out the docs for a deep dive into operator-sdk
‘s OLM integration.
Create a Memcached CR
Update the sample Memcached CR manifest at config/samples/cache_v1alpha1_memcached.yaml
and define the spec
as the following:
apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
name: memcached-sample
spec:
size: 3
Create the CR:
kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
Ensure that the memcached operator creates the deployment for the sample CR with the correct size:
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
memcached-sample 3/3 3 3 1m
Check the pods and CR status to confirm the status is updated with the memcached pod names:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
memcached-sample-6fd7c98d8-7dqdr 1/1 Running 0 1m
memcached-sample-6fd7c98d8-g5k7v 1/1 Running 0 1m
memcached-sample-6fd7c98d8-m7vn7 1/1 Running 0 1m
$ kubectl get memcached/memcached-sample -o yaml
apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
clusterName: ""
creationTimestamp: 2018-03-31T22:51:08Z
generation: 0
name: memcached-sample
namespace: default
resourceVersion: "245453"
selfLink: /apis/cache.example.com/v1alpha1/namespaces/default/memcacheds/memcached-sample
uid: 0026cc97-3536-11e8-bd83-0800274106a1
spec:
size: 3
status:
nodes:
- memcached-sample-6fd7c98d8-7dqdr
- memcached-sample-6fd7c98d8-g5k7v
- memcached-sample-6fd7c98d8-m7vn7
Update the size
Update config/samples/cache_v1alpha1_memcached.yaml
to change the spec.size
field in the Memcached CR from 3 to 5:
kubectl patch memcached memcached-sample -p '{"spec":{"size": 5}}' --type=merge
Confirm that the operator changes the deployment size:
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
memcached-sample 5/5 5 5 3m
Cleanup
Call the following to delete all deployed resources:
make undeploy
Next Steps
We recommend reading through the our Ansible development section for tips and tricks, including how to run the operator locally.
In this tutorial, the scaffolded watches.yaml
could be used as-is, but has additional optional features. See watches reference.
For brevity, some of the scaffolded files were left out of this guide. See Scaffolding Reference
This example built a namespaced scope operator, but Ansible operators can also be used with cluster-wide scope.
OLM will manage creation of most if not all resources required to run your operator, using a bit of setup from other operator-sdk commands. Check out the OLM integration guide.
Last modified January 28, 2021: doc: improve/fix memcached go tutorial (#4021) (7845eec3)