OpenShift Operator SDK: Ansible Getting Started Guide Part III
Overview
In this article we will provide a hands-on guide to building your very first operator in Ansible. Using the Operator SDK we will learn how to create boilerplate code, build and deploy an operator.
This article is part of a series that will walk you through understanding and building operators in Go or Ansible end-to-end.
- OpenShift Operator Getting Started Part I
- OpenShift Operator SDK: Go Getting Started Guide Part II
- OpenShift Operator SDK: Ansible Getting Started Guide Part III
- OpenShift Operator Lifecycle Management Guide: Integrating Operators in OLM Part IV
Setup Development Environment
There are some prerequisites needed to develop and build an operator using Ansible. Also this guide and the operator-sdk assume you know Ansible roles. If you aren not yet up to speed please read about Ansible roles before proceeding.
Install Docker 17.03+
Add the docker ce repositories. Note: you can also use podman and buildah instead of Docker for those that want a complete and clean divorce.
$ sudo dnf -y install dnf-plugins-core
$ sudo dnf config-manager \
--add-repo \
https://download.docker.com/linux/fedora/docker-ce.repo
Install docker-ce
$ sudo dnf -y install docker-ce docker-ce-cli
$ sudo systemctl start docker $ sudo systemctl enable docker
Install Ansible and Module Dependencies
Install ansible
$ dnf install ansible
The Ansible runner and http runner is used to run a local version of the operator. This is very useful for development and testing.
$ pip3 install --user ansible-runner $ pip3 install --user ansible-runner-http
Install required python modules
$ pip3 install --user requests
$ pip3 install --user openshift
Install Operator Framework SDK
You can simply downloada pre-built release and install it under /usr/local/bin.
Building Your First Operator
The SDK is able to generate not only boilerplate code but also the CRDs, controller and API endpoints. In this example we will create a cars operator. It will simply provide an endpoint allowing us to do CRUD on car objects. Each car object will spawn a pod with a base image.
Using operatore-sdk cli create boilerplate code.
$ operator-sdk new cars-operator --api-version=cars.example.com/v1alpha1 --kind=Car --type=ansible
The operator sdk creates boilerplate roles.
$ ls cars-operator build deploy molecule requirements.yml roles watches.yaml
The build directory containers Dockerfile, deploy is where the yaml files are for creating the k8s operator objects (including CRD/CR), the roles directory contains a role called car and finally watches.yaml is the mapping from a k8s object / CRD to a roll.
By default when a car instance is created the operator will execute the role car. You can of course change the behavior, even configure finalizers for dealing with deletion of components not under the control of k8s (see the sdk guide)
Run Operator locally
The Ansible operator can be run locally. Simply deploy the CRD, service account and role.
Create new project for our operator
$ oc new-project cars-operator
Setup service accounts and role bindings
$ oc create -f deploy/service_account.yaml
$ oc create -f deploy/role.yaml
$ oc create -f deploy/role_binding.yaml
Create the CRD for our operator
$ oc create -f deploy/crds/cars.example.com_cars_crd.yaml
Run Operator locally
$ operator-sdk run --local
Create a car
$ vi car.yaml apiVersion: cars.example.com/v1alpha1 kind: Car metadata: name: example-car spec: size: 3 bmw: model: m3 audi: model: rs4
$ oc create -f car.yaml
Anything under 'spec' will be passed to Ansible as a variable. If you want to reference the size in the ansible role you simply use ''. You can also access nested variables such as the car model by using ''.
Build Ansible Operator
Create User on Quay.io
We will be using quay to store operator images. Authenticate using Github or Gmail to https://quay.io. Once authenticated go to account settings and set a password.
Test Quay.io Credentials
$ sudo docker login quay.io
Build Operator
Using operator-sdk cli build the operator which will push the image to your local Docker registry. Make sure you are in the cars-operator directory.
$ sudo operator-sdk build quay.io/ktenzer/cars-operator
Note: Substitute ktenzer for your username.
Push Operator to your Quay.io Account
$ sudo docker push quay.io/ktenzer/cars-operator:latest
Make Quay Repository Public
By default your Quay repository will be private. If we want to access it from OpenShift we need to make it public. Login to quay.io with your username/password
Select the cars-operator repository
Under Repository Settings enable visibility to be public
Update Image in operator.yaml
We need to point the image to the location in our Quay repository.
vi deploy/operator.yaml --- # Replace this with the built image name image: quay.io/ktenzer/cars-operator command: ---
Deploy Cars Operator on OpenShift
Now that the operator is built and pushed to our Quay repository we can deploy it on an OpenShift cluster.
Authenticate to OpenShift Cluster
$oc login https://api.ocp4.keithtenzer.com:6443
Create new project for our operator
$ oc new-project cars-operator
Setup service accounts and role bindings
$ oc create -f deploy/service_account.yaml
$ oc create -f deploy/role.yaml
$ oc create -f deploy/role_binding.yaml
Create the CRD for our operator
$ oc create -f deploy/crds/cars.example.com_cars_crd.yaml
Deploy our operator
$ oc create -f deploy/operator.yaml
Create the CR for our operator
$ oc create -f deploy/crds/cars.example.com_v1alpha1_car_cr.yaml
Using Cars Operator
The cars operator will automatically deploy an example-car. Whe can query our car object just like any other Kubernetes object. This is the beauty of CR/CRDs and operators. We can easily extend the Kubernetes API without needing to understand it’s complexity.
$ oc get car NAME AGE example-car 31m
Next we can get information about our example-car.
$ oc get car example-car -o yaml apiVersion: cars.example.com/v1alpha1 kind: Car metadata: creationTimestamp: "2020-01-25T12:15:45Z" generation: 1 name: example-car namespace: cars-operator resourceVersion: "2635723" selfLink: /apis/cars.example.com/v1alpha1/namespaces/cars-operator/cars/example-car uid: 6a424ef9-3f6c-11ea-a391-fa163e9f184b spec: size: 3
Looking at the running pods in our cars-operator project we see the operator and our example-car.
$ oc get pods -n cars-operator NAME READY STATUS RESTARTS AGE cars-operator-b98bff54d-t2465 1/1 Running 0 5m example-car-pod 1/1 Running 0 5m
Create a new Car
Lets now create a BMW car.
$ vi bmw.yaml kind: Car metadata: name: bmw spec: size: 3 bmw: model: m3
$ oc create -f bmw.yaml
Here we can see we now have a BMW car.
$ oc get car NAME AGE bmw 11m example-car 31m
Of course we can get information about our BMW car.
$ oc get car bmw -o yaml apiVersion: cars.example.com/v1alpha1 kind: Car metadata: creationTimestamp: "2020-01-25T12:35:25Z" generation: 1 name: bmw namespace: cars-operator resourceVersion: "2644044" selfLink: /apis/cars.example.com/v1alpha1/namespaces/cars-operator/cars/bmw uid: 294bc47f-3f6f-11ea-b32c-fa163e3e8e24 spec: size: 1
Finally as with the example-car, the operator will start a new pod when the BMW car is created.
$ oc get pods -n cars-operator NAME READY STATUS RESTARTS AGE bmw-pod 1/1 Running 0 10m cars-operator-b98bff54d-t2465 1/1 Running 0 14m example-car-pod 1/1 Running 0 14m
Cleanup
Follow these steps to remove the operator cleanly.
$ oc delete -f deploy/crds/cars.example.com_v1alpha1_car_cr.yaml
$ oc delete -f deploy/operator.yaml
$ oc delete -f deploy/role.yaml
$ oc delete -f deploy/role_binding.yaml
$ oc delete -f deploy/service_account.yaml
$ oc delete -f deploy/crds/cars.example.com_cars_crd.yaml
$ oc delete project cars-operator
Summary
In this article a step-by-step guide was provided to setup a development environment, generate boilerplate code and deploy our custom cars operator using Ansible on OpenShift with the Operator Framework.
Happy Operatoring!
(c) 2020 Keith Tenzer