Writing a Kubernetes Operator — Locust Operator
In my previous article, I discussed how two frameworks can be used to generate the basic structure for a Kubernetes Operator. In this article I would discuss how to build on top of the base generated by the framework, so let’s go through how to build an operator with simple functionality. If you don’t know about Kubernetes operators check out the session where I talked about extedin detail.
I will be building an operator for Locust. Locust is a framework for load testing written in python. It is easy to use and supports distributed mode for simulating a higher load. But running locust in distributed mode needs more effort and a good understanding of infrastructure.
Main functions expected from Locust Operator:
- Deploy locust in standalone mode
- Deploy locust in distributed mode
- Create Kubernetes Deployments and Services
- Number of slaves can be customized
Generate Resources
operator-sdk new locust-operator --repo=github.com/amila-ku/locust-operatoroperator-sdk add api --api-version=locustload.cndev.io/v1alpha1 --kind=Locust
Defining the API of Locust custom resource.
type definitions are created under pkg/api/locustload/v1alpha1/ in locust_types.go file, struct is already generated by operator SDK with a default value but I would replace with values for our locust operator.
LocustSpec directly relates to the configurable fields we want to have in our Custom Resource Definition(CRD). I defined variables for configurations required to run locust.
- HostURL: URL of the load tested service
- Image: docker image which contains locust scripts
- Users: Number of users locust should generate
- Workers: Number of workers locust should create
Generate CRDs and manifests
operator-sdk generate k8s; operator-sdk generate crds
Add the controller
operator-sdk add controller --api-version=locustload.cndev.io/v1alpha1 --kind=Locust
Modify the reconcile loop
All controllers have the Reconcile() function, this is where controller continuously check the state of resources and tries to maintain the desired specification. lets add a new deployment for locust master.
now let’s implement the deploymentForLocustO() function. This handles the creation of a Kubernetes deployment for locust master. Since locust only requires a single master this would not be user-modifiable. Container image and Target host URL is derived from the custom resource definition. Locust port 8089 is exposed together with 5557,5558 for locust workers.
Now we can build the project if everything is correctly added we would be able to generate a docker image with our changes. The image has to be pushed to a public docker registry so we can use it later.
operator-sdk build amilaku/locust-operator:v0.0.1
docker push amilaku/locust-operator:v0.0.1
then the operator.yaml has to be updated with a new image
sed -i 's|REPLACE_IMAGE|amilaku/locust-operator:v0.0.1|g' deploy/operator.yaml
then install the dependencies required
kubectl create -f deploy/service_account.yamlkubectl create -f deploy/role.yamlkubectl create -f deploy/role_binding.yamlkubectl create -f deploy/operator.yaml
install operator and create CRD
kubectl create -f deploy/operator.yaml
kubectl create -f deploy/crds/locustload.cndev.io_locusts_crd.yaml
now create the Custom Resource
kubectl create -f deploy/crds/locustload.cndev.io_v1alpha1_locust_cr.yaml
output
:~$ zk get locusts
NAME AGE
example-locust 26d:~$ zk get pods | grep -i locust
example-locust-5c55878467-hvqjl 1/1 Running 7 26d
locust-operator-697b767b6b-zwtvl 1/1 Running 7 26d
Additional Thoughts:
What I have shown so far is just creating a deployment with an operator created using the operator framework. This only completes the requirement to perform a standalone deployment of Locust. Locust Operator which can create distributed deployments with workers is available here https://github.com/amila-ku/locust-operator. Feel free to try it out and create an Issue/PR.
Related Articles: https://medium.com/devops-srilanka/writing-a-kubernetes-operator-2c7201c6745c
More about Operators: https://www.youtube.com/watch?v=zT7CHpHy1Oc&t=3684s