volcano.sh/volcano@v1.9.0/docs/design/event-handler-framework.md (about) 1 EventHandler Framework 2 3 [@sivanzcw](https://github.com/sivanzcw); Dec 1, 2021 4 5 ## Table of Contents 6 7 ## Summary 8 9 As a monolithic scheduler, the volcano use a single, centralized scheduling algorithm for all jobs. It schedules 10 jobs based on resources managed by kubernetes. When there is a need for scheduling for resources managed outsider 11 of kubernetes, there is no mechanism to do it today. 12 13 The proposal is to make volcano extensible by adding the capability to replace the resource manager to help achieve this 14 functionality. 15 16 ## Motivation 17 18 There are two ways to add new scheduling rules to volcano: 19 - Update existing or add new scheduler plugins and recompiling, whether to use the plugin is determined by the 20 configuration file 21 - Secondary development based on the volcano, implementing a new scheduler framework that encapsulate the volcano 22 23 This document describes the second approach. If you want to expand the ability of volcano add new policies and 24 specialized implementations, and have certain R & D capabilities, then this approach is likely the better choice. 25 26 The rise of secure containers makes it possible to deploy both containerized applications and virtual machine applications 27 on physical machines. In the scene of a hybrid deployment of containerized applications and virtual machine applications, 28 scheduling jobs based on resources managed by kubernetes alone will lead to resource conflicts. In such scenario, the 29 organizations will want to run multiple frameworks in the same cluster and a central resource manager above frameworks 30 is needed to coordinate and allocate resources uniformly. This two-level scheduling approach is used by a number of systems. 31 32 In two-level scheduler, like `Mesos`, a centralized resource allocator dynamically partitions a cluster, allocating 33 resources to different scheduler frameworks. Resources distributed to the frameworks contain only `avaliable` resources, 34 ones that are currently unused. Because only one framework is examining a resource at a time, resource conflicts is avoided. 35 36 To access volcano as one framework to a two-level scheduler, the volcano needs to provide access for external modifications 37 to its cached resource objects. 38 39 ## Goals 40 41 - volcano provides an entrance for external modification of its cached resource objects 42 43 ## Design Details 44 45  46 47 ### In-tree SharedIndexInformer 48 49 As the native implementation of kubernetes, the In-tree SharedIndexInformer provides eventually consistent linkage of 50 volcano to the authoritative state of a given collection of objects from kubernetes cluster. It watches specified 51 resources and causes all changes to be reflected in the given store, also trigger event handler to handle the object. 52 53 ### Custom SharedIndexInformer 54 55 The Custom SharedIndexInformer provides eventually consistent linkage of volcano to the authoritative state of a given 56 collection of objects from non-kubernetes cluster. Different from the original sharedIndexInformer, custom 57 sharedIndexInformer does not get resource objects from kubernetes cluster, but an external resource manager service. 58 The Custom sharedIndexInformer uses the same local data caching and event distribution mechanism as the native informer. 59 60 ### Resource EventHandler Framework 61 62 Both the native and the custom informers use the same resource event handler framework. The framework provides a set of 63 event handler interfaces, including the processing of `ADD`, `MODIFIED`, `DELETED` events of objects involved. Volcano 64 provides a default event handling suite. The developers can customize the event handling processing to replace the 65 default implementation. 66 67 ### Custom SharedIndexInformer implementation 68 69 Add a customInformers array to the `SchedulerCache` struct as the entry to the custom SharedIndexInformer implementation. 70 71 ```go 72 // SchedulerCache cache for the kube batch 73 type SchedulerCache struct { 74 ...... 75 CustomInformers []cache.SharedIndexInformer 76 ...... 77 } 78 ``` 79 80 Start the custom informer when `SchedulerCache` starts 81 82 ```go 83 // Run starts the schedulerCache 84 func (sc *SchedulerCache) Run(stopCh <-chan struct{}) { 85 go sc.podInformer.Informer().Run(stopCh) 86 ...... 87 for _, i := range sc.CustomInformers { 88 go i.Run(stopCh) 89 } 90 ...... 91 } 92 ``` 93 94 Wait the custom informer to synced before the starting of scheduling loop 95 96 ```go 97 // WaitForCacheSync sync the cache with the api server 98 func (sc *SchedulerCache) WaitForCacheSync(stopCh <-chan struct{}) bool { 99 return cache.WaitForCacheSync(stopCh, 100 func() []cache.InformerSynced { 101 informerSynced := []cache.InformerSynced{ 102 sc.podInformer.Informer().HasSynced, 103 ...... 104 } 105 ...... 106 }()..., 107 ) && func() bool { 108 for _, i := range sc.CustomInformers { 109 if !i.HasSynced() { 110 return false 111 } 112 } 113 114 return true 115 } 116 } 117 ``` 118 119 ### EventHandler Factory 120 121 - define an interface, including object event processing function set 122 - provide a factory to realize the object event processing function 123 - provide a factory registration mechanism 124 125 ```go 126 // Interface containers all involved object event processing interfaces. 127 type Interface interface { 128 AddPod(obj interface{}) 129 // other object event processing interfaces. 130 } 131 132 type Factory func(cache *SchedulerCache, config io.Reader, restConfig *rest.Config) (Interface, error) 133 134 var eventHandlers = make(map[string]Factory) 135 var eventHandlerMutex sync.Mutex 136 137 func RegisterEventHandler(name string, eventHandler Factory) { 138 eventHandlerMutex.Lock() 139 defer eventHandlerMutex.Unlock() 140 if _, found := eventHandlers[name]; found { 141 klog.Fatalf("EventHandler %q was registered twice.", name) 142 } 143 klog.V(1).Infof("Registered eventHandler %q.", name) 144 eventHandlers[name] = eventHandler 145 } 146 147 func GetEventHandler(name, configFilePath string, cache *SchedulerCache, restConfig *rest.Config) (Interface, error) { 148 eventHandlerMutex.Lock() 149 defer eventHandlerMutex.Unlock() 150 f, found := eventHandlers[name] 151 if !found { 152 return nil, nil 153 } 154 155 var config *os.File 156 if len(configFilePath) != 0 { 157 config, err = os.Open(configFilePath) 158 } 159 return f(cache, config, restConfig), nil 160 } 161 ``` 162 163 #### Implement the default eventHandler 164 165 ```go 166 type defaultEventHandler struct{ 167 cache *SchedulerCache 168 } 169 170 func init() { 171 RegisterEventHandler("default", func(cache *SchedulerCache, config io.Reader, restConfig *rest.Config) (i Interface, err error) { 172 return &defaultEventHandler{cache:cache}, nil 173 }) 174 } 175 176 func (eh *defaultEventHandler) AddPod(obj interface{}) { 177 pod, ok := obj.(*v1.Pod) 178 if !ok { 179 klog.Errorf("Cannot convert to *v1.Pod: %v", obj) 180 return 181 } 182 183 sc := eh.cache 184 185 sc.Mutex.Lock() 186 defer sc.Mutex.Unlock() 187 188 err := sc.addPod(pod) 189 if err != nil { 190 klog.Errorf("Failed to add pod <%s/%s> into cache: %v", 191 pod.Namespace, pod.Name, err) 192 return 193 } 194 klog.V(3).Infof("Added pod <%s/%v> into cache.", pod.Namespace, pod.Name) 195 } 196 ``` 197 198 #### Dynamically obtain eventHandler processing object 199 200 - Add `--event-handler` command to declare the eventHandler processing object 201 - Add `--config-path` command to declare the configuration path of eventHandler 202 203 ```go 204 eventHadler := GetEventHandler(eventHandlerName, configFilePath, sc, config) 205 sc.nodeInformer.Informer().AddEventHandlerWithResyncPeriod( 206 cache.FilteringResourceEventHandler{ 207 FilterFunc: func(obj interface{}) bool { 208 switch v := obj.(type) { 209 case *v1.Node: 210 return responsibleForNode(v.Name, mySchedulerPodName, c) 211 default: 212 return false 213 } 214 }, 215 Handler: cache.ResourceEventHandlerFuncs{ 216 AddFunc: eventHadler.AddNode, 217 UpdateFunc: eventHadler.UpdateNode, 218 DeleteFunc: eventHadler.DeleteNode, 219 }, 220 }, 221 0, 222 ) 223 ```