scheduler/NodeUnschedulable: reduce pod scheduling latency

Co-authored-by: wackxu <xushiwei5@huawei.com>
Co-authored-by: Kensei Nakada <handbomusic@gmail.com>
This commit is contained in:
carlory 2023-12-15 14:00:52 +08:00
parent ae185414f4
commit d36a7089cd
2 changed files with 124 additions and 1 deletions

View File

@ -22,8 +22,10 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
v1helper "k8s.io/component-helpers/scheduling/corev1"
"k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/scheduler/framework"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/names"
"k8s.io/kubernetes/pkg/scheduler/util"
)
// NodeUnschedulable plugin filters nodes that set node.Spec.Unschedulable=true unless
@ -48,10 +50,31 @@ const (
// failed by this plugin schedulable.
func (pl *NodeUnschedulable) EventsToRegister() []framework.ClusterEventWithHint {
return []framework.ClusterEventWithHint{
{Event: framework.ClusterEvent{Resource: framework.Node, ActionType: framework.Add | framework.UpdateNodeTaint}},
{Event: framework.ClusterEvent{Resource: framework.Node, ActionType: framework.Add | framework.Update}, QueueingHintFn: pl.isSchedulableAfterNodeChange},
}
}
// isSchedulableAfterNodeChange is invoked for all node events reported by
// an informer. It checks whether that change made a previously unschedulable
// pod schedulable.
func (pl *NodeUnschedulable) isSchedulableAfterNodeChange(logger klog.Logger, pod *v1.Pod, oldObj, newObj interface{}) (framework.QueueingHint, error) {
_, modifiedNode, err := util.As[*v1.Node](oldObj, newObj)
if err != nil {
return framework.Queue, err
}
if !modifiedNode.Spec.Unschedulable {
logger.V(5).Info("node was created or updated, pod may be schedulable now", "pod", klog.KObj(pod), "node", klog.KObj(modifiedNode))
return framework.Queue, nil
}
// TODO: also check if the original node meets the pod's requestments once preCheck is completely removed.
// See: https://github.com/kubernetes/kubernetes/issues/110175
logger.V(5).Info("node was created or updated, but it doesn't make this pod schedulable", "pod", klog.KObj(pod), "node", klog.KObj(modifiedNode))
return framework.QueueSkip, nil
}
// Name returns name of the plugin. It is used in logs, etc.
func (pl *NodeUnschedulable) Name() string {
return Name

View File

@ -85,3 +85,103 @@ func TestNodeUnschedulable(t *testing.T) {
}
}
}
func TestIsSchedulableAfterNodeChange(t *testing.T) {
testCases := []struct {
name string
pod *v1.Pod
oldObj, newObj interface{}
expectedHint framework.QueueingHint
expectedErr bool
}{
{
name: "backoff-wrong-new-object",
pod: &v1.Pod{},
newObj: "not-a-node",
expectedHint: framework.Queue,
expectedErr: true,
},
{
name: "backoff-wrong-old-object",
pod: &v1.Pod{},
newObj: &v1.Node{
Spec: v1.NodeSpec{
Unschedulable: true,
},
},
oldObj: "not-a-node",
expectedHint: framework.Queue,
expectedErr: true,
},
{
name: "skip-queue-on-unschedulable-node-added",
pod: &v1.Pod{},
newObj: &v1.Node{
Spec: v1.NodeSpec{
Unschedulable: true,
},
},
expectedHint: framework.QueueSkip,
},
{
name: "queue-on-schedulable-node-added",
pod: &v1.Pod{},
newObj: &v1.Node{
Spec: v1.NodeSpec{
Unschedulable: false,
},
},
expectedHint: framework.Queue,
},
{
name: "skip-unrelated-change",
pod: &v1.Pod{},
newObj: &v1.Node{
Spec: v1.NodeSpec{
Unschedulable: true,
Taints: []v1.Taint{
{
Key: v1.TaintNodeNotReady,
Effect: v1.TaintEffectNoExecute,
},
},
},
},
oldObj: &v1.Node{
Spec: v1.NodeSpec{
Unschedulable: true,
},
},
expectedHint: framework.QueueSkip,
},
{
name: "queue-on-unschedulable-field-change",
pod: &v1.Pod{},
newObj: &v1.Node{
Spec: v1.NodeSpec{
Unschedulable: false,
},
},
oldObj: &v1.Node{
Spec: v1.NodeSpec{
Unschedulable: true,
},
},
expectedHint: framework.Queue,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
pl := &NodeUnschedulable{}
got, err := pl.isSchedulableAfterNodeChange(logger, testCase.pod, testCase.oldObj, testCase.newObj)
if err != nil && !testCase.expectedErr {
t.Errorf("unexpected error: %v", err)
}
if got != testCase.expectedHint {
t.Errorf("isSchedulableAfterNodeChange() = %v, want %v", got, testCase.expectedHint)
}
})
}
}