Adding custom resource to Kubernetes
Last updated
Last updated
Besides the default resources such as Pod, ReplicaSet, Deployment, StatefulSet, ..., kubernetes allows us to create additional custom resources to meet our needs in the project, each custom resource will serve a specific purpose. some specific purpose in our project. For example, to create a postgres database in kubernetes, we will first define a StatefulSet, then create a Service for this StatefulSet so that clients can connect to it. We can reduce the process of having to create many related things like that by defining a custom resource named Postgres. Every time we need a postgres database, we just need to create a Postgres custom resource, for example as follows:
Before talking about custom resources, we will talk about how to create custom controllers first. In the article Kubernetes internals architecture , we learn about the internal structure of kubernetes, it includes 4 main components: etcd, API server, Controller Manager, Scheduler. The Controller Manager is responsible for monitoring the API server and creating resources related to it. For example, the Deployment Controller will be responsible for monitoring the Deployment resource on the API server and creating related resources. In addition to the available Controller Managers inside kubernetes, we can create additional custom controllers to serve a different purpose.
In kubernetes, you will notice that when we create a ConfigMap and assign it to a Pod, when we update that ConfigMap with the new value, the Pod using our ConfigMap still retains the old value, if we want If the Pod uses the new ConfigMap value, we must delete that Pod and recreate it so it can update the new value. This job is a bit laborious, we can create a custom controller to do this job automatically, our customer controller will monitor the ConfigMap resource on the API server, and if it detects a change in the ConfigMap, it will automatically Delete that Pod and if the Pod is created with resources such as ReplicaSet, Deployment, it will be automatically recreated, at this point our new Pod will use the new value of ConfigMap.
To create a custom controller, first we will write code that will monitor the API server with the resources we want, then we will build it into an image, then we will create a Deployment that uses the image we just created and deploy it to kubernetes. In essence, a customer controller is just a normal Deployment guy, the difference is that we will write our own code to interact with the API server.
Now we will create a customer controller named config-watcher-controller, it will monitor ConfigMap and if any Pod uses the related ConfigMap, when ConfigMap changes, this new ConfigMap value will also be updated for the Pod. automatically. It will do this by deleting the old Pod so that the Pod can be recreated. An illustration of config-watcher-controller is as follows:
Now we will proceed to write the code and build the image for the config-watcher container, creating a file config-watcher-controller.sh with the following code:
We don't need to understand the detailed code, the above code will have the task of monitoring ConfigMap on the API server with the command curl -N -s $base/api/v1/${ns}/configmaps?watch=true | while read -r event
, if ConfigMap changes anything, it will run to the code below, and detect if If any ConfigMap changes and a Pod uses it, it will delete that Pod with the code:
We just need to understand the action of the above code. Next, we create the Dockerfile:
Next, build and push the image to your docker hub if you don't want to use image 080196/configmap-watcher :
After finishing, we create a file named config-watcher-controller.yaml with the following configuration:
In the file above, we will create a separate ServiceAccount to use for our config-watcher-controller instead of using the default ServiceAccount, then we will use RoleBinding to bind the edit role to this ServiceAccount to allow it the right to edit functions. resource in a namespace. In the Deployment config, we will declare the above ServiceAccount in the Pod template, so that the container application in the Pod can edit kubernetes resources. You can review lesson 13 to better understand ServiceAccount. To let this controller know which namespace it is monitoring, we use the Downward API mentioned in lesson 11.
We create the above controller:
Ok, so we have created a custom controller, next we will create a resource and test it, to use config-watcher-controller, when we declare ConfigMap, we will add the annotations field with the value k8spatterns.io/podDeleteSelector: "<key>=<value>"
, with key value being the label of the Pod that we want to update the ConfigMap value for when our ConfigMap changes. Create a file named confimap-watch.yaml:
Create a file named deploy-use-configmap-watcher.yaml:
The Pod's label value is the value we declared in the ConfiMap above. We create a Deployment and access it to see the previous ConfigMap value, then we will update the ConfigMap value again and see if our Pod is automatically updated with the value or not:
Update the confimap-watch.yaml file:
Now if we get pod, we will see that one is being deleted and another is being created:
When we access the new pod and check again, we will see that our env has been updated:
Ok, so our custom controller is running correctly 😄. At this point, we know how to write and create a customer controller to serve our specific purpose, but before we do anything, we should see if anyone has done it before. If so, we can use it. Just for use, because writing a controller that can run in a production environment requires many more tests, the code above is only for the dev environment.
After we talked about custom controllers, now we will talk about custom resources. To create a custom resource, we will use CustomResourceDefinition , we will write CustomResourceDefinition and define our custom resource values ​​in it. Then we will create this CustomResourceDefinition, then we will write a controller to monitor our newly created custom resource and perform actions related to it. For example, we have a website-crd.yaml file with the CustomResourceDefinition config as follows:
In the file above, the group and version fields will define the API group and version of this resource on the API server. The values ​​of those two fields above are extensions.example.com
and v1
, so when we declare the resource, the apiVersion we will specify is extensions.example.com/v1
, In the names field , we will define kind and two verbs, singular and plural, of the custom resource. With the above value, we will execute the command kubectl get website
to list all Website resources. We create CustomResourceDefinition above:
Now we have defined our custom resource on the API server. To create this resource, we create a file called website.yaml with the following configuration:
Ok, so we have a custom resource. To interact with it, we also use interaction commands like other normal resources:
So our custom resource has run successfully, but it will not take any action. In order for this resource to be actually used, we need to create a controller for it. We will want our Website resource to act as follows, we will define the Website resource with the path to gitlab of the static website we need to deploy, then we will create the Website resource, our controller will monitor and detect the presence of the Website resource. newly created, it will create a resource Deployment to deploy the Pod that runs the static website, then it will create a service that exposes website traffic to the client.
We create a controller website-controller.yaml with the following configuration:
The operation of the website-controller container is similar to the configmap watch controller we wrote above.
If you want to see the code, look at this github repo https://github.com/luksa/k8s-website-controller . Now we will create the Website resource again to see it in action:
Ok, so our custom resource and controller are working correctly. Instead of having to create Deployment and Service separately, we just need to define a CRD. Although doing this at first is difficult, later on, our work will be much easier