Allow users to specify which schedulers to ignore
This commit is contained in:
		
							parent
							
								
									cfbfaa271a
								
							
						
					
					
						commit
						4635a6dc04
					
				|  | @ -281,4 +281,7 @@ type AutoscalingOptions struct { | |||
| 	//for scheduler to mark pods as unschedulable and will process both marked & non-marked pods
 | ||||
| 	//it will also signal whether we enable/disable waiting for pod time buffers before triggering a scale-up.
 | ||||
| 	IgnoreSchedulerProcessing bool | ||||
| 	//IgnoredSchedulers are used to specify which schedulers to ignore their processing
 | ||||
| 	//if IgnoreSchedulerProcessing is set to true
 | ||||
| 	IgnoredSchedulers map[string]bool | ||||
| } | ||||
|  |  | |||
|  | @ -311,7 +311,7 @@ func (a *StaticAutoscaler) RunOnce(currentTime time.Time) caerrors.AutoscalerErr | |||
| 	originalScheduledPods, unschedulablePods := kube_util.ScheduledPods(pods), kube_util.UnschedulablePods(pods) | ||||
| 	schedulerUnprocessed := make([]*apiv1.Pod, 0, 0) | ||||
| 	if a.IgnoreSchedulerProcessing { | ||||
| 		schedulerUnprocessed = kube_util.SchedulerUnprocessedPods(pods) | ||||
| 		schedulerUnprocessed = kube_util.SchedulerUnprocessedPods(pods, a.IgnoredSchedulers) | ||||
| 	} | ||||
| 
 | ||||
| 	// Update cluster resource usage metrics
 | ||||
|  |  | |||
|  | @ -992,6 +992,9 @@ func TestStaticAutoscalerRunOnceWithFilteringOnUpcomingNodesEnabledNoScaleUp(t * | |||
| } | ||||
| 
 | ||||
| func TestStaticAutoscalerRunOnceWithSchedulerProcessingIgnored(t *testing.T) { | ||||
| 	ignoredScheduler := "ignored-scheduler" | ||||
| 	nonIgnoredScheduler := "non-ignored-scheduler" | ||||
| 
 | ||||
| 	readyNodeLister := kubernetes.NewTestNodeLister(nil) | ||||
| 	allNodeLister := kubernetes.NewTestNodeLister(nil) | ||||
| 	allPodListerMock := &podListerMock{} | ||||
|  | @ -1010,7 +1013,9 @@ func TestStaticAutoscalerRunOnceWithSchedulerProcessingIgnored(t *testing.T) { | |||
| 	p1 := BuildTestPod("p1", 600, 100) | ||||
| 	p1.Spec.NodeName = "n1" | ||||
| 	p2 := BuildTestPod("p2", 600, 100, MarkUnschedulable()) | ||||
| 	p3 := BuildTestPod("p3", 600, 100) // Not yet processed by scheduler
 | ||||
| 	p3 := BuildTestPod("p3", 600, 100, AddSchedulerName(apiv1.DefaultSchedulerName)) // Not yet processed by scheduler, default scheduler is ignored
 | ||||
| 	p4 := BuildTestPod("p4", 600, 100, AddSchedulerName(ignoredScheduler))           // non-default scheduler & ignored, expects a scale-up
 | ||||
| 	p5 := BuildTestPod("p5", 600, 100, AddSchedulerName(nonIgnoredScheduler))        // non-default scheduler & not ignored, shouldn't cause a scale-up
 | ||||
| 
 | ||||
| 	provider := testprovider.NewTestCloudProvider( | ||||
| 		func(id string, delta int) error { | ||||
|  | @ -1041,6 +1046,10 @@ func TestStaticAutoscalerRunOnceWithSchedulerProcessingIgnored(t *testing.T) { | |||
| 		MaxCoresTotal:             10, | ||||
| 		MaxMemoryTotal:            100000, | ||||
| 		IgnoreSchedulerProcessing: true, | ||||
| 		IgnoredSchedulers: map[string]bool{ | ||||
| 			apiv1.DefaultSchedulerName: true, | ||||
| 			ignoredScheduler:           true, | ||||
| 		}, | ||||
| 	} | ||||
| 	processorCallbacks := newStaticAutoscalerProcessorCallbacks() | ||||
| 
 | ||||
|  | @ -1083,10 +1092,10 @@ func TestStaticAutoscalerRunOnceWithSchedulerProcessingIgnored(t *testing.T) { | |||
| 	// Scale up.
 | ||||
| 	readyNodeLister.SetNodes([]*apiv1.Node{n1}) | ||||
| 	allNodeLister.SetNodes([]*apiv1.Node{n1}) | ||||
| 	allPodListerMock.On("List").Return([]*apiv1.Pod{p1, p2, p3}, nil).Twice() | ||||
| 	allPodListerMock.On("List").Return([]*apiv1.Pod{p1, p2, p3, p4, p5}, nil).Twice() | ||||
| 	daemonSetListerMock.On("List", labels.Everything()).Return([]*appsv1.DaemonSet{}, nil).Once() | ||||
| 	podDisruptionBudgetListerMock.On("List").Return([]*policyv1.PodDisruptionBudget{}, nil).Once() | ||||
| 	onScaleUpMock.On("ScaleUp", "ng1", 2).Return(nil).Once() | ||||
| 	onScaleUpMock.On("ScaleUp", "ng1", 3).Return(nil).Once() | ||||
| 
 | ||||
| 	err = autoscaler.RunOnce(later.Add(time.Hour)) | ||||
| 	assert.NoError(t, err) | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ import ( | |||
| 
 | ||||
| 	"github.com/spf13/pflag" | ||||
| 
 | ||||
| 	apiv1 "k8s.io/api/core/v1" | ||||
| 	"k8s.io/apimachinery/pkg/api/meta" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apiserver/pkg/server/mux" | ||||
|  | @ -244,6 +245,7 @@ var ( | |||
| 	forceDaemonSets                         = flag.Bool("force-ds", false, "Blocks scale-up of node groups too small for all suitable Daemon Sets pods.") | ||||
| 	dynamicNodeDeleteDelayAfterTaintEnabled = flag.Bool("dynamic-node-delete-delay-after-taint-enabled", false, "Enables dynamic adjustment of NodeDeleteDelayAfterTaint based of the latency between CA and api-server") | ||||
| 	ignoreSchedulerProcessing               = flag.Bool("ignore-scheduler-processing", false, "If true, cluster autoscaler will not wait for scheduler to mark pods as unschedulable and will process both marked & non-marked pods (Schedulable pods will be filtered before scaling-up) it will also disable waiting for pod time buffers before triggering a scale-up.") | ||||
| 	ignoredSchedulers                       = pflag.StringSlice("ignore-schedulers", []string{apiv1.DefaultSchedulerName}, fmt.Sprintf("Names of schedulers to be ignored if '--ignore-scheduler-processing' is set to true. default value '%s' is used", apiv1.DefaultSchedulerName)) | ||||
| ) | ||||
| 
 | ||||
| func isFlagPassed(name string) bool { | ||||
|  | @ -392,6 +394,7 @@ func createAutoscalingOptions() config.AutoscalingOptions { | |||
| 		}, | ||||
| 		DynamicNodeDeleteDelayAfterTaintEnabled: *dynamicNodeDeleteDelayAfterTaintEnabled, | ||||
| 		IgnoreSchedulerProcessing:               *ignoreSchedulerProcessing, | ||||
| 		IgnoredSchedulers:                       scheduler_util.GetIgnoredSchedulersMap(*ignoredSchedulers), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -144,18 +144,23 @@ type PodLister interface { | |||
| 	List() ([]*apiv1.Pod, error) | ||||
| } | ||||
| 
 | ||||
| // isScheduled checks whether a pod is scheduled on a node or not
 | ||||
| func isScheduled(pod *apiv1.Pod) bool { | ||||
| 	if pod == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return pod.Spec.NodeName != "" | ||||
| } | ||||
| 
 | ||||
| // isDeleted checks whether a pod is deleted not
 | ||||
| func isDeleted(pod *apiv1.Pod) bool { | ||||
| 	if pod == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return pod.GetDeletionTimestamp() != nil | ||||
| } | ||||
| 
 | ||||
| // isUnschedulable checks whether a pod is unschedulable or not
 | ||||
| func isUnschedulable(pod *apiv1.Pod) bool { | ||||
| 	if pod == nil { | ||||
| 		return false | ||||
|  | @ -170,6 +175,12 @@ func isUnschedulable(pod *apiv1.Pod) bool { | |||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // getIsDefaultSchedulerIgnored checks if the default scheduler should be ignored or not
 | ||||
| func getIsDefaultSchedulerIgnored(ignoredSchedulers map[string]bool) bool { | ||||
| 	ignored, ok := ignoredSchedulers[apiv1.DefaultSchedulerName] | ||||
| 	return ignored && ok | ||||
| } | ||||
| 
 | ||||
| // ScheduledPods is a helper method that returns all scheduled pods from given pod list.
 | ||||
| func ScheduledPods(allPods []*apiv1.Pod) []*apiv1.Pod { | ||||
| 	var scheduledPods []*apiv1.Pod | ||||
|  | @ -182,10 +193,20 @@ func ScheduledPods(allPods []*apiv1.Pod) []*apiv1.Pod { | |||
| 	return scheduledPods | ||||
| } | ||||
| 
 | ||||
| // SchedulerUnprocessedPods is a helper method that returns all pods which are not yet processed by the scheduler
 | ||||
| func SchedulerUnprocessedPods(allPods []*apiv1.Pod) []*apiv1.Pod { | ||||
| // SchedulerUnprocessedPods is a helper method that returns all pods which are not yet processed by the specified ignored schedulers
 | ||||
| func SchedulerUnprocessedPods(allPods []*apiv1.Pod, ignoredSchedulers map[string]bool) []*apiv1.Pod { | ||||
| 	var unprocessedPods []*apiv1.Pod | ||||
| 
 | ||||
| 	isDefaultSchedulerIgnored := getIsDefaultSchedulerIgnored(ignoredSchedulers) | ||||
| 
 | ||||
| 	for _, pod := range allPods { | ||||
| 		// Don't add a pod with a scheduler that isn't specified by the user
 | ||||
| 		if !isDefaultSchedulerIgnored && pod.Spec.SchedulerName == "" { | ||||
| 			continue | ||||
| 		} | ||||
| 		if isIgnored, found := ignoredSchedulers[pod.Spec.SchedulerName]; !found || !isIgnored { | ||||
| 			continue | ||||
| 		} | ||||
| 		// Make sure it's not scheduled or deleted
 | ||||
| 		if isScheduled(pod) || isDeleted(pod) || isUnschedulable(pod) { | ||||
| 			continue | ||||
|  |  | |||
|  | @ -147,3 +147,12 @@ func ConfigFromPath(path string) (*scheduler_config.KubeSchedulerConfiguration, | |||
| 
 | ||||
| 	return cfgObj, nil | ||||
| } | ||||
| 
 | ||||
| // GetIgnoredSchedulersMap returns a map of scheduler names that should be ignored as keys, and values are set to true
 | ||||
| func GetIgnoredSchedulersMap(ignoredSchedulers []string) map[string]bool { | ||||
| 	ignoredSchedulersMap := make(map[string]bool, len(ignoredSchedulers)) | ||||
| 	for _, scheduler := range ignoredSchedulers { | ||||
| 		ignoredSchedulersMap[scheduler] = true | ||||
| 	} | ||||
| 	return ignoredSchedulersMap | ||||
| } | ||||
|  |  | |||
|  | @ -82,6 +82,13 @@ func MarkUnschedulable() func(*apiv1.Pod) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // AddSchedulerName adds scheduler name to a pod.
 | ||||
| func AddSchedulerName(schedulerName string) func(*apiv1.Pod) { | ||||
| 	return func(pod *apiv1.Pod) { | ||||
| 		pod.Spec.SchedulerName = schedulerName | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // BuildDSTestPod creates a DaemonSet pod with cpu and memory.
 | ||||
| func BuildDSTestPod(name string, cpu int64, mem int64) *apiv1.Pod { | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue