Introduce a metric measuring heterogeneity of binpacking

Binpacking estimator has optimizations to allow homogeneous workloads to
be efficiently processed together. Measuring heterogeneity is required
to gain insight into cases when these optimizations matter and when they
don't.
This commit is contained in:
Daniel Kłobuszewski 2025-05-06 20:51:55 +02:00
parent 8bc75e5b58
commit 99121ebf42
2 changed files with 47 additions and 0 deletions

View File

@ -18,9 +18,11 @@ package estimator
import (
"fmt"
"strconv"
apiv1 "k8s.io/api/core/v1"
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
"k8s.io/autoscaler/cluster-autoscaler/metrics"
core_utils "k8s.io/autoscaler/cluster-autoscaler/simulator"
"k8s.io/autoscaler/cluster-autoscaler/simulator/clustersnapshot"
"k8s.io/autoscaler/cluster-autoscaler/simulator/framework"
@ -93,6 +95,7 @@ func (e *BinpackingNodeEstimator) Estimate(
nodeTemplate *framework.NodeInfo,
nodeGroup cloudprovider.NodeGroup,
) (int, []*apiv1.Pod) {
observeBinpackingHeterogeneity(podsEquivalenceGroups, nodeTemplate)
e.limiter.StartEstimation(podsEquivalenceGroups, nodeGroup, e.context)
defer e.limiter.EndEstimation()
@ -231,3 +234,31 @@ func (e *BinpackingNodeEstimator) addNewNodeToSnapshot(
estimationState.newNodeNames[estimationState.lastNodeName] = true
return nil
}
func observeBinpackingHeterogeneity(podsEquivalenceGroups []PodEquivalenceGroup, nodeTemplate *framework.NodeInfo) {
node := nodeTemplate.Node()
var instanceType, cpuCount string
if node != nil {
if node.Labels != nil {
instanceType = node.Labels[apiv1.LabelInstanceTypeStable]
}
cpuCount = node.Status.Capacity.Cpu().String()
}
namespaces := make(map[string]bool)
for _, peg := range podsEquivalenceGroups {
e := peg.Exemplar()
if e != nil {
namespaces[e.Namespace] = true
}
}
// Quantize # of namespaces to limit metric cardinality.
nsCountBucket := ""
if len(namespaces) <= 5 {
nsCountBucket = strconv.Itoa(len(namespaces))
} else if len(namespaces) <= 10 {
nsCountBucket = "6-10"
} else {
nsCountBucket = "11+"
}
metrics.ObserveBinpackingHeterogeneity(instanceType, cpuCount, nsCountBucket, len(podsEquivalenceGroups))
}

View File

@ -418,6 +418,15 @@ var (
Help: "Number of migs where instance count according to InstanceGroupManagers.List() differs from the results of Instances.List(). This can happen when some instances are abandoned or a user edits instance 'created-by' metadata.",
},
)
binpackingHeterogeneity = k8smetrics.NewHistogramVec(
&k8smetrics.HistogramOpts{
Namespace: caNamespace,
Name: "binpacking_heterogeneity",
Help: "Number of groups of equivalent pods being processed as a part of the same binpacking simulation.",
Buckets: []float64{1, 2, 4, 6, 10},
}, []string{"instance_type", "cpu_count", "namespace_count"},
)
)
// RegisterAll registers all metrics.
@ -453,6 +462,7 @@ func RegisterAll(emitPerNodeGroupMetrics bool) {
legacyregistry.MustRegister(pendingNodeDeletions)
legacyregistry.MustRegister(nodeTaintsCount)
legacyregistry.MustRegister(inconsistentInstancesMigsCount)
legacyregistry.MustRegister(binpackingHeterogeneity)
if emitPerNodeGroupMetrics {
legacyregistry.MustRegister(nodesGroupMinNodes)
@ -736,3 +746,9 @@ func ObserveNodeTaintsCount(taintType string, count float64) {
func UpdateInconsistentInstancesMigsCount(migCount int) {
inconsistentInstancesMigsCount.Set(float64(migCount))
}
// ObserveBinpackingHeterogeneity records the number of pod equivalence groups
// considered in a single binpacking estimation.
func ObserveBinpackingHeterogeneity(instanceType, cpuCount, namespaceCount string, pegCount int) {
binpackingHeterogeneity.WithLabelValues(instanceType, cpuCount, namespaceCount).Observe(float64(pegCount))
}