31

I need my Go app to monitor some resources in a Kubernetes cluster and react to their changes. Based on numerous articles and examples, I seem to have found a few ways to do it; however, I'm relatively new to Kubernetes, and they're described in terms much too complex to me, such that I'm still unable to grasp the difference between them — and thus, to know which one to use, so that I don't get some unexpected behaviors... Specifically:

  1. watch.Interface.ResultChan() — (acquired through e.g. rest.Request.Watch()) — this already seems to let me react to changes happening to a resource, by providing Added/Modified/Deleted events;
  2. cache.NewInformer() — when I implement a cache.ResourceEventHandler, I can pass it as last argument in:

    cache.NewInformer(
            cache.NewListWatchFromClient(clientset.Batch().RESTClient(), "jobs", ...),
            &batchv1.Job{},
            0,
            myHandler)
    

    — then, the myHandler object will receive OnAdd()/OnUpdate()/OnDelete() calls.

    To me, this seems more or less equivalent to the ResultChan I got in (1.) above; one difference is that apparently now I get the "before" state of the resource as a bonus, whereas with ResultChan I would only get its "after" state.

    Also, IIUC, this is actually somehow built on the watch.Interface mentioned above (through NewListWatchFromClient) — so I guess it brings some value over it, and/or fixes some (what?) deficiencies of a raw watch.Interface?

  3. cache.NewSharedInformer() and cache.NewSharedIndexInformer()(uh wow, now those are a mouthful...) I tried to dig through the godocs, but I feel completely overloaded with terminology I don't understand, such that I don't seem to be able to grasp the subtle (?) differences between a "regular" NewInformer vs. NewSharedInformer vs. NewSharedIndexInformer...

Could someone please help me understand the differences between above APIs in the Kubernetes client-go package?

Jonas
  • 121,568
  • 97
  • 310
  • 388
akavel
  • 4,789
  • 1
  • 35
  • 66

1 Answers1

32

These methods differ in the level of abstraction. If a higher level abstraction fits your need, you should use it, as many lower level problems is solved for you.

Informers is a higher level of abstraction than watch that also include listers. In most use cases you should use any kind of Informer instead of lower level abstraction. An Informer internally consists of a watcher, a lister and an in-memory cache.

SharedInformers share the connection with the API server and other resources between your informers.

SharedIndexInformers add an index to your data cache, in case you work with a larger dataset.

It is recommended to use SharedInformers instead of the lower level abstractions. Instantiate new SharedInformes from the same SharedInformerFactory. Theres is an example in Kubernetes Handbook example

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

podInformer := informerFactory.Core().V1().Pods()
serviceInformer := informerFactory.Core().V1().Services()

podInformer.Informer().AddEventHandler(
    // add your event handling 
)

// add event handling for serviceInformer

informerFactory.Start(wait.NeverStop)
informerFactory.WaitForCacheSync(wait.NeverStop)
Jonas
  • 121,568
  • 97
  • 310
  • 388
  • 1
    What lower level problems? — I don't seem to see any with Watcher, and I am specifically anxious what I might be missing? Also, does the "in-memory cache" help me in any way if I'm just listening to resource change events? Finally, does the connection sharing happen "magically" through client-go's internal global vars, or do I have to arrange for this somehow, e.g. by ensuring they're built from the same ListerWatcher, or something else? As to index = for larger dataset, this seems to clear that aspect up, thanks!!! – akavel Dec 31 '19 at 14:10
  • 5
    @akavel by just using watch, you may miss events (e.g. network problems) sometimes and it can be solved by regularly do a _list_ request... all this is handled for you with _Informers_. Yes, sharedInformers will be shared automatically, but you need to use the same **sharedInformerFactory** to instantiate them. – Jonas Dec 31 '19 at 14:16
  • 1
    so, if I just run `NewSharedInformer`, it will *not* be shared with anything, unless I get hold of some preexisting `SharedInformerFactory`? What's the point of this func then? If I don't have access to a factory, I can just create a non-shared `NewInformer` and it will be no different? – akavel Dec 31 '19 at 14:31
  • 1
    @akavel those are building blocks that the factory will call. See my code example on how to use `SharedInformerFactory` and it has fewer parameters, fewer technical details that the developer need to think about - higher abstractions. – Jonas Dec 31 '19 at 19:33
  • 1
    Thanks; your answer is definitely helpful; though you skipped answering some of the clarification questions I posed, which leaves me still with a lot of doubts, so I'm not ready to mark this as the Accepted Answer in its current state. – akavel Jan 01 '20 at 23:10
  • This should be the intro to the java library also, which is absolutely terribly described. Thank u for the answer. – Eugene Jan 01 '21 at 03:53