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

Avoid overprovisioning capacity for workloads on expired nodes #1929

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion pkg/controllers/provisioning/provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,14 @@ import (
"sigs.k8s.io/karpenter/pkg/scheduling"
nodeutils "sigs.k8s.io/karpenter/pkg/utils/node"
nodepoolutils "sigs.k8s.io/karpenter/pkg/utils/nodepool"
podutil "sigs.k8s.io/karpenter/pkg/utils/pod"
"sigs.k8s.io/karpenter/pkg/utils/pretty"
)

const (
AvgProvisionTimeSeconds = 90
)

// LaunchOptions are the set of options that can be used to trigger certain
// actions and configuration during scheduling
type LaunchOptions struct {
Expand Down Expand Up @@ -319,11 +324,33 @@ func (p *Provisioner) Schedule(ctx context.Context) (scheduler.Results, error) {
return scheduler.Results{}, err
}

nodesMarkedForDeletion := nodes.Deleting()

nodesMarkedForDeletion = lo.Filter(nodesMarkedForDeletion, func(n *state.StateNode, _ int) bool {
expirationTimeString, exists := n.NodeClaim.ObjectMeta.Annotations[v1.NodeClaimTerminationTimestampAnnotationKey]
if !exists {
pods, err := n.ReschedulablePods(ctx, p.kubeClient)
if err != nil {
return false
}

evictablePods := lo.Filter(pods, func(p *corev1.Pod, _ int) bool { return podutil.IsEvictable(p) })
return len(evictablePods) == len(pods)
}

expirationTime, err := time.Parse(time.RFC3339, expirationTimeString)
if err != nil {
return false
}

return time.Until(expirationTime) <= AvgProvisionTimeSeconds*time.Second
})

// Get pods from nodes that are preparing for deletion
// We do this after getting the pending pods so that we undershoot if pods are
// actively migrating from a node that is being deleted
// NOTE: The assumption is that these nodes are cordoned and no additional pods will schedule to them
deletingNodePods, err := nodes.Deleting().ReschedulablePods(ctx, p.kubeClient)
deletingNodePods, err := nodesMarkedForDeletion.ReschedulablePods(ctx, p.kubeClient)
if err != nil {
return scheduler.Results{}, err
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/controllers/provisioning/scheduling/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3625,6 +3625,8 @@ var _ = Context("Scheduling", func() {

// Mark for deletion so that we consider all pods on this node for reschedulability
cluster.MarkForDeletion(node.Spec.ProviderID)
err := env.Client.Delete(ctx, node)
Expect(err).To(BeNil())

// Trigger an eviction to set the deletion timestamp but not delete the pod
ExpectEvicted(ctx, env.Client, pod)
Expand Down
Loading