Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parse pod's node affinity once in preFilter #99213

Merged
merged 1 commit into from
Feb 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/kube-scheduler/app/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ profiles:
{Name: "PodTopologySpread"},
{Name: "InterPodAffinity"},
{Name: "VolumeBinding"},
{Name: "NodeAffinity"},
},
"FilterPlugin": {
{Name: "NodeUnschedulable"},
Expand Down Expand Up @@ -290,6 +291,7 @@ profiles:
{Name: "PodTopologySpread"},
{Name: "InterPodAffinity"},
{Name: "VolumeBinding"},
{Name: "NodeAffinity"},
},
"FilterPlugin": {
{Name: "NodeUnschedulable"},
Expand Down
1 change: 1 addition & 0 deletions pkg/scheduler/algorithmprovider/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func getDefaultConfig() *schedulerapi.Plugins {
{Name: podtopologyspread.Name},
{Name: interpodaffinity.Name},
{Name: volumebinding.Name},
{Name: nodeaffinity.Name},
},
},
Filter: schedulerapi.PluginSet{
Expand Down
3 changes: 3 additions & 0 deletions pkg/scheduler/algorithmprovider/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func TestClusterAutoscalerProvider(t *testing.T) {
{Name: podtopologyspread.Name},
{Name: interpodaffinity.Name},
{Name: volumebinding.Name},
{Name: nodeaffinity.Name},
},
},
Filter: schedulerapi.PluginSet{
Expand Down Expand Up @@ -150,6 +151,7 @@ func TestApplyFeatureGates(t *testing.T) {
{Name: podtopologyspread.Name},
{Name: interpodaffinity.Name},
{Name: volumebinding.Name},
{Name: nodeaffinity.Name},
},
},
Filter: schedulerapi.PluginSet{
Expand Down Expand Up @@ -231,6 +233,7 @@ func TestApplyFeatureGates(t *testing.T) {
{Name: podtopologyspread.Name},
{Name: interpodaffinity.Name},
{Name: volumebinding.Name},
{Name: nodeaffinity.Name},
},
},
Filter: schedulerapi.PluginSet{
Expand Down
19 changes: 19 additions & 0 deletions pkg/scheduler/apis/config/testing/compatibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"PreFilterPlugin": {
{Name: "NodeResourcesFit"},
{Name: "NodePorts"},
{Name: "NodeAffinity"},
},
"FilterPlugin": {
{Name: "NodeUnschedulable"},
Expand Down Expand Up @@ -125,6 +126,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
},
Expand Down Expand Up @@ -180,6 +182,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
},
Expand Down Expand Up @@ -240,6 +243,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
},
Expand Down Expand Up @@ -312,6 +316,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
{Name: "InterPodAffinity"},
Expand Down Expand Up @@ -390,6 +395,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
{Name: "InterPodAffinity"},
Expand Down Expand Up @@ -479,6 +485,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
{Name: "InterPodAffinity"},
Expand Down Expand Up @@ -579,6 +586,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
{Name: "InterPodAffinity"},
Expand Down Expand Up @@ -680,6 +688,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
{Name: "VolumeBinding"},
Expand Down Expand Up @@ -788,6 +797,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
{Name: "VolumeBinding"},
Expand Down Expand Up @@ -908,6 +918,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
{Name: "VolumeBinding"},
Expand Down Expand Up @@ -1030,6 +1041,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
{Name: "VolumeBinding"},
Expand Down Expand Up @@ -1152,6 +1164,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
{Name: "VolumeBinding"},
Expand Down Expand Up @@ -1279,6 +1292,7 @@ func TestCompatibility_v1_Scheduler(t *testing.T) {
"QueueSortPlugin": {{Name: "PrioritySort"}},
"PreFilterPlugin": {
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "NodeResourcesFit"},
{Name: "ServiceAffinity"},
{Name: "VolumeBinding"},
Expand Down Expand Up @@ -1411,6 +1425,7 @@ func TestAlgorithmProviderCompatibility(t *testing.T) {
{Name: "PodTopologySpread"},
{Name: "InterPodAffinity"},
{Name: "VolumeBinding"},
{Name: "NodeAffinity"},
},
"FilterPlugin": {
{Name: "NodeUnschedulable"},
Expand Down Expand Up @@ -1480,6 +1495,7 @@ func TestAlgorithmProviderCompatibility(t *testing.T) {
{Name: "PodTopologySpread"},
{Name: "InterPodAffinity"},
{Name: "VolumeBinding"},
{Name: "NodeAffinity"},
},
"FilterPlugin": {
{Name: "NodeUnschedulable"},
Expand Down Expand Up @@ -1568,6 +1584,7 @@ func TestPluginsConfigurationCompatibility(t *testing.T) {
{Name: "PodTopologySpread"},
{Name: "InterPodAffinity"},
{Name: "VolumeBinding"},
{Name: "NodeAffinity"},
},
"FilterPlugin": {
{Name: "NodeUnschedulable"},
Expand Down Expand Up @@ -1689,6 +1706,7 @@ func TestPluginsConfigurationCompatibility(t *testing.T) {
{Name: "PodTopologySpread"},
{Name: "InterPodAffinity"},
{Name: "VolumeBinding"},
{Name: "NodeAffinity"},
},
"FilterPlugin": {
{Name: "NodeUnschedulable"},
Expand Down Expand Up @@ -1890,6 +1908,7 @@ func TestPluginsConfigurationCompatibility(t *testing.T) {
PreFilter: config.PluginSet{
Disabled: []config.Plugin{
{Name: "NodeResourcesFit"},
{Name: "NodeAffinity"},
{Name: "NodePorts"},
{Name: "InterPodAffinity"},
{Name: "PodTopologySpread"},
Expand Down
1 change: 1 addition & 0 deletions pkg/scheduler/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ func TestCreateFromConfig(t *testing.T) {
Enabled: []schedulerapi.Plugin{
{Name: "NodeResourcesFit"},
{Name: "NodePorts"},
{Name: "NodeAffinity"},
{Name: "VolumeBinding"},
{Name: "PodTopologySpread"},
{Name: "InterPodAffinity"},
Expand Down
2 changes: 2 additions & 0 deletions pkg/scheduler/framework/plugins/legacy_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ func NewLegacyRegistry() *LegacyRegistry {
plugins.Filter = appendToPluginSet(plugins.Filter, nodeports.Name, nil)
plugins.PreFilter = appendToPluginSet(plugins.PreFilter, nodeports.Name, nil)
plugins.Filter = appendToPluginSet(plugins.Filter, nodeaffinity.Name, nil)
plugins.PreFilter = appendToPluginSet(plugins.PreFilter, nodeaffinity.Name, nil)
})
registry.registerPredicateConfigProducer(PodToleratesNodeTaintsPred,
func(_ ConfigProducerArgs, plugins *config.Plugins, _ *[]config.PluginConfig) {
Expand All @@ -259,6 +260,7 @@ func NewLegacyRegistry() *LegacyRegistry {
registry.registerPredicateConfigProducer(MatchNodeSelectorPred,
func(_ ConfigProducerArgs, plugins *config.Plugins, _ *[]config.PluginConfig) {
plugins.Filter = appendToPluginSet(plugins.Filter, nodeaffinity.Name, nil)
plugins.PreFilter = appendToPluginSet(plugins.PreFilter, nodeaffinity.Name, nil)
})
registry.registerPredicateConfigProducer(CheckNodeUnschedulablePred,
func(_ ConfigProducerArgs, plugins *config.Plugins, _ *[]config.PluginConfig) {
Expand Down
1 change: 1 addition & 0 deletions pkg/scheduler/framework/plugins/nodeaffinity/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ go_library(
"//pkg/scheduler/framework:go_default_library",
"//pkg/scheduler/framework/plugins/helper:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/component-helpers/scheduling/corev1/nodeaffinity:go_default_library",
],
Expand Down
77 changes: 74 additions & 3 deletions pkg/scheduler/framework/plugins/nodeaffinity/node_affinity.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"

v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/component-helpers/scheduling/corev1/nodeaffinity"
"k8s.io/kubernetes/pkg/scheduler/apis/config"
Expand All @@ -46,6 +47,9 @@ const (
// preScoreStateKey is the key in CycleState to NodeAffinity pre-computed data for Scoring.
preScoreStateKey = "PreScore" + Name

// preFilterStateKey is the key in CycleState to NodeAffinity pre-compute data for Filtering.
preFilterStateKey = "PreFilter" + Name

// ErrReasonPod is the reason for Pod's node affinity/selector not matching.
ErrReasonPod = "node(s) didn't match Pod's node affinity/selector"

Expand All @@ -58,6 +62,28 @@ func (pl *NodeAffinity) Name() string {
return Name
}

type preFilterState struct {
requiredNodeSelector labels.Selector
requiredNodeAffinity *nodeaffinity.LazyErrorNodeSelector
}

// Clone just returns the same state because it is not affected by pod additions or deletions.
func (s *preFilterState) Clone() framework.StateData {
return s
}

// PreFilter builds and writes cycle state used by Filter.
func (pl *NodeAffinity) PreFilter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod) *framework.Status {
state := getPodRequiredNodeSelectorAndAffinity(pod)
cycleState.Write(preFilterStateKey, state)
return nil
}

// PreFilterExtensions not necessary for this plugin as state doesn't depend on pod additions or deletions.
func (pl *NodeAffinity) PreFilterExtensions() framework.PreFilterExtensions {
return nil
}

// Filter checks if the Node matches the Pod .spec.affinity.nodeAffinity and
// the plugin's added affinity.
func (pl *NodeAffinity) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
Expand All @@ -68,8 +94,25 @@ func (pl *NodeAffinity) Filter(ctx context.Context, state *framework.CycleState,
if pl.addedNodeSelector != nil && !pl.addedNodeSelector.Match(node) {
return framework.NewStatus(framework.UnschedulableAndUnresolvable, errReasonEnforced)
}
if !pluginhelper.PodMatchesNodeSelectorAndAffinityTerms(pod, node) {
return framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonPod)

s, err := getPreFilterState(state)
if err != nil {
// Fallback to calculate requiredNodeSelector and requiredNodeAffinity
// here when PreFilter is disabled.
s = getPodRequiredNodeSelectorAndAffinity(pod)
}

if s.requiredNodeSelector != nil {
if !s.requiredNodeSelector.Matches(labels.Set(node.Labels)) {
return framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonPod)
}
}
if s.requiredNodeAffinity != nil {
// Ignore parsing errors for backwards compatibility.
matches, _ := s.requiredNodeAffinity.Match(node)
if !matches {
return framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReasonPod)
}
}
return nil
}
Expand Down Expand Up @@ -122,7 +165,7 @@ func (pl *NodeAffinity) Score(ctx context.Context, state *framework.CycleState,

s, err := getPreScoreState(state)
if err != nil {
// fallback to calculate preferredNodeAffinity here when PreScore is disabled
// Fallback to calculate preferredNodeAffinity here when PreScore is disabled.
preferredNodeAffinity, err := getPodPreferredNodeAffinity(pod)
if err != nil {
return 0, framework.AsStatus(err)
Expand Down Expand Up @@ -204,3 +247,31 @@ func getPreScoreState(cycleState *framework.CycleState) (*preScoreState, error)
}
return s, nil
}

func getPodRequiredNodeSelectorAndAffinity(pod *v1.Pod) *preFilterState {
var selector labels.Selector
if len(pod.Spec.NodeSelector) > 0 {
selector = labels.SelectorFromSet(pod.Spec.NodeSelector)
}
// Use LazyErrorNodeSelector for backwards compatibility of parsing errors.
var affinity *nodeaffinity.LazyErrorNodeSelector
if pod.Spec.Affinity != nil &&
pod.Spec.Affinity.NodeAffinity != nil &&
pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
affinity = nodeaffinity.NewLazyErrorNodeSelector(pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a comment:

Using LazyErrorNodeSelector for backwards compatibility of parsing errors

}
return &preFilterState{requiredNodeSelector: selector, requiredNodeAffinity: affinity}
}

func getPreFilterState(cycleState *framework.CycleState) (*preFilterState, error) {
c, err := cycleState.Read(preFilterStateKey)
if err != nil {
return nil, fmt.Errorf("reading %q from cycleState: %v", preFilterStateKey, err)
}

s, ok := c.(*preFilterState)
if !ok {
return nil, fmt.Errorf("invalid PreFilter state, got type %T", c)
}
return s, nil
}