From 434823bcb362700165b49715b22cbf75eb112ca6 Mon Sep 17 00:00:00 2001 From: Yusuke Kuoka Date: Tue, 16 Feb 2021 17:16:26 +0900 Subject: [PATCH] `scale{Up,Down}Adjustment` to add/remove constant number of replicas on scaling (#315) * `scale{Up,Down}Adjustment` to add/remove constant number of replicas on scaling Ref #305 * Bump chart version --- .../horizontalrunnerautoscaler_types.go | 10 ++++++ charts/actions-runner-controller/Chart.yaml | 2 +- ...rwind.dev_horizontalrunnerautoscalers.yaml | 9 +++++ ...rwind.dev_horizontalrunnerautoscalers.yaml | 9 +++++ controllers/autoscaling.go | 36 ++++++++++++++++--- 5 files changed, 61 insertions(+), 5 deletions(-) diff --git a/api/v1alpha1/horizontalrunnerautoscaler_types.go b/api/v1alpha1/horizontalrunnerautoscaler_types.go index a19eacd788..659f773bb3 100644 --- a/api/v1alpha1/horizontalrunnerautoscaler_types.go +++ b/api/v1alpha1/horizontalrunnerautoscaler_types.go @@ -126,6 +126,16 @@ type MetricSpec struct { // to determine how many pods should be removed. // +optional ScaleDownFactor string `json:"scaleDownFactor,omitempty"` + + // ScaleUpAdjustment is the number of runners added on scale-up. + // You can only specify either ScaleUpFactor or ScaleUpAdjustment. + // +optional + ScaleUpAdjustment int `json:"scaleUpAdjustment,omitempty"` + + // ScaleDownAdjustment is the number of runners removed on scale-down. + // You can only specify either ScaleDownFactor or ScaleDownAdjustment. + // +optional + ScaleDownAdjustment int `json:"scaleDownAdjustment,omitempty"` } type HorizontalRunnerAutoscalerStatus struct { diff --git a/charts/actions-runner-controller/Chart.yaml b/charts/actions-runner-controller/Chart.yaml index fe880c2130..60a41dd1ea 100644 --- a/charts/actions-runner-controller/Chart.yaml +++ b/charts/actions-runner-controller/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.4.0 +version: 0.5.0 home: https://github.com/summerwind/actions-runner-controller diff --git a/charts/actions-runner-controller/crds/actions.summerwind.dev_horizontalrunnerautoscalers.yaml b/charts/actions-runner-controller/crds/actions.summerwind.dev_horizontalrunnerautoscalers.yaml index d3669c9852..891a79fce0 100644 --- a/charts/actions-runner-controller/crds/actions.summerwind.dev_horizontalrunnerautoscalers.yaml +++ b/charts/actions-runner-controller/crds/actions.summerwind.dev_horizontalrunnerautoscalers.yaml @@ -78,6 +78,11 @@ spec: items: type: string type: array + scaleDownAdjustment: + description: ScaleDownAdjustment is the number of runners removed + on scale-down. You can only specify either ScaleDownFactor or + ScaleDownAdjustment. + type: integer scaleDownFactor: description: ScaleDownFactor is the multiplicative factor applied to the current number of runners used to determine how many @@ -87,6 +92,10 @@ spec: description: ScaleDownThreshold is the percentage of busy runners less than which will trigger the hpa to scale the runners down. type: string + scaleUpAdjustment: + description: ScaleUpAdjustment is the number of runners added + on scale-up. You can only specify either ScaleUpFactor or ScaleUpAdjustment. + type: integer scaleUpFactor: description: ScaleUpFactor is the multiplicative factor applied to the current number of runners used to determine how many diff --git a/config/crd/bases/actions.summerwind.dev_horizontalrunnerautoscalers.yaml b/config/crd/bases/actions.summerwind.dev_horizontalrunnerautoscalers.yaml index d3669c9852..891a79fce0 100644 --- a/config/crd/bases/actions.summerwind.dev_horizontalrunnerautoscalers.yaml +++ b/config/crd/bases/actions.summerwind.dev_horizontalrunnerautoscalers.yaml @@ -78,6 +78,11 @@ spec: items: type: string type: array + scaleDownAdjustment: + description: ScaleDownAdjustment is the number of runners removed + on scale-down. You can only specify either ScaleDownFactor or + ScaleDownAdjustment. + type: integer scaleDownFactor: description: ScaleDownFactor is the multiplicative factor applied to the current number of runners used to determine how many @@ -87,6 +92,10 @@ spec: description: ScaleDownThreshold is the percentage of busy runners less than which will trigger the hpa to scale the runners down. type: string + scaleUpAdjustment: + description: ScaleUpAdjustment is the number of runners added + on scale-up. You can only specify either ScaleUpFactor or ScaleUpAdjustment. + type: integer scaleUpFactor: description: ScaleUpFactor is the multiplicative factor applied to the current number of runners used to determine how many diff --git a/controllers/autoscaling.go b/controllers/autoscaling.go index 61cf887752..a5fbb3ceb0 100644 --- a/controllers/autoscaling.go +++ b/controllers/autoscaling.go @@ -222,14 +222,34 @@ func (r *HorizontalRunnerAutoscalerReconciler) calculateReplicasByPercentageRunn scaleDownThreshold = sdt } - if metrics.ScaleUpFactor != "" { + + scaleUpAdjustment := metrics.ScaleUpAdjustment + if scaleUpAdjustment != 0 { + if metrics.ScaleUpAdjustment < 0 { + return nil, errors.New("validating autoscaling metrics: spec.autoscaling.metrics[].scaleUpAdjustment cannot be lower than 0") + } + + if metrics.ScaleUpFactor != "" { + return nil, errors.New("validating autoscaling metrics: spec.autoscaling.metrics[]: scaleUpAdjustment and scaleUpFactor cannot be specified together") + } + } else if metrics.ScaleUpFactor != "" { suf, err := strconv.ParseFloat(metrics.ScaleUpFactor, 64) if err != nil { return nil, errors.New("validating autoscaling metrics: spec.autoscaling.metrics[].scaleUpFactor cannot be parsed into a float64") } scaleUpFactor = suf } - if metrics.ScaleDownFactor != "" { + + scaleDownAdjustment := metrics.ScaleDownAdjustment + if scaleDownAdjustment != 0 { + if metrics.ScaleDownAdjustment < 0 { + return nil, errors.New("validating autoscaling metrics: spec.autoscaling.metrics[].scaleDownAdjustment cannot be lower than 0") + } + + if metrics.ScaleDownFactor != "" { + return nil, errors.New("validating autoscaling metrics: spec.autoscaling.metrics[]: scaleDownAdjustment and scaleDownFactor cannot be specified together") + } + } else if metrics.ScaleDownFactor != "" { sdf, err := strconv.ParseFloat(metrics.ScaleDownFactor, 64) if err != nil { return nil, errors.New("validating autoscaling metrics: spec.autoscaling.metrics[].scaleDownFactor cannot be parsed into a float64") @@ -273,9 +293,17 @@ func (r *HorizontalRunnerAutoscalerReconciler) calculateReplicasByPercentageRunn var desiredReplicas int fractionBusy := float64(numRunnersBusy) / float64(numRunners) if fractionBusy >= scaleUpThreshold { - desiredReplicas = int(math.Ceil(float64(numRunners) * scaleUpFactor)) + if scaleUpAdjustment > 0 { + desiredReplicas = numRunners + scaleUpAdjustment + } else { + desiredReplicas = int(math.Ceil(float64(numRunners) * scaleUpFactor)) + } } else if fractionBusy < scaleDownThreshold { - desiredReplicas = int(float64(numRunners) * scaleDownFactor) + if scaleDownAdjustment > 0 { + desiredReplicas = numRunners - scaleDownAdjustment + } else { + desiredReplicas = int(float64(numRunners) * scaleDownFactor) + } } else { desiredReplicas = *rd.Spec.Replicas }