Skip to content
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

Configurable HorizontalPodAutoscaler #74525

Merged
merged 5 commits into from
Dec 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/api-rules/violation_exceptions.list
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta1,HorizontalP
API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta1,HorizontalPodAutoscalerSpec,Metrics
API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta1,HorizontalPodAutoscalerStatus,Conditions
API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta1,HorizontalPodAutoscalerStatus,CurrentMetrics
API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta2,HPAScalingRules,Policies
API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta2,HorizontalPodAutoscalerList,Items
API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta2,HorizontalPodAutoscalerSpec,Metrics
API rule violation: list_type_missing,k8s.io/api/autoscaling/v2beta2,HorizontalPodAutoscalerStatus,Conditions
Expand Down
65 changes: 65 additions & 0 deletions api/openapi-spec/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/api/testing/defaulting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ func TestDefaulting(t *testing.T) {
{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v2beta1", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "autoscaling", Version: "v2beta2", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v2beta2", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "batch", Version: "v1", Kind: "Job"}: {},
{Group: "batch", Version: "v1", Kind: "JobList"}: {},
{Group: "batch", Version: "v1beta1", Kind: "CronJob"}: {},
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/autoscaling/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ const HorizontalPodAutoscalerConditionsAnnotation = "autoscaling.alpha.kubernete
// metrics are present. This is here because it's used by both the v2beta1 defaulting
// logic, and the pseudo-defaulting done in v1 conversion.
const DefaultCPUUtilization = 80

// BehaviorSpecsAnnotation is the annotation which holds the HPA constraints specs
// when converting the `Behavior` field from autoscaling/v2beta2
const BehaviorSpecsAnnotation = "autoscaling.alpha.kubernetes.io/behavior"
37 changes: 37 additions & 0 deletions pkg/apis/autoscaling/fuzzer/fuzzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,43 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
},
},
}
stabilizationWindow := int32(c.RandUint64())
maxPolicy := autoscaling.MaxPolicySelect
minPolicy := autoscaling.MinPolicySelect
s.Behavior = &autoscaling.HorizontalPodAutoscalerBehavior{
ScaleUp: &autoscaling.HPAScalingRules{
StabilizationWindowSeconds: &stabilizationWindow,
SelectPolicy: &maxPolicy,
Policies: []autoscaling.HPAScalingPolicy{
{
Type: autoscaling.PodsScalingPolicy,
Value: int32(c.RandUint64()),
PeriodSeconds: int32(c.RandUint64()),
},
{
Type: autoscaling.PercentScalingPolicy,
Value: int32(c.RandUint64()),
PeriodSeconds: int32(c.RandUint64()),
},
},
},
ScaleDown: &autoscaling.HPAScalingRules{
StabilizationWindowSeconds: &stabilizationWindow,
SelectPolicy: &minPolicy,
Policies: []autoscaling.HPAScalingPolicy{
{
Type: autoscaling.PodsScalingPolicy,
Value: int32(c.RandUint64()),
PeriodSeconds: int32(c.RandUint64()),
},
{
Type: autoscaling.PercentScalingPolicy,
Value: int32(c.RandUint64()),
PeriodSeconds: int32(c.RandUint64()),
},
},
},
}
},
func(s *autoscaling.HorizontalPodAutoscalerStatus, c fuzz.Continue) {
c.FuzzNoCustom(s) // fuzz self without calling this function again
Expand Down
84 changes: 84 additions & 0 deletions pkg/apis/autoscaling/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,90 @@ type HorizontalPodAutoscalerSpec struct {
// more information about how each type of metric must respond.
// +optional
Metrics []MetricSpec

// behavior configures the scaling behavior of the target
// in both Up and Down directions (scaleUp and scaleDown fields respectively).
// If not set, the default HPAScalingRules for scale up and scale down are used.
// +optional
Behavior *HorizontalPodAutoscalerBehavior
gliush marked this conversation as resolved.
Show resolved Hide resolved
}

// HorizontalPodAutoscalerBehavior configures a scaling behavior for Up and Down direction
// (scaleUp and scaleDown fields respectively).
type HorizontalPodAutoscalerBehavior struct {
// scaleUp is scaling policy for scaling Up.
// If not set, the default value is the higher of:
thockin marked this conversation as resolved.
Show resolved Hide resolved
// * increase no more than 4 pods per 60 seconds
// * double the number of pods per 60 seconds
// No stabilization is used.
// +optional
ScaleUp *HPAScalingRules
// scaleDown is scaling policy for scaling Down.
// If not set, the default value is to allow to scale down to minReplicas pods, with a
// 300 second stabilization window (i.e., the highest recommendation for
// the last 300sec is used).
// +optional
ScaleDown *HPAScalingRules
}

// ScalingPolicySelect is used to specify which policy should be used while scaling in a certain direction
type ScalingPolicySelect string

const (
// MaxPolicySelect selects the policy with the highest possible change.
MaxPolicySelect ScalingPolicySelect = "Max"
// MinPolicySelect selects the policy with the lowest possible change.
MinPolicySelect ScalingPolicySelect = "Min"
// DisabledPolicySelect disables the scaling in this direction.
DisabledPolicySelect ScalingPolicySelect = "Disabled"
)

// HPAScalingRules configures the scaling behavior for one direction.
// These Rules are applied after calculating DesiredReplicas from metrics for the HPA.
// They can limit the scaling velocity by specifying scaling policies.
// They can prevent flapping by specifying the stabilization window, so that the
// number of replicas is not set instantly, instead, the safest value from the stabilization
gliush marked this conversation as resolved.
Show resolved Hide resolved
// window is chosen.
type HPAScalingRules struct {
// StabilizationWindowSeconds is the number of seconds for which past recommendations should be
// considered while scaling up or scaling down.
// StabilizationWindowSeconds must be greater than or equal to zero and less than or equal to 3600 (one hour).
// If not set, use the default values:
// - For scale up: 0 (i.e. no stabilization is done).
// - For scale down: 300 (i.e. the stabilization window is 300 seconds long).
// +optional
StabilizationWindowSeconds *int32
// selectPolicy is used to specify which policy should be used.
// If not set, the default value MaxPolicySelect is used.
// +optional
SelectPolicy *ScalingPolicySelect
gliush marked this conversation as resolved.
Show resolved Hide resolved
// policies is a list of potential scaling polices which can used during scaling.
// At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid
// +optional
Policies []HPAScalingPolicy
}

// HPAScalingPolicyType is the type of the policy which could be used while making scaling decisions.
type HPAScalingPolicyType string

const (
// PodsScalingPolicy is a policy used to specify a change in absolute number of pods.
PodsScalingPolicy HPAScalingPolicyType = "Pods"
// PercentScalingPolicy is a policy used to specify a relative amount of change with respect to
// the current number of pods.
PercentScalingPolicy HPAScalingPolicyType = "Percent"
)

// HPAScalingPolicy is a single policy which must hold true for a specified past interval.
type HPAScalingPolicy struct {
// Type is used to specify the scaling policy.
Type HPAScalingPolicyType
// Value contains the amount of change which is permitted by the policy.
thockin marked this conversation as resolved.
Show resolved Hide resolved
// It must be greater than zero
Value int32
// PeriodSeconds specifies the window of time for which the policy should hold true.
// PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min).
PeriodSeconds int32
}

// MetricSourceType indicates the type of metric.
Expand Down
21 changes: 19 additions & 2 deletions pkg/apis/autoscaling/v1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,9 @@ func Convert_autoscaling_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(i
}
}

if len(otherMetrics) > 0 || len(in.Status.CurrentMetrics) > 0 || len(currentConditions) > 0 {
if len(otherMetrics) > 0 || len(in.Status.CurrentMetrics) > 0 || len(currentConditions) > 0 || in.Spec.Behavior != nil {
old := out.Annotations
out.Annotations = make(map[string]string, len(old)+3)
out.Annotations = make(map[string]string, len(old)+4)
for k, v := range old {
out.Annotations[k] = v
}
Expand All @@ -313,6 +313,14 @@ func Convert_autoscaling_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler(i
out.Annotations[autoscaling.MetricStatusesAnnotation] = string(currentMetricsEnc)
}

if in.Spec.Behavior != nil {
behaviorEnc, err := json.Marshal(in.Spec.Behavior)
if err != nil {
return err
}
out.Annotations[autoscaling.BehaviorSpecsAnnotation] = string(behaviorEnc)
}

if len(in.Status.Conditions) > 0 {
currentConditionsEnc, err := json.Marshal(currentConditions)
if err != nil {
Expand Down Expand Up @@ -349,6 +357,15 @@ func Convert_v1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(i
delete(out.Annotations, autoscaling.MetricSpecsAnnotation)
}

if behaviorEnc, hasConstraints := out.Annotations[autoscaling.BehaviorSpecsAnnotation]; hasConstraints {
gliush marked this conversation as resolved.
Show resolved Hide resolved
var behavior autoscaling.HorizontalPodAutoscalerBehavior
if err := json.Unmarshal([]byte(behaviorEnc), &behavior); err != nil {
return err
}
out.Spec.Behavior = &behavior
delete(out.Annotations, autoscaling.BehaviorSpecsAnnotation)
}

if currentMetricsEnc, hasCurrentMetrics := out.Annotations[autoscaling.MetricStatusesAnnotation]; hasCurrentMetrics {
// ignore any existing status values -- the ones here have more information
var currentMetrics []autoscalingv1.MetricStatus
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/autoscaling/v1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions pkg/apis/autoscaling/v2beta1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package v2beta1

import (
"encoding/json"

autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1"

v1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -257,3 +259,45 @@ func Convert_v2beta1_PodsMetricStatus_To_autoscaling_PodsMetricStatus(in *autosc
}
return nil
}

func Convert_autoscaling_HorizontalPodAutoscaler_To_v2beta1_HorizontalPodAutoscaler(in *autoscaling.HorizontalPodAutoscaler, out *autoscalingv2beta1.HorizontalPodAutoscaler, s conversion.Scope) error {
if err := autoConvert_autoscaling_HorizontalPodAutoscaler_To_v2beta1_HorizontalPodAutoscaler(in, out, s); err != nil {
return err
}
if in.Spec.Behavior != nil {
old := out.Annotations
gliush marked this conversation as resolved.
Show resolved Hide resolved
out.Annotations = make(map[string]string, len(old)+1)
for k, v := range old {
out.Annotations[k] = v
}

behaviorEnc, err := json.Marshal(in.Spec.Behavior)
if err != nil {
return err
}
Comment on lines +268 to +277
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire block of code is redundant. There are other places where similar code is used but it really adds nothing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arjunrn : what block do you mean? lines 275-277?

// Even if the annotation for behavior exists, we will just overwrite it
out.Annotations[autoscaling.BehaviorSpecsAnnotation] = string(behaviorEnc)
gliush marked this conversation as resolved.
Show resolved Hide resolved
}

return nil
}

func Convert_v2beta1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(in *autoscalingv2beta1.HorizontalPodAutoscaler, out *autoscaling.HorizontalPodAutoscaler, s conversion.Scope) error {
if err := autoConvert_v2beta1_HorizontalPodAutoscaler_To_autoscaling_HorizontalPodAutoscaler(in, out, s); err != nil {
return err
}

if behaviorEnc, hasBehaviors := out.Annotations[autoscaling.BehaviorSpecsAnnotation]; hasBehaviors {
var behavior autoscaling.HorizontalPodAutoscalerBehavior
if err := json.Unmarshal([]byte(behaviorEnc), &behavior); err != nil {
return err
}
out.Spec.Behavior = &behavior
delete(out.Annotations, autoscaling.BehaviorSpecsAnnotation)
}
return nil
}

func Convert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec(in *autoscaling.HorizontalPodAutoscalerSpec, out *autoscalingv2beta1.HorizontalPodAutoscalerSpec, s conversion.Scope) error {
return autoConvert_autoscaling_HorizontalPodAutoscalerSpec_To_v2beta1_HorizontalPodAutoscalerSpec(in, out, s)
}
Loading