Skip to content

Commit

Permalink
Rewrite addon-resizer nanny
Browse files Browse the repository at this point in the history
There will be a single estimator class, providing two ranges: acceptable
and recommended range. As long as current pod requirements and limits fall into
acceptable range, nothing happens. Once either limits or requirements
fall out of acceptable range, they are both updated to lower (when
upscaling) or higher (when downscaling) end of recommended range. This
approach prevents flapping, which took place in previous implementation,
when cluster size oscillated around certain values.

Also, fix the code to actually use poll-period flag.
  • Loading branch information
x13n committed Jun 20, 2017
1 parent 7b3a75c commit a4440c3
Show file tree
Hide file tree
Showing 6 changed files with 473 additions and 255 deletions.
2 changes: 1 addition & 1 deletion addon-resizer/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
OUT_DIR = build
PACKAGE = k8s.io/contrib/addon-resizer
PREFIX = gcr.io/google_containers
TAG = 1.7
TAG = 2.0

# Rules for building the real image for deployment to gcr.io

Expand Down
65 changes: 36 additions & 29 deletions addon-resizer/nanny/estimator.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@ package nanny

import (
"fmt"
"math"

api "k8s.io/kubernetes/pkg/api/v1"

"k8s.io/kubernetes/pkg/api/resource"
)

const (
eps = float64(0.01)
log "github.com/golang/glog"
)

// Resource defines the name of a resource, the quantity, and the marginal value.
Expand All @@ -34,34 +32,45 @@ type Resource struct {
Name api.ResourceName
}

// LinearEstimator estimates the amount of resources as r = base + extra*nodes.
type LinearEstimator struct {
Resources []Resource
// ResourceListPair is a pair of ResourceLists, denoting a range.
type ResourceListPair struct {
lower, upper api.ResourceList
}

func (e LinearEstimator) scaleWithNodes(numNodes uint64) *api.ResourceRequirements {
return calculateResources(numNodes, e.Resources)
// EstimatorResult is the result of the resource Estimation, used by Estimator struct.
type EstimatorResult struct {
RecommendedRange, AcceptableRange ResourceListPair
}

// ExponentialEstimator estimates the amount of resources in the way that
// prevents from frequent updates but may end up with larger resource usage
// than actually needed (though no more than ScaleFactor).
type ExponentialEstimator struct {
Resources []Resource
ScaleFactor float64
// Estimator is a struct used for estimating accepted and recommended resource requirements.
type Estimator struct {
Resources []Resource
AcceptanceOffset int64
RecommendationOffset int64
}

func (e ExponentialEstimator) scaleWithNodes(numNodes uint64) *api.ResourceRequirements {
n := uint64(16)
for n < numNodes {
n = uint64(float64(n)*e.ScaleFactor + eps)
func decWithPercentageOffset(value uint64, offset int64, rounder func(float64) float64) uint64 {
return uint64(int64(value) + int64(rounder(float64(offset)*float64(value)/100)))
}

func nodesAndOffsetToRange(numNodes uint64, offset int64, res []Resource) ResourceListPair {
numNodesMin := decWithPercentageOffset(numNodes, -offset, math.Floor)
numNodesMax := decWithPercentageOffset(numNodes, offset, math.Ceil)
return ResourceListPair{
lower: calculateResources(numNodesMin, res),
upper: calculateResources(numNodesMax, res),
}
return calculateResources(n, e.Resources)
}

func calculateResources(numNodes uint64, resources []Resource) *api.ResourceRequirements {
limits := make(api.ResourceList)
requests := make(api.ResourceList)
func (e Estimator) scaleWithNodes(numNodes uint64) *EstimatorResult {
return &EstimatorResult{
RecommendedRange: nodesAndOffsetToRange(numNodes, e.RecommendationOffset, e.Resources),
AcceptableRange: nodesAndOffsetToRange(numNodes, e.AcceptanceOffset, e.Resources),
}
}

func calculateResources(numNodes uint64, resources []Resource) api.ResourceList {
resourceList := make(api.ResourceList)
for _, r := range resources {
// Since we want to enable passing values smaller than e.g. 1 millicore per node,
// we need to have some more hacky solution here than operating on MilliValues.
Expand All @@ -73,11 +82,9 @@ func calculateResources(numNodes uint64, resources []Resource) *api.ResourceRequ
newRes := r.Base
newRes.Add(overhead)

limits[r.Name] = newRes
requests[r.Name] = newRes
}
return &api.ResourceRequirements{
Limits: limits,
Requests: requests,
log.V(4).Infof("New requirement for resource %s with %d nodes is %s", r.Name, numNodes, newRes.String())

resourceList[r.Name] = newRes
}
return resourceList
}
Loading

0 comments on commit a4440c3

Please sign in to comment.