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
|
//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.
|
//it will also signal whether we enable/disable waiting for pod time buffers before triggering a scale-up.
|
||||||
IgnoreSchedulerProcessing bool
|
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)
|
originalScheduledPods, unschedulablePods := kube_util.ScheduledPods(pods), kube_util.UnschedulablePods(pods)
|
||||||
schedulerUnprocessed := make([]*apiv1.Pod, 0, 0)
|
schedulerUnprocessed := make([]*apiv1.Pod, 0, 0)
|
||||||
if a.IgnoreSchedulerProcessing {
|
if a.IgnoreSchedulerProcessing {
|
||||||
schedulerUnprocessed = kube_util.SchedulerUnprocessedPods(pods)
|
schedulerUnprocessed = kube_util.SchedulerUnprocessedPods(pods, a.IgnoredSchedulers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update cluster resource usage metrics
|
// Update cluster resource usage metrics
|
||||||
|
|
|
||||||
|
|
@ -992,6 +992,9 @@ func TestStaticAutoscalerRunOnceWithFilteringOnUpcomingNodesEnabledNoScaleUp(t *
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStaticAutoscalerRunOnceWithSchedulerProcessingIgnored(t *testing.T) {
|
func TestStaticAutoscalerRunOnceWithSchedulerProcessingIgnored(t *testing.T) {
|
||||||
|
ignoredScheduler := "ignored-scheduler"
|
||||||
|
nonIgnoredScheduler := "non-ignored-scheduler"
|
||||||
|
|
||||||
readyNodeLister := kubernetes.NewTestNodeLister(nil)
|
readyNodeLister := kubernetes.NewTestNodeLister(nil)
|
||||||
allNodeLister := kubernetes.NewTestNodeLister(nil)
|
allNodeLister := kubernetes.NewTestNodeLister(nil)
|
||||||
allPodListerMock := &podListerMock{}
|
allPodListerMock := &podListerMock{}
|
||||||
|
|
@ -1010,7 +1013,9 @@ func TestStaticAutoscalerRunOnceWithSchedulerProcessingIgnored(t *testing.T) {
|
||||||
p1 := BuildTestPod("p1", 600, 100)
|
p1 := BuildTestPod("p1", 600, 100)
|
||||||
p1.Spec.NodeName = "n1"
|
p1.Spec.NodeName = "n1"
|
||||||
p2 := BuildTestPod("p2", 600, 100, MarkUnschedulable())
|
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(
|
provider := testprovider.NewTestCloudProvider(
|
||||||
func(id string, delta int) error {
|
func(id string, delta int) error {
|
||||||
|
|
@ -1041,6 +1046,10 @@ func TestStaticAutoscalerRunOnceWithSchedulerProcessingIgnored(t *testing.T) {
|
||||||
MaxCoresTotal: 10,
|
MaxCoresTotal: 10,
|
||||||
MaxMemoryTotal: 100000,
|
MaxMemoryTotal: 100000,
|
||||||
IgnoreSchedulerProcessing: true,
|
IgnoreSchedulerProcessing: true,
|
||||||
|
IgnoredSchedulers: map[string]bool{
|
||||||
|
apiv1.DefaultSchedulerName: true,
|
||||||
|
ignoredScheduler: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
processorCallbacks := newStaticAutoscalerProcessorCallbacks()
|
processorCallbacks := newStaticAutoscalerProcessorCallbacks()
|
||||||
|
|
||||||
|
|
@ -1083,10 +1092,10 @@ func TestStaticAutoscalerRunOnceWithSchedulerProcessingIgnored(t *testing.T) {
|
||||||
// Scale up.
|
// Scale up.
|
||||||
readyNodeLister.SetNodes([]*apiv1.Node{n1})
|
readyNodeLister.SetNodes([]*apiv1.Node{n1})
|
||||||
allNodeLister.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()
|
daemonSetListerMock.On("List", labels.Everything()).Return([]*appsv1.DaemonSet{}, nil).Once()
|
||||||
podDisruptionBudgetListerMock.On("List").Return([]*policyv1.PodDisruptionBudget{}, 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))
|
err = autoscaler.RunOnce(later.Add(time.Hour))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
apiv1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apiserver/pkg/server/mux"
|
"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.")
|
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")
|
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.")
|
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 {
|
func isFlagPassed(name string) bool {
|
||||||
|
|
@ -392,6 +394,7 @@ func createAutoscalingOptions() config.AutoscalingOptions {
|
||||||
},
|
},
|
||||||
DynamicNodeDeleteDelayAfterTaintEnabled: *dynamicNodeDeleteDelayAfterTaintEnabled,
|
DynamicNodeDeleteDelayAfterTaintEnabled: *dynamicNodeDeleteDelayAfterTaintEnabled,
|
||||||
IgnoreSchedulerProcessing: *ignoreSchedulerProcessing,
|
IgnoreSchedulerProcessing: *ignoreSchedulerProcessing,
|
||||||
|
IgnoredSchedulers: scheduler_util.GetIgnoredSchedulersMap(*ignoredSchedulers),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,18 +144,23 @@ type PodLister interface {
|
||||||
List() ([]*apiv1.Pod, error)
|
List() ([]*apiv1.Pod, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isScheduled checks whether a pod is scheduled on a node or not
|
||||||
func isScheduled(pod *apiv1.Pod) bool {
|
func isScheduled(pod *apiv1.Pod) bool {
|
||||||
if pod == nil {
|
if pod == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return pod.Spec.NodeName != ""
|
return pod.Spec.NodeName != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isDeleted checks whether a pod is deleted not
|
||||||
func isDeleted(pod *apiv1.Pod) bool {
|
func isDeleted(pod *apiv1.Pod) bool {
|
||||||
if pod == nil {
|
if pod == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return pod.GetDeletionTimestamp() != nil
|
return pod.GetDeletionTimestamp() != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isUnschedulable checks whether a pod is unschedulable or not
|
||||||
func isUnschedulable(pod *apiv1.Pod) bool {
|
func isUnschedulable(pod *apiv1.Pod) bool {
|
||||||
if pod == nil {
|
if pod == nil {
|
||||||
return false
|
return false
|
||||||
|
|
@ -170,6 +175,12 @@ func isUnschedulable(pod *apiv1.Pod) bool {
|
||||||
return true
|
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.
|
// ScheduledPods is a helper method that returns all scheduled pods from given pod list.
|
||||||
func ScheduledPods(allPods []*apiv1.Pod) []*apiv1.Pod {
|
func ScheduledPods(allPods []*apiv1.Pod) []*apiv1.Pod {
|
||||||
var scheduledPods []*apiv1.Pod
|
var scheduledPods []*apiv1.Pod
|
||||||
|
|
@ -182,10 +193,20 @@ func ScheduledPods(allPods []*apiv1.Pod) []*apiv1.Pod {
|
||||||
return scheduledPods
|
return scheduledPods
|
||||||
}
|
}
|
||||||
|
|
||||||
// SchedulerUnprocessedPods is a helper method that returns all pods which are not yet processed by the scheduler
|
// SchedulerUnprocessedPods is a helper method that returns all pods which are not yet processed by the specified ignored schedulers
|
||||||
func SchedulerUnprocessedPods(allPods []*apiv1.Pod) []*apiv1.Pod {
|
func SchedulerUnprocessedPods(allPods []*apiv1.Pod, ignoredSchedulers map[string]bool) []*apiv1.Pod {
|
||||||
var unprocessedPods []*apiv1.Pod
|
var unprocessedPods []*apiv1.Pod
|
||||||
|
|
||||||
|
isDefaultSchedulerIgnored := getIsDefaultSchedulerIgnored(ignoredSchedulers)
|
||||||
|
|
||||||
for _, pod := range allPods {
|
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
|
// Make sure it's not scheduled or deleted
|
||||||
if isScheduled(pod) || isDeleted(pod) || isUnschedulable(pod) {
|
if isScheduled(pod) || isDeleted(pod) || isUnschedulable(pod) {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -147,3 +147,12 @@ func ConfigFromPath(path string) (*scheduler_config.KubeSchedulerConfiguration,
|
||||||
|
|
||||||
return cfgObj, nil
|
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.
|
// BuildDSTestPod creates a DaemonSet pod with cpu and memory.
|
||||||
func BuildDSTestPod(name string, cpu int64, mem int64) *apiv1.Pod {
|
func BuildDSTestPod(name string, cpu int64, mem int64) *apiv1.Pod {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue