Skip to content

Commit 6bc4264

Browse files
feat: refactoring thresholds and usage assessment
this commit refactors the thresholds and usage assessment for the node utilization plugins. both high and low plugins are affected by this change.
1 parent a4d6119 commit 6bc4264

File tree

8 files changed

+863
-160
lines changed

8 files changed

+863
-160
lines changed

pkg/api/types.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@ limitations under the License.
1717
package api
1818

1919
import (
20+
"math"
21+
2022
v1 "k8s.io/api/core/v1"
2123
"k8s.io/apimachinery/pkg/api/resource"
2224
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2325
"k8s.io/apimachinery/pkg/runtime"
26+
"k8s.io/utils/ptr"
2427
)
2528

2629
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -75,6 +78,25 @@ type (
7578
ResourceThresholds map[v1.ResourceName]Percentage
7679
)
7780

81+
// Negative returns a new ResourceThresholds with all its values negated.
82+
func (r ResourceThresholds) Negative() ResourceThresholds {
83+
reversed := make(ResourceThresholds, len(r))
84+
for k, v := range r {
85+
reversed[k] = -v
86+
}
87+
return reversed
88+
}
89+
90+
// Round returns a new ResourceThresholds with all its values rounded according
91+
// to math.Round.
92+
func (r ResourceThresholds) Round() ResourceThresholds {
93+
rounded := make(ResourceThresholds, len(r))
94+
for k, v := range r {
95+
rounded[k] = Percentage(math.Round(float64(v)))
96+
}
97+
return rounded
98+
}
99+
78100
type PriorityThreshold struct {
79101
Value *int32 `json:"value"`
80102
Name string `json:"name"`
@@ -114,3 +136,31 @@ type MetricsCollector struct {
114136

115137
// ReferencedResourceList is an adaption of v1.ResourceList with resources as references
116138
type ReferencedResourceList = map[v1.ResourceName]*resource.Quantity
139+
140+
// ReferencedResourceListForNodesCapacity returns a ReferencedResourceList for
141+
// the capacity of a list of nodes. If allocatable resources are present, they
142+
// are used instead of capacity.
143+
func ReferencedResourceListForNodesCapacity(node []*v1.Node) map[string]ReferencedResourceList {
144+
capacities := map[string]ReferencedResourceList{}
145+
for _, n := range node {
146+
capacities[n.Name] = ReferencedResourceListForNodeCapacity(n)
147+
}
148+
return capacities
149+
}
150+
151+
// ReferencedResourceListForNodeCapacity returns a ReferencedResourceList for
152+
// the capacity of a node. If allocatable resources are present, they are used
153+
// instead of capacity.
154+
func ReferencedResourceListForNodeCapacity(node *v1.Node) ReferencedResourceList {
155+
capacity := node.Status.Capacity
156+
if len(node.Status.Allocatable) > 0 {
157+
capacity = node.Status.Allocatable
158+
}
159+
160+
referenced := ReferencedResourceList{}
161+
for name, quantity := range capacity {
162+
referenced[name] = ptr.To(quantity)
163+
}
164+
165+
return referenced
166+
}

pkg/api/types_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package api
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestResourceThresholdsRound(t *testing.T) {
9+
limit := ResourceThresholds{
10+
"cpu": 100 / 3,
11+
"mem": 100 / 3,
12+
}
13+
expected := ResourceThresholds{
14+
"cpu": 33,
15+
"mem": 33,
16+
}
17+
if !reflect.DeepEqual(limit, expected) {
18+
t.Errorf("Expected %v, got %v", expected, limit)
19+
}
20+
}

pkg/framework/plugins/nodeutilization/highnodeutilization.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,18 @@ func (h *HighNodeUtilization) Balance(ctx context.Context, nodes []*v1.Node) *fr
102102
}
103103

104104
nodesMap, nodesUsageMap, podListMap := getNodeUsageSnapshot(nodes, h.usageClient)
105-
nodeThresholdsMap := getStaticNodeThresholds(nodes, h.args.Thresholds, h.targetThresholds)
106-
nodesUsageAsNodeThresholdsMap := nodeUsageToResourceThresholds(nodesUsageMap, nodesMap)
105+
106+
usage, thresholds := AssessNodesUsagesAndThresholds(
107+
nodesUsageMap,
108+
api.ReferencedResourceListForNodesCapacity(nodes),
109+
h.args.Thresholds,
110+
h.targetThresholds,
111+
false,
112+
)
113+
107114
nodeGroups := classifyNodeUsage(
108-
nodesUsageAsNodeThresholdsMap,
109-
nodeThresholdsMap,
115+
usage,
116+
thresholds,
110117
[]classifierFnc{
111118
// underutilized nodes
112119
func(nodeName string, usage, threshold api.ResourceThresholds) bool {
@@ -128,16 +135,21 @@ func (h *HighNodeUtilization) Balance(ctx context.Context, nodes []*v1.Node) *fr
128135
category := []string{"underutilized", "overutilized"}
129136
for i := range nodeGroups {
130137
for nodeName := range nodeGroups[i] {
131-
klog.InfoS("Node is "+category[i], "node", klog.KObj(nodesMap[nodeName]), "usage", nodesUsageMap[nodeName], "usagePercentage", resourceUsagePercentages(nodesUsageMap[nodeName], nodesMap[nodeName], true))
138+
klog.InfoS(
139+
fmt.Sprintf("Node is %s", category[i]),
140+
"node", klog.KObj(nodesMap[nodeName]),
141+
"usage", nodesUsageMap[nodeName],
142+
"usagePercentage", usage[nodeName].Round(),
143+
)
132144
nodeInfos[i] = append(nodeInfos[i], NodeInfo{
133145
NodeUsage: NodeUsage{
134146
node: nodesMap[nodeName],
135147
usage: nodesUsageMap[nodeName], // get back the original node usage
136148
allPods: podListMap[nodeName],
137149
},
138150
thresholds: NodeThresholds{
139-
lowResourceThreshold: resourceThresholdsToNodeUsage(nodeThresholdsMap[nodeName][0], nodesMap[nodeName]),
140-
highResourceThreshold: resourceThresholdsToNodeUsage(nodeThresholdsMap[nodeName][1], nodesMap[nodeName]),
151+
lowResourceThreshold: resourceThresholdsToNodeUsage(thresholds[nodeName][0], nodesMap[nodeName]),
152+
highResourceThreshold: resourceThresholdsToNodeUsage(thresholds[nodeName][1], nodesMap[nodeName]),
141153
},
142154
})
143155
}

pkg/framework/plugins/nodeutilization/lownodeutilization.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,18 @@ func (l *LowNodeUtilization) Balance(ctx context.Context, nodes []*v1.Node) *fra
123123
}
124124

125125
nodesMap, nodesUsageMap, podListMap := getNodeUsageSnapshot(nodes, l.usageClient)
126-
var nodeThresholdsMap map[string][]api.ResourceThresholds
127-
if l.args.UseDeviationThresholds {
128-
nodeThresholdsMap = getNodeThresholdsFromAverageNodeUsage(nodes, l.usageClient, l.args.Thresholds, l.args.TargetThresholds)
129-
} else {
130-
nodeThresholdsMap = getStaticNodeThresholds(nodes, l.args.Thresholds, l.args.TargetThresholds)
131-
}
132-
nodesUsageAsNodeThresholdsMap := nodeUsageToResourceThresholds(nodesUsageMap, nodesMap)
126+
127+
usage, thresholds := AssessNodesUsagesAndThresholds(
128+
nodesUsageMap,
129+
api.ReferencedResourceListForNodesCapacity(nodes),
130+
l.args.Thresholds,
131+
l.args.TargetThresholds,
132+
l.args.UseDeviationThresholds,
133+
)
133134

134135
nodeGroups := classifyNodeUsage(
135-
nodesUsageAsNodeThresholdsMap,
136-
nodeThresholdsMap,
136+
usage,
137+
thresholds,
137138
[]classifierFnc{
138139
// underutilization
139140
func(nodeName string, usage, threshold api.ResourceThresholds) bool {
@@ -156,7 +157,12 @@ func (l *LowNodeUtilization) Balance(ctx context.Context, nodes []*v1.Node) *fra
156157
listedNodes := map[string]struct{}{}
157158
for i := range nodeGroups {
158159
for nodeName := range nodeGroups[i] {
159-
klog.InfoS("Node is "+category[i], "node", klog.KObj(nodesMap[nodeName]), "usage", nodesUsageMap[nodeName], "usagePercentage", resourceUsagePercentages(nodesUsageMap[nodeName], nodesMap[nodeName], true))
160+
klog.InfoS(
161+
fmt.Sprintf("Node is %x", category[i]),
162+
"node", klog.KObj(nodesMap[nodeName]),
163+
"usage", nodesUsageMap[nodeName],
164+
"usagePercentage", usage[nodeName].Round(),
165+
)
160166
listedNodes[nodeName] = struct{}{}
161167
nodeInfos[i] = append(nodeInfos[i], NodeInfo{
162168
NodeUsage: NodeUsage{
@@ -165,15 +171,20 @@ func (l *LowNodeUtilization) Balance(ctx context.Context, nodes []*v1.Node) *fra
165171
allPods: podListMap[nodeName],
166172
},
167173
thresholds: NodeThresholds{
168-
lowResourceThreshold: resourceThresholdsToNodeUsage(nodeThresholdsMap[nodeName][0], nodesMap[nodeName]),
169-
highResourceThreshold: resourceThresholdsToNodeUsage(nodeThresholdsMap[nodeName][1], nodesMap[nodeName]),
174+
lowResourceThreshold: resourceThresholdsToNodeUsage(thresholds[nodeName][0], nodesMap[nodeName]),
175+
highResourceThreshold: resourceThresholdsToNodeUsage(thresholds[nodeName][1], nodesMap[nodeName]),
170176
},
171177
})
172178
}
173179
}
174180
for nodeName := range nodesMap {
175181
if _, ok := listedNodes[nodeName]; !ok {
176-
klog.InfoS("Node is appropriately utilized", "node", klog.KObj(nodesMap[nodeName]), "usage", nodesUsageMap[nodeName], "usagePercentage", resourceUsagePercentages(nodesUsageMap[nodeName], nodesMap[nodeName], true))
182+
klog.InfoS(
183+
"Node is appropriately utilized",
184+
"node", klog.KObj(nodesMap[nodeName]),
185+
"usage", nodesUsageMap[nodeName],
186+
"usagePercentage", usage[nodeName].Round(),
187+
)
177188
}
178189
}
179190

0 commit comments

Comments
 (0)