Kubernetes Informers: No more load to API Server

Note: This is hand written blog. Since, many folks want to contribute to the CNCF project and most of the project are operator based. So, it is very important to understand the internal concept of Kubernetes. In this blog we will be starting with Kubernetes informer and I will try my best to continue this series further.

If you are working with Kubernetes development, custom controller then you might be aware about Kubernetes Informer. Informer is a component that watches the API server for resource changes and keeps a local cache in sync, triggering events when objects are added, updated, or delete

Why we need it ?

  1. Most important part is to decrease the continuous call to the API server.

  2. Since, it keep local cache which significantly increase in speed and efficiency.

Kubernetes Informer in Action:

This is sample go code that demonstrates use of informer in action:

package main

import (
 "fmt"
 "os"
 "path/filepath"
 "time"

 appsv1 "k8s.io/api/apps/v1"
 "k8s.io/client-go/informers"
 "k8s.io/client-go/kubernetes"
 "k8s.io/client-go/rest"
 "k8s.io/client-go/tools/cache"
 "k8s.io/client-go/tools/clientcmd"
)

func buildConfig() (*rest.Config, error) {

 homeDir, err := os.UserHomeDir()
 if err != nil {
  return nil, err
 }

 kubeconfig := filepath.Join(homeDir, ".kube", "config")
 return clientcmd.BuildConfigFromFlags("", kubeconfig)
}

func main() {
 config, err := buildConfig()
 if err != nil {
  panic(err)
 }

 clientset, err := kubernetes.NewForConfig(config)
 if err != nil {
  panic(err)
 }

 informerFactory := informers.NewSharedInformerFactory(clientset, 30*time.Second)

 deploymentInformer := informerFactory.Apps().V1().Deployments().Informer()

 deploymentInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{

  AddFunc: func(obj interface{}) {
   deployment := obj.(*appsv1.Deployment)
   fmt.Println("Deployment added:", deployment.Namespace+"/"+deployment.Name)
  },

  UpdateFunc: func(oldObj, newObj interface{}) {
   deployment := newObj.(*appsv1.Deployment)
   fmt.Println("Deployment updated:", deployment.Namespace+"/"+deployment.Name)
  },

  DeleteFunc: func(obj interface{}) {
   deployment := obj.(*appsv1.Deployment)
   fmt.Println("Deployment deleted:", deployment.Namespace+"/"+deployment.Name)
  },
 })

 stopCh := make(chan struct{})
 defer close(stopCh)

 informerFactory.Start(stopCh)

 if !cache.WaitForCacheSync(stopCh, deploymentInformer.HasSynced) {
  panic("Timed out waiting for caches to sync")
 }

 fmt.Println("Deployment informer is running...")
 <-stopCh
}

Whenever there will any changes happen to the deployment, Informer will trigger the event immediately and respective controller will reconcile it.
This is example of deployment informer but we have replicaset informer, pod informer e.t.c as well.

Demo link: https://drive.google.com/file/d/1dIT1bXJ_EqjvkCtk5H27G_MmVC7A_RFi/view?usp=sharing

Conclusion:

Always try to leverage informer while writing custom controller cause when we start moving to scale and production then we don’t want to see unusual things to happen.