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
Change break condition and thresholds validation for lowUtilization #285
Change break condition and thresholds validation for lowUtilization #285
Conversation
@lixiang233: Cannot trigger testing until a trusted user reviews the PR and leaves an In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for noticing insufficiency in the stopping conditions. I agree the conditions need to be stronger. Which on the other hand requires all resources to be set. Also, defaulting cpu/memory to 100 allows a user to execute "I want to balance all nodes wrt. memory" case and yet have confidence cpu resource will be still balanced properly. Which, in the future can be extended with arbitrary set of resources to balance wrt some threshold.
@@ -249,7 +258,8 @@ func evictPods( | |||
taintsOfLowNodes map[string][]v1.Taint, | |||
podEvictor *evictions.PodEvictor, | |||
node *v1.Node) { | |||
if IsNodeAboveTargetUtilization(nodeUsage, targetThresholds) && (*totalPods > 0 || *totalCPU > 0 || *totalMem > 0) { | |||
// stop if node utilization drops below target threshold or any of required capacity (cpu, memory, pods) is moved | |||
if IsNodeAboveTargetUtilization(nodeUsage, targetThresholds) && *totalPods > 0 && *totalCPU > 0 && *totalMem > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Valid change: if at least one of the essential resources is zero, no pod can be rescheduled to a different node
// check if node utilization drops below target threshold or required capacity (cpu, memory, pods) is moved | ||
if !IsNodeAboveTargetUtilization(nodeUsage, targetThresholds) || (*totalPods <= 0 && *totalCPU <= 0 && *totalMem <= 0) { | ||
// check if node utilization drops below target threshold or any required capacity (cpu, memory, pods) is moved | ||
if !IsNodeAboveTargetUtilization(nodeUsage, targetThresholds) || *totalPods <= 0 || *totalCPU <= 0 || *totalMem <= 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Valid change: the same condition, just negated.
@@ -190,12 +190,21 @@ func evictPodsFromTargetNodes( | |||
if _, ok := targetThresholds[v1.ResourceCPU]; ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to default both CPU and Memory target thresholds to 100% and fail, if either of thresholds is not set. Rather then checking it for every node. Can you move the change into LowNodeUtilization
right after if !validateTargetThresholds(targetThresholds)
condition and set the missing target thresholds there?
@@ -141,6 +144,64 @@ func TestLowNodeUtilization(t *testing.T) { | |||
}, | |||
expectedPodsEvicted: 3, | |||
}, | |||
{ | |||
name: "without priorities stop if any required capacity (cpu, memory, pods) is moved", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
without priorities stop when cpu capacity is depleted
@lixiang233 can you also squash both commits into one? |
/ok-to-test |
@lixiang233 you will also need to call |
1d61242
to
92fc350
Compare
92fc350
to
6c34ad2
Compare
done. @ingvagabund |
README.md
Outdated
@@ -91,7 +91,8 @@ Currently, pods request resource requirements are considered for computing node | |||
There is another configurable threshold, `targetThresholds`, that is used to compute those potential nodes | |||
from where pods could be evicted. Any node, between the thresholds, `thresholds` and `targetThresholds` is | |||
considered appropriately utilized and is not considered for eviction. The threshold, `targetThresholds`, | |||
can be configured for cpu, memory, and number of pods too in terms of percentage. | |||
can be configured for cpu, memory, and number of pods too in terms of percentage. Notice that `pods` must be | |||
configured and if `cpu` or `memory` not configured, they'll be set to default value `100`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/not configured/are not configured/
@@ -59,6 +59,13 @@ func LowNodeUtilization(ctx context.Context, client clientset.Interface, strateg | |||
if !validateTargetThresholds(targetThresholds) { | |||
return | |||
} | |||
// check if CPU/Mem not set in targetThresholds and set it to 100 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check if CPU/Mem not set in targetThresholds and set it to 100
check if CPU/Mem are set in targetThresholds, if not, set them to 100
cpuPercentage := targetThresholds[v1.ResourceCPU] - node.usage[v1.ResourceCPU] | ||
totalCPU += ((float64(cpuPercentage) * float64(nodeCapacity.Cpu().MilliValue())) / 100) | ||
} | ||
// CPU and Mem in targetThresholds have already validated in LowNodeUtilization |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for the comment as it's assumed both targetThresholds are already set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just nits, otherwise lgtm
6c34ad2
to
564f628
Compare
Changed, thanks for your help @ingvagabund |
README.md
Outdated
can be configured for cpu, memory, and number of pods too in terms of percentage. Notice that `pods` must be | ||
configured and if `cpu` or `memory` are not configured, they'll be set to default value `100`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is pods
required now, and we should add some validation to ensure that it's set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only added default value to cpu
and mem
and didn't change the validate func .Current validateTargetThresholds
func only checks wheather pods
is configured, if not, LowNodeUtilization
will stop, so only pods
is required. Should we treatpods
as same as cpu
and mem
, and validateTargetThresholds
func returns true if any of these resources configured?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can use validateThresholds
to check targetThresholds
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like you pushed a change to set a default for pods
, is that correct? If so please remember to update this readme note
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I'll update it in later commit
What about the latest commit? I unified the verification method for |
@@ -117,11 +125,20 @@ func validateThresholds(thresholds api.ResourceThresholds) bool { | |||
for name := range thresholds { | |||
switch name { | |||
case v1.ResourceCPU: | |||
continue | |||
if !validateResourcePercentage(thresholds[v1.ResourceCPU]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you refactor the function to return fmt.Errorf("cpu threshold not valid")
instead of bool? Logging Info/Error and return bool is not practical. Instead, caller of validateThresholds
can decide what to do about non-nil error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -56,15 +61,18 @@ func LowNodeUtilization(ctx context.Context, client clientset.Interface, strateg | |||
return | |||
} | |||
targetThresholds := strategy.Params.NodeResourceUtilizationThresholds.TargetThresholds | |||
if !validateTargetThresholds(targetThresholds) { | |||
if !validateThresholds(targetThresholds) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better but not sufficient. thresholds
has to be less than targetThresholds
per a resource type. @lixiang233 Can you add check for that as well?
Also, thresholds
and targetThresholds
have gave me a lot of headache before learning what they actually mean. I would prefer to rename both of them into lowThreshold
and highThreshold
at least on the code level. Also given the strategy is configured as:
"LowNodeUtilization":
enabled: true
params:
nodeResourceUtilizationThresholds:
thresholds:
"cpu" : 20
"memory": 20
"pods": 20
targetThresholds:
"cpu" : 50
"memory": 50
"pods": 50
we can drop thresholds
and targetThresholds
and have:
"LowNodeUtilization":
enabled: true
params:
nodeResourceUtilizationThresholds:
low:
"cpu" : 20
"memory": 20
"pods": 20
high:
"cpu" : 50
"memory": 50
"pods": 50
@damemi @seanmalloy anything against?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we allow thresholds
and targetThresholds
configuring different resources? For instance, thresholds
configured cpu
and mem
while targetThresholds
only configured pods
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Assuming low threshold has a cpu set but high does not, you mark a node as underutilized wrt. cpu. You then find over-utilized node(s) wrt. memory. In the process of moving workload from over-utilized (since you want to decrease memory consumption), you move some pods to the underutilized nodes with a hope there's at least some memory left on those nodes. So, in practice this scenario is possible though you are removing any guarantee the list of underutilized nodes is selected effectively. I.e. the nodes have sufficient memory left. Since there's no low threshold defaulting for memory, you might consume all the memory left and end up with nodes which are memory over utilized. Also, selecting under-utilized nodes based on cpu becomes irrelevant since you then may pick any subset of nodes since you still have no guarantee there's sufficient memory left.
So, the answer is no.
klog.V(1).Infof("no target resource threshold for pods is configured") | ||
return false | ||
// validateResourcePercentage checks if resource percentage is in the range [0, 100] | ||
func validateResourcePercentage(percent api.Percentage) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think validateResourcePercentage
is the right function to unit test given its length and complexity. Can you move the distinct cases into TestValidateThresholds and drop TestValidateResourcePercentage
completely?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -453,7 +543,7 @@ func TestValidateThresholds(t *testing.T) { | |||
} | |||
|
|||
for _, test := range tests { | |||
isValid := validateThresholds(test.input) | |||
isValid := (validateThresholds(test.input) == nil) | |||
|
|||
if isValid != test.succeed { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please replace succeed bool
with error err
and set it to nil or a specific error instead given validateThresholds
now returns err
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. I'll commit later.
One more question, after I added new validation check, |
Updated README and validation checks for the two thresholds @damemi @ingvagabund |
@lixiang233 that's the last nit I found. Sorry for the review taking so long. Thanks for asking the right questions!!! |
e86db6d
to
11663c2
Compare
Simplified |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some documentation nits. Otherwise, lgtm.
README.md
Outdated
@@ -89,7 +89,8 @@ usage is below threshold for all (cpu, memory, and number of pods), the node is | |||
Currently, pods request resource requirements are considered for computing node resource utilization. | |||
|
|||
There is another configurable threshold, `targetThresholds`, that is used to compute those potential nodes | |||
from where pods could be evicted. Any node, between the thresholds, `thresholds` and `targetThresholds` is | |||
from where pods could be evicted. If a node's usage is above targetThreshold for any (cpu, memory, or number of pods), | |||
the node is considered over utilized. Any node, between the thresholds, `thresholds` and `targetThresholds` is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any node between the thresholds thresholds
and targetThresholds
is ... (no need for the comas)
README.md
Outdated
@@ -114,6 +115,15 @@ strategies: | |||
"pods": 50 | |||
``` | |||
|
|||
Policy should pass the following validation checks: | |||
* Only three types of resource are supported: `cpu`, `memory` and `pods`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/resource/resources
README.md
Outdated
* The valid range of the resource's percentage value is \[0, 100\] | ||
* Percentage value of `thresholds` can not be greater than `targetThresholds` for the same resource. | ||
|
||
You can configure any number of these three resources, if one resource is not configured, it will be set to default |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If any of the resource types is not specified, all its thresholds default to 100% to avoid nodes going from underutilized to overutilized
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these modified
1.Set default CPU/Mem/Pods percentage of thresholds to 100 2.Stop evicting pods if any resource ran out 3.Add thresholds verification method and limit resource percentage within [0, 100] 4.Change testcases and readme
11663c2
to
2a8dc69
Compare
/lgtm |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/approve
Thanks for the fix! I think this is one of the more confusing strategies, so I appreciate the help fixing bugs like this.
// check if Pods/CPU/Mem are set, if not, set them to 100 | ||
if _, ok := thresholds[v1.ResourcePods]; !ok { | ||
thresholds[v1.ResourcePods] = MaxResourcePercentage | ||
targetThresholds[v1.ResourcePods] = MaxResourcePercentage | ||
} | ||
if _, ok := thresholds[v1.ResourceCPU]; !ok { | ||
thresholds[v1.ResourceCPU] = MaxResourcePercentage | ||
targetThresholds[v1.ResourceCPU] = MaxResourcePercentage | ||
} | ||
if _, ok := thresholds[v1.ResourceMemory]; !ok { | ||
thresholds[v1.ResourceMemory] = MaxResourcePercentage | ||
targetThresholds[v1.ResourceMemory] = MaxResourcePercentage | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I feel like this section could probably be moved to validateStrategyConfig
but I won't block on it
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: damemi, lixiang233 The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
/kind bug |
kubernetes-sigs#285 got merged before re-running the unit tests.
Based on https://github.com/kubernetes/community/blob/master/community-membership.md#requirements-1: The following apply to the part of codebase for which one would be a reviewer in an OWNERS file (for repos using the bot). > member for at least 3 months For a couple of years now > Primary reviewer for at least 5 PRs to the codebase kubernetes-sigs#285 kubernetes-sigs#275 kubernetes-sigs#267 kubernetes-sigs#254 kubernetes-sigs#181 > Reviewed or merged at least 20 substantial PRs to the codebase https://github.com/kubernetes-sigs/descheduler/pulls?q=is%3Apr+is%3Aclosed+assignee%3Aingvagabund > Knowledgeable about the codebase yes > Sponsored by a subproject approver > With no objections from other approvers > Done through PR to update the OWNERS file this PR > May either self-nominate, be nominated by an approver in this subproject, or be nominated by a robot self-nominating
…tilization_break Change break condition and thresholds validation for lowUtilization
kubernetes-sigs#285 got merged before re-running the unit tests.
Based on https://github.com/kubernetes/community/blob/master/community-membership.md#requirements-1: The following apply to the part of codebase for which one would be a reviewer in an OWNERS file (for repos using the bot). > member for at least 3 months For a couple of years now > Primary reviewer for at least 5 PRs to the codebase kubernetes-sigs#285 kubernetes-sigs#275 kubernetes-sigs#267 kubernetes-sigs#254 kubernetes-sigs#181 > Reviewed or merged at least 20 substantial PRs to the codebase https://github.com/kubernetes-sigs/descheduler/pulls?q=is%3Apr+is%3Aclosed+assignee%3Aingvagabund > Knowledgeable about the codebase yes > Sponsored by a subproject approver > With no objections from other approvers > Done through PR to update the OWNERS file this PR > May either self-nominate, be nominated by an approver in this subproject, or be nominated by a robot self-nominating
We should stop evicting pods when any of totalCPU/totalMem/totalPods ran out, otherwise we may make underutilized nodes overutilized which is not efficient. To do this, there should be a value of totalCPU/Mem if their targetThreshold is not configured, here I set it to all of the remaining resources
on these nodes.