-
3.1. api/v1alpha1/ptemplate_types.go
3.2. controllers/ptemplate_controller.go
3.4. Makefile
-
4.2. Creating An Instance
-
5.1. Uninstall CRDs
5.2. Undeploy controller
An Operator is an extension to Kubernetes and OpenShift that adds a Custom Resource type that provides higher level functionality.
Our example ptemplate-operator implements the Ptemplate
resource type (or Kind). When a resource of this Kind is created Kubernetes notifies the ptemplate-operator instance which runs its Reconcile() function which then creates a number of other resources needed to run our example. It does this by creating a Module resource that KMM
picks up and processes, and a DaemonSet
resource for or consumer pods that is picked up by core Kubernetes
e.g (See here)
apiVersion: ptemplates.pt.example.com/v1alpha
kind: Ptemplate
metadata:
name: consumer-pod
spec:
maxdev: 5
defaultmsg: ptemplate
consumerimage: quay.io/chrisp262/pt-device-plugin:consumer-latest
requiredDevices: 2
imageRepoSecret:
name: pt-char-pull-secret
selector:
node-role.kubernetes.io/worker: worker
Implementing an operator requires a large amount of boilerplate code. The easiest way to create this code is to use the operator-sdk. It's QuickStart and tutorial walks you through generating the boilerplate code for the operator and adding an api to it (which defines the yaml structure used to create the resource instance).
Once its generated all the code, a minimum the following files need customising to implement the functionality that this Operator requires.
This contains the data types that define the Custom Resources specification as a series of nested golang structures. Each field has a json:
tag that is used to define the associated field in the yaml (or json) used to create the resource e.g.
ConsumerImage string `json:"consumer"`
means that if a consumer
field is defined in the yaml that value is used to populate the ConsumerImage field in the relevant golang structure.
The heart of the operator is the Reconcile() function in this file which implements the actual functionality.
This function is run whenever an object of a type owned by the operator (listed in the SetupWithManager()
function), so it needs to be idempotent.
In the ptemplate-operator we own a DaemonSet so reconcile is run for each ptemplate resource, whenever any DaemonSet in the cluster is changed. It needs to get the config for the relevant ptemplate resource, check the sub-resources that should exist (in this case a KMM Module, and a DaemonSet) and reconcile them, creating them if they don't exist, correcting them if they differ from the ptemplate.Spec
, update the ptemplate.Status
if needed, and most importantly of all, do nothing if nothing has changed.
For production purposes it would make sense to break this file into separate files for the Module and consumer DaemonSet, remove some of the logging, handle errors better, and generally do all the things that turn example code into production quality. But that would also reduce its clarity!
By default the operator only has permissions on its own CRD, to be able to access any other resource in the cluster (e.g. to list, and create DaemonSets or Pods) permissions need to be added to this file. These then get deployed as part of the operator-manager-role e.g.
kubectl get clusterrole ptemplate-operator-manager-role
If the operator logs show permissions errors this is probably the file that needs changing.
The variable IMAGE_TAG_BASE
was set to have the required image name and registry, for convenience.
Building the operator is as easy as running make build
this will also regenerate any of the boilerplate code to reflect changes in the api (api/v1alpha1/ptemplate_types.go
).
Once the code is building successfully you can build the operator image running the docker-build
target
make docker-build
Will build the code and create a docker image with it named for the IMAGE_TAG_BASE
variable, which can then be pushed to a registry.
make deploy
Will deploy the image as a pod. It will also create all the other required resource such as the CRD
itself, serviceaccounts
, clusterroles
and rolebindings
etc. These are initially created by operator-sdk, but can be changed if needed.
(During testing I had a strange error where I was writing to the ptemplate.status.consumers
field to report the status but it wasn't getting changed, and no errors were being thrown, eventually I realised I had updated the CRD, and the config/crd/bases/pt.example.com_ptemplates.yaml
file had been updated by Make, but I hadn't re-run make deploy
so Kubernetes wasn't aware of my changes. Once I'd done that everything worked correctly)
Once the operator is installed and running successfully you can create a ptemplate
resource from the example yaml
kubectl apply -f example.yaml
This should create the module
resource and a daemonset
for the consumer in the current namespace. The next time the kmm
operator reconciles it will see the Module and load the kmod.
Any errors during the resource creation process can be seen in the controllers log. The controller is a pod with a name that starts ptemplate-operator-controller-manager-
in the ptemplate-operator-system
namespace:
e.g.
kubectl logs -n ptemplate-operator-system ptemplate-operator-controller-manager-5d4795f966-4cb98
To delete the CRDs from the cluster:
make uninstall
Undeploy the controller from the cluster:
make undeploy
-
Kubebuilder is not technically the same as the Operator SDK but is close enough to provide a good alternate view when the Operator SDK docs fail you.