Promote LogarithmicScaleDown to beta

This promotes the LogarithmicScaleDown feature gate to Beta, enabling it
by default. It also introduces a new metric, `sorting_deletion_age_ratio`,
intended to measure the efficacy of this new replica set scaledown behavior.
This commit is contained in:
Mike Dame 2021-06-28 14:25:19 -04:00
parent a445eb2b7f
commit 4b9230ed27
3 changed files with 73 additions and 2 deletions

View File

@ -0,0 +1,44 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package metrics
import (
"k8s.io/component-base/metrics"
)
const ReplicaSetControllerSubsystem = "replicaset_controller"
var SortingDeletionAgeRatio = metrics.NewHistogram(
&metrics.HistogramOpts{
Subsystem: ReplicaSetControllerSubsystem,
Name: "sorting_deletion_age_ratio",
Help: "The ratio of chosen deleted pod's ages to the current youngest pod's age (at the time). Should be <2." +
"The intent of this metric is to measure the rough efficacy of the LogarithmicScaleDown feature gate's effect on" +
"the sorting (and deletion) of pods when a replicaset scales down. This only considers Ready pods when calculating and reporting.",
Buckets: metrics.ExponentialBuckets(0.25, 2, 6),
StabilityLevel: metrics.ALPHA,
},
)
// Register registers ReplicaSet controller metrics.
func Register(registrationFunc func(metrics.Registerable) error) error {
err := registrationFunc(SortingDeletionAgeRatio)
if err != nil {
return err
}
return nil
}

View File

@ -55,10 +55,12 @@ import (
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"k8s.io/component-base/metrics/legacyregistry"
"k8s.io/component-base/metrics/prometheus/ratelimiter"
"k8s.io/klog/v2"
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/controller/replicaset/metrics"
"k8s.io/utils/integer"
)
@ -112,6 +114,9 @@ func NewReplicaSetController(rsInformer appsinformers.ReplicaSetInformer, podInf
eventBroadcaster := record.NewBroadcaster()
eventBroadcaster.StartStructuredLogging(0)
eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: kubeClient.CoreV1().Events("")})
if err := metrics.Register(legacyregistry.Register); err != nil {
klog.ErrorS(err, "unable to register metrics")
}
return NewBaseController(rsInformer, podInformer, kubeClient, burstReplicas,
apps.SchemeGroupVersion.WithKind("ReplicaSet"),
"replicaset_controller",
@ -802,10 +807,31 @@ func getPodsToDelete(filteredPods, relatedPods []*v1.Pod, diff int) []*v1.Pod {
if diff < len(filteredPods) {
podsWithRanks := getPodsRankedByRelatedPodsOnSameNode(filteredPods, relatedPods)
sort.Sort(podsWithRanks)
reportSortingDeletionAgeRatioMetric(filteredPods, diff)
}
return filteredPods[:diff]
}
func reportSortingDeletionAgeRatioMetric(filteredPods []*v1.Pod, diff int) {
now := time.Now()
youngestTime := time.Time{}
// first we need to check all of the ready pods to get the youngest, as they may not necessarily be sorted by timestamp alone
for _, pod := range filteredPods {
if pod.CreationTimestamp.Time.After(youngestTime) && podutil.IsPodReady(pod) {
youngestTime = pod.CreationTimestamp.Time
}
}
// for each pod chosen for deletion, report the ratio of its age to the youngest pod's age
for _, pod := range filteredPods[:diff] {
if !podutil.IsPodReady(pod) {
continue
}
ratio := float64(now.Sub(pod.CreationTimestamp.Time).Milliseconds() / now.Sub(youngestTime).Milliseconds())
metrics.SortingDeletionAgeRatio.Observe(ratio)
}
}
// getPodsRankedByRelatedPodsOnSameNode returns an ActivePodsWithRanks value
// that wraps podsToRank and assigns each pod a rank equal to the number of
// active pods in relatedPods that are colocated on the same node with the pod.

View File

@ -658,7 +658,8 @@ const (
ServiceLoadBalancerClass featuregate.Feature = "ServiceLoadBalancerClass"
// owner: @damemi
// aplpha: v1.21
// alpha: v1.21
// beta: v1.22
//
// Enables scaling down replicas via logarithmic comparison of creation/ready timestamps
LogarithmicScaleDown featuregate.Feature = "LogarithmicScaleDown"
@ -844,9 +845,9 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
TopologyAwareHints: {Default: false, PreRelease: featuregate.Alpha},
PodAffinityNamespaceSelector: {Default: true, PreRelease: featuregate.Beta},
ServiceLoadBalancerClass: {Default: true, PreRelease: featuregate.Beta},
LogarithmicScaleDown: {Default: false, PreRelease: featuregate.Alpha},
IngressClassNamespacedParams: {Default: true, PreRelease: featuregate.Beta},
ServiceInternalTrafficPolicy: {Default: true, PreRelease: featuregate.Beta},
LogarithmicScaleDown: {Default: true, PreRelease: featuregate.Beta},
SuspendJob: {Default: true, PreRelease: featuregate.Beta},
KubeletPodResourcesGetAllocatable: {Default: false, PreRelease: featuregate.Alpha},
NamespaceDefaultLabelName: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.24