support UpdatePodScaleDown instead of UpdatePodRequest

This commit is contained in:
Kensei Nakada 2024-07-20 19:15:16 +09:00
parent 0dee497876
commit fa8092f838
7 changed files with 42 additions and 22 deletions

View File

@ -57,8 +57,8 @@ var (
assignedPodOtherUpdate = ClusterEvent{Resource: Pod, ActionType: updatePodOther, Label: "AssignedPodUpdate"} assignedPodOtherUpdate = ClusterEvent{Resource: Pod, ActionType: updatePodOther, Label: "AssignedPodUpdate"}
// AssignedPodDelete is the event when an assigned pod is deleted. // AssignedPodDelete is the event when an assigned pod is deleted.
AssignedPodDelete = ClusterEvent{Resource: Pod, ActionType: Delete, Label: "AssignedPodDelete"} AssignedPodDelete = ClusterEvent{Resource: Pod, ActionType: Delete, Label: "AssignedPodDelete"}
// PodRequestChange is the event when a pod's resource request is changed. // PodRequestScaledDown is the event when a pod's resource request is scaled down.
PodRequestChange = ClusterEvent{Resource: Pod, ActionType: UpdatePodRequest, Label: "PodRequestChange"} PodRequestScaledDown = ClusterEvent{Resource: Pod, ActionType: UpdatePodScaleDown, Label: "PodRequestScaledDown"}
// PodLabelChange is the event when a pod's label is changed. // PodLabelChange is the event when a pod's label is changed.
PodLabelChange = ClusterEvent{Resource: Pod, ActionType: UpdatePodLabel, Label: "PodLabelChange"} PodLabelChange = ClusterEvent{Resource: Pod, ActionType: UpdatePodLabel, Label: "PodLabelChange"}
// NodeSpecUnschedulableChange is the event when unschedulable node spec is changed. // NodeSpecUnschedulableChange is the event when unschedulable node spec is changed.
@ -108,7 +108,7 @@ var (
func PodSchedulingPropertiesChange(newPod *v1.Pod, oldPod *v1.Pod) (events []ClusterEvent) { func PodSchedulingPropertiesChange(newPod *v1.Pod, oldPod *v1.Pod) (events []ClusterEvent) {
podChangeExtracters := []podChangeExtractor{ podChangeExtracters := []podChangeExtractor{
extractPodLabelsChange, extractPodLabelsChange,
extractPodResourceRequestChange, extractPodScaleDown,
} }
for _, fn := range podChangeExtracters { for _, fn := range podChangeExtracters {
@ -128,13 +128,27 @@ func PodSchedulingPropertiesChange(newPod *v1.Pod, oldPod *v1.Pod) (events []Clu
type podChangeExtractor func(newNode *v1.Pod, oldNode *v1.Pod) *ClusterEvent type podChangeExtractor func(newNode *v1.Pod, oldNode *v1.Pod) *ClusterEvent
func extractPodResourceRequestChange(newPod, oldPod *v1.Pod) *ClusterEvent { // extractPodScaleDown interprets the update of a pod and returns PodRequestScaledDown event if any pod's resource request(s) is scaled down.
func extractPodScaleDown(newPod, oldPod *v1.Pod) *ClusterEvent {
opt := resource.PodResourcesOptions{ opt := resource.PodResourcesOptions{
InPlacePodVerticalScalingEnabled: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling), InPlacePodVerticalScalingEnabled: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling),
} }
if !equality.Semantic.DeepEqual(resource.PodRequests(newPod, opt), resource.PodRequests(oldPod, opt)) { newPodRequests := resource.PodRequests(newPod, opt)
return &PodRequestChange oldPodRequests := resource.PodRequests(oldPod, opt)
for rName, oldReq := range oldPodRequests {
newReq, ok := newPodRequests[rName]
if !ok {
// The resource request of rName is removed.
return &PodRequestScaledDown
} }
if oldReq.MilliValue() > newReq.MilliValue() {
// The resource request of rName is scaled down.
return &PodRequestScaledDown
}
}
return nil return nil
} }

View File

@ -291,7 +291,7 @@ func Test_podSchedulingPropertiesChange(t *testing.T) {
}, },
}, },
} }
podWithBigRequestAndLabel := &v1.Pod{ podWithSmallRequestAndLabel := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar"}, Labels: map[string]string{"foo": "bar"},
}, },
@ -300,7 +300,7 @@ func Test_podSchedulingPropertiesChange(t *testing.T) {
{ {
Name: "app", Name: "app",
Resources: v1.ResourceRequirements{ Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("101m")}, Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("100m")},
}, },
}, },
}, },
@ -309,7 +309,7 @@ func Test_podSchedulingPropertiesChange(t *testing.T) {
ContainerStatuses: []v1.ContainerStatus{ ContainerStatuses: []v1.ContainerStatus{
{ {
Name: "app", Name: "app",
AllocatedResources: v1.ResourceList{v1.ResourceCPU: resource.MustParse("101m")}, AllocatedResources: v1.ResourceList{v1.ResourceCPU: resource.MustParse("100m")},
}, },
}, },
}, },
@ -347,16 +347,22 @@ func Test_podSchedulingPropertiesChange(t *testing.T) {
want: []ClusterEvent{PodLabelChange}, want: []ClusterEvent{PodLabelChange},
}, },
{ {
name: "only pod's resource request is updated", name: "pod's resource request is scaled down",
oldPod: podWithBigRequest,
newPod: podWithSmallRequest,
want: []ClusterEvent{PodRequestScaledDown},
},
{
name: "pod's resource request is scaled up",
oldPod: podWithSmallRequest, oldPod: podWithSmallRequest,
newPod: podWithBigRequest, newPod: podWithBigRequest,
want: []ClusterEvent{PodRequestChange}, want: []ClusterEvent{assignedPodOtherUpdate},
}, },
{ {
name: "both pod's resource request and label are updated", name: "both pod's resource request and label are updated",
oldPod: podWithSmallRequest, oldPod: podWithBigRequest,
newPod: podWithBigRequestAndLabel, newPod: podWithSmallRequestAndLabel,
want: []ClusterEvent{PodLabelChange, PodRequestChange}, want: []ClusterEvent{PodLabelChange, PodRequestScaledDown},
}, },
{ {
name: "untracked properties of pod is updated", name: "untracked properties of pod is updated",

View File

@ -252,7 +252,7 @@ func (f *Fit) EventsToRegister(_ context.Context) ([]framework.ClusterEventWithH
if f.enableInPlacePodVerticalScaling { if f.enableInPlacePodVerticalScaling {
// If InPlacePodVerticalScaling (KEP 1287) is enabled, then PodRequestUpdate event should be registered // If InPlacePodVerticalScaling (KEP 1287) is enabled, then PodRequestUpdate event should be registered
// for this plugin since a Pod update may free up resources that make other Pods schedulable. // for this plugin since a Pod update may free up resources that make other Pods schedulable.
podActionType |= framework.UpdatePodRequest podActionType |= framework.UpdatePodScaleDown
} }
return []framework.ClusterEventWithHint{ return []framework.ClusterEventWithHint{
{Event: framework.ClusterEvent{Resource: framework.Pod, ActionType: podActionType}, QueueingHintFn: f.isSchedulableAfterPodChange}, {Event: framework.ClusterEvent{Resource: framework.Pod, ActionType: podActionType}, QueueingHintFn: f.isSchedulableAfterPodChange},
@ -296,7 +296,7 @@ func (f *Fit) isSchedulableAfterPodChange(logger klog.Logger, pod *v1.Pod, oldOb
return framework.QueueSkip, nil return framework.QueueSkip, nil
} }
logger.V(5).Info("the max request resources of another scheduled pod got reduced and it may make the unscheduled pod schedulable", "pod", klog.KObj(pod), "modifiedPod", klog.KObj(modifiedPod)) logger.V(5).Info("another scheduled pod or the target pod itself got scaled down, and it may make the unscheduled pod schedulable", "pod", klog.KObj(pod), "modifiedPod", klog.KObj(modifiedPod))
return framework.Queue, nil return framework.Queue, nil
} }

View File

@ -1095,7 +1095,7 @@ func TestEventsToRegister(t *testing.T) {
"Register events with InPlacePodVerticalScaling feature enabled", "Register events with InPlacePodVerticalScaling feature enabled",
true, true,
[]framework.ClusterEventWithHint{ []framework.ClusterEventWithHint{
{Event: framework.ClusterEvent{Resource: "Pod", ActionType: framework.UpdatePodRequest | framework.Delete}}, {Event: framework.ClusterEvent{Resource: "Pod", ActionType: framework.UpdatePodScaleDown | framework.Delete}},
{Event: framework.ClusterEvent{Resource: "Node", ActionType: framework.Add | framework.Update}}, {Event: framework.ClusterEvent{Resource: "Node", ActionType: framework.Add | framework.Update}},
}, },
}, },

View File

@ -66,8 +66,8 @@ const (
// It's better to narrow down the scope of the event by using them instead of Update event // It's better to narrow down the scope of the event by using them instead of Update event
// for better performance in requeueing. // for better performance in requeueing.
UpdatePodLabel UpdatePodLabel
// UpdatePodRequest is a update for pod's resource request calculated by resource.PodRequests() function. // UpdatePodScaleDown is an update for pod's scale down (i.e., any resource request is reduced).
UpdatePodRequest UpdatePodScaleDown
// updatePodOther is a update for pod's other fields. // updatePodOther is a update for pod's other fields.
// It's used only for the internal event handling, and thus unexported. // It's used only for the internal event handling, and thus unexported.
@ -76,7 +76,7 @@ const (
All ActionType = 1<<iota - 1 All ActionType = 1<<iota - 1
// Use the general Update type if you don't either know or care the specific sub-Update type to use. // Use the general Update type if you don't either know or care the specific sub-Update type to use.
Update = UpdateNodeAllocatable | UpdateNodeLabel | UpdateNodeTaint | UpdateNodeCondition | UpdateNodeAnnotation | UpdatePodLabel | UpdatePodRequest | updatePodOther Update = UpdateNodeAllocatable | UpdateNodeLabel | UpdateNodeTaint | UpdateNodeCondition | UpdateNodeAnnotation | UpdatePodLabel | UpdatePodScaleDown | updatePodOther
) )
// GVK is short for group/version/kind, which can uniquely represent a particular API resource. // GVK is short for group/version/kind, which can uniquely represent a particular API resource.

View File

@ -1169,7 +1169,7 @@ func (p *PriorityQueue) AssignedPodAdded(logger klog.Logger, pod *v1.Pod) {
// may make pending pods with matching affinity terms schedulable. // may make pending pods with matching affinity terms schedulable.
func (p *PriorityQueue) AssignedPodUpdated(logger klog.Logger, oldPod, newPod *v1.Pod, event framework.ClusterEvent) { func (p *PriorityQueue) AssignedPodUpdated(logger klog.Logger, oldPod, newPod *v1.Pod, event framework.ClusterEvent) {
p.lock.Lock() p.lock.Lock()
if event.Resource == framework.Pod && event.ActionType&framework.UpdatePodRequest != 0 { if event.Resource == framework.Pod && event.ActionType&framework.UpdatePodScaleDown != 0 {
// In this case, we don't want to pre-filter Pods by getUnschedulablePodsWithCrossTopologyTerm // In this case, we don't want to pre-filter Pods by getUnschedulablePodsWithCrossTopologyTerm
// because Pod related events may make Pods that were rejected by NodeResourceFit schedulable. // because Pod related events may make Pods that were rejected by NodeResourceFit schedulable.
p.moveAllToActiveOrBackoffQueue(logger, framework.AssignedPodUpdate, oldPod, newPod, nil) p.moveAllToActiveOrBackoffQueue(logger, framework.AssignedPodUpdate, oldPod, newPod, nil)

View File

@ -893,7 +893,7 @@ func Test_UnionedGVKs(t *testing.T) {
name: "plugins with default profile (InPlacePodVerticalScaling: enabled)", name: "plugins with default profile (InPlacePodVerticalScaling: enabled)",
plugins: schedulerapi.PluginSet{Enabled: defaults.PluginsV1.MultiPoint.Enabled}, plugins: schedulerapi.PluginSet{Enabled: defaults.PluginsV1.MultiPoint.Enabled},
want: map[framework.GVK]framework.ActionType{ want: map[framework.GVK]framework.ActionType{
framework.Pod: framework.Add | framework.UpdatePodLabel | framework.UpdatePodRequest | framework.Delete, framework.Pod: framework.Add | framework.UpdatePodLabel | framework.UpdatePodScaleDown | framework.Delete,
framework.Node: framework.All, framework.Node: framework.All,
framework.CSINode: framework.All - framework.Delete, framework.CSINode: framework.All - framework.Delete,
framework.CSIDriver: framework.All - framework.Delete, framework.CSIDriver: framework.All - framework.Delete,