Skip to content

Commit

Permalink
Add Scaling behaviours (#80)
Browse files Browse the repository at this point in the history
  • Loading branch information
jthomperoo committed Jan 15, 2023
1 parent a377608 commit 2612922
Show file tree
Hide file tree
Showing 19 changed files with 2,111 additions and 316 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Changed
- See the [migration guide from `v0.11.2`
here](https://predictive-horizontal-pod-autoscaler.readthedocs.io/en/latest/user-guide/migration/v0_11_2-to-v0_12_0).
- **BREAKING CHANGE** PHPA spec upgraded from `autoscaling/v2beta2` to `autoscaling/v2` for the following definitions:
- `CrossVersionObjectReference` in the `scaleTargetRef` field.
- `MetricSpec` in the `metrics` field.
- `MetricStatus` in the `currentMetrics` field.
- Upgraded to [k8shorizmetrics `v2.0.0`](https://github.com/jthomperoo/k8shorizmetrics/releases/tag/v2.0.0).
- Upgraded from `autoscaling/v2beta2` to `autoscaling/v2`.
- Upgraded to Go `v1.19`.
### Removed
- **BREAKING CHANGE** Removed `downscaleStabilization`, replaced with `behavior`, `scaleDown`,
`stabilizationWindowSeconds`.
### Added
- Support for [HPA scaling
behaviors](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior).

## [v0.11.2] - 2022-11-25
### Fixed
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ format:
go mod tidy
find algorithms -name '*.py' -print0 | xargs -0 yapf -i

test: generate
@echo "=============Running tests============="
test: gotest pytest

gotest:
go test ./... -cover -coverprofile unit_cover.out

pytest:
pytest algorithms/ --cov-report term --cov-report=xml:algorithm_coverage.out --cov-report=html:.algorithm_coverage --cov=algorithms/

docker:
Expand Down
34 changes: 24 additions & 10 deletions api/v1alpha1/predictivehorizontalpodautoscaler_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,14 +216,11 @@ type PredictiveHorizontalPodAutoscalerSpec struct {
// +optional
Metrics []autoscalingv2.MetricSpec `json:"metrics"`

// downscaleStabilization defines in seconds the length of the downscale stabilization window; based on the
// Horizontal Pod Autoscaler downscale stabilization. Downscale stabilization works by recording all evaluations
// over the window specified and picking out the maximum target replicas from these evaluations. This results in a
// more smoothed downscaling and a cooldown, which can reduce the effect of thrashing.
// Default value 300 seconds (5 minutes).
// +kubebuilder:validation:Minimum=0
// 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
DownscaleStabilization *int `json:"downscaleStabilization"`
Behavior *autoscalingv2.HorizontalPodAutoscalerBehavior `json:"behavior,omitempty"`

// cpuInitializationPeriod is equivalent to --horizontal-pod-autoscaler-cpu-initialization-period; the period after
// pod start when CPU samples might be skipped.
Expand Down Expand Up @@ -274,10 +271,27 @@ type PredictiveHorizontalPodAutoscalerStatus struct {
// +optional
LastScaleTime *metav1.Time `json:"lastScaleTime,omitempty"`

// replicaHistory is a timestamped history of all the calculated replica values, used for calculating downscale
// stabilization.
// scaleUpReplicaHistory is a list of timestamped replicas within the scale up stabilization window.
// Used for calculating upscale stabilization.
// +optional
ScaleUpReplicaHistory []TimestampedReplicas `json:"scaleUpReplicaHistory"`

// scaleDownReplicaHistory is a list of timestamped replicas within the scale down stabilization window.
// Used for calculating downscale stabilization.
// +optional
ScaleDownReplicaHistory []TimestampedReplicas `json:"scaleDownReplicaHistory"`

// scaleUpEventHistory is a list of timestamped changes in replicas for every time a scale up event occurs for
// this resource. A value of 5 means that at that scale event the resource was scaled up by 5 replicas.
// Used for applying scale up policies.
// +optional
ScaleUpEventHistory []TimestampedReplicas `json:"scaleUpEventHistory"`

// scaleDownEventHistory is a list of timestamped changes in replicas for every time a scale down event occurs for
// this resource. A value of 5 means that at that scale event the resource was scaled down by 5 replicas.
// Used for applying scale down policies.
// +optional
ReplicaHistory []TimestampedReplicas `json:"replicaHistory"`
ScaleDownEventHistory []TimestampedReplicas `json:"scaleDownEventHistory"`

// reference is the resource being referenced and targeted for scaling.
Reference string `json:"reference"`
Expand Down
33 changes: 27 additions & 6 deletions api/v1alpha1/zz_generated.deepcopy.go

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

26 changes: 10 additions & 16 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,6 @@ Default value: `15000` (15 seconds).

Set in milliseconds.

## downscaleStabilization

```yaml
downscaleStabilization: 150
```

Equivalent to `--horizontal-pod-autoscaler-downscale-stabilization`; the length of the downscale stabilization window.
Downscale stabilization works by recording all evaluations over the window specified and picking out the maximum target
replicas from these evaluations. This results in a more smoothed downscaling and a cooldown, which can reduce the
effect of thrashing.

Set in seconds.

Default value: `300` (5 minutes).

## cpuInitializationPeriod

```yaml
Expand Down Expand Up @@ -104,11 +89,20 @@ Possible values:

- **maximum** - pick the highest evaluation of the models.
- **minimum** - pick the lowest evaluation of the models.
- **mean** - calculate the mean number of replicas between the models.
- **mean** - calculate the mean number of replicas (rounded to nearest integer) between the models.
- **median** - calculate the median number of replicas between the models.

Default value: `maximum`.

## behavior

Scaling behavior to apply.

Intended to be feature equivalent to Kubernetes HPA behavior.

See the [Horizontal Pod Autoscaler docs
here](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior).

## models

List of statistical models to apply.
Expand Down
8 changes: 7 additions & 1 deletion docs/user-guide/metrics.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Metrics

Deciding which metrics to use is done by using `MetricSpecs`, which are a key part of HPAs, and look like this:

```yaml
- type: Resource
resource:
Expand All @@ -12,8 +13,9 @@ Deciding which metrics to use is done by using `MetricSpecs`, which are a key pa

To send these specs to the Predictive HPA, add a config option called `metrics` to the PHPA, with a multiline string
containing the metric list. For example:

```yaml
netrics:
metrics:
- type: Resource
resource:
name: cpu
Expand All @@ -26,3 +28,7 @@ This allows porting over existing Kubernetes HPA metric configurations to the Pr
Equivalent to K8s HPA metric specs; which are [demonstrated in this HPA
walkthrough](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics).
Can hold multiple values as it is an array.

See the [Horizontal Pod Autoscaler
documentation](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) for a full list of supported
metrics (the Predictive Horizontal Pod Autoscaler intends to be functionally equivalent).
67 changes: 67 additions & 0 deletions docs/user-guide/migration/v0_11_2-to-v0_12_0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Migration from v0.11.2 to v0.12.0

There are some minor breaking changes moving from Predictive Horizontal Pod Autoscaler `v0.11.2` to `v0.12.0` - this
page will detail how you can migrate between versions and handle these breaking changes.

This is split into two parts, for most users of the PHPA the only part that will apply is [Migrating Predictive
Horizontal Pod Autoscaler definitions](#migrating-predictive-horizontal-pod-autoscaler-definitions), the section
on [Go code dependencies](#migrating-go-code-dependencies) only applies if you have a Go project that directly depends
on the Go modules exposed in this project.

## Migrating Predictive Horizontal Pod Autoscaler definitions

The Predictive Horizontal Pod Autoscaler no longer has the `downscaleStabilization` field, this has been replaced by
the [`behavior` field](../../reference/configuration.md#behavior).

To apply downscale stabilization you can now use this `behavior` field.

For example:

```yaml
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
downscaleStabilization: 0
```

Would instead be:

```yaml
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
behavior:
scaleDown:
stabilizationWindowSeconds: 0
```

## Migrating Go code dependencies

The Go code no longer uses the `autoscaling/v2beta2` types, instead using the `autoscaling/v2`. This affects these
Go types:

- `CrossVersionObjectReference` in the `scaleTargetRef` field.
- `MetricSpec` in the `metrics` field.
- `MetricStatus` in the `currentMetrics` field.

To migrate these switch from importing `autoscaling/v2beta2` to importing `autoscaling/v2`.

For example:

```go
import autoscalingv2 "k8s.io/api/autoscaling/v2beta2"
```

Would instead be:

```go
import autoscalingv2 "k8s.io/api/autoscaling/v2"
```
78 changes: 60 additions & 18 deletions examples/dynamic-holt-winters/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,60 @@ operator](https://predictive-horizontal-pod-autoscaler.readthedocs.io/en/latest/
This example was based on the [Horizontal Pod Autoscaler
Walkthrough](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/).

1. Use `kubectl apply -f deployment.yaml` to spin up the app/deployment to manage, called `php-apache`.
2. Build the tuning image `docker build -t tuning tuning` and import it into your Kubernetes cluster
(`k3d image import tuning`).
4. Deploy the tuning service `kubectl apply -f tuning/tuning.yaml`.
5. Use `kubectl apply -f phpa.yaml` to start the autoscaler, pointing at the previously created deployment.
6. Build the load tester image `docker build -t load-tester load` and import it into your Kubernetes cluster
(`k3d image import load-tester`).
7. Deploy the load tester, note the time as it will run for 30 seconds every minute `kubectl apply -f load/load.yaml`.
8. Use `kubectl logs -l name=predictive-horizontal-pod-autoscaler -f` to see the autoscaler working and the log output
it produces.
9. Use `kubectl logs -l run=tuning -f` to see the logs of the tuning service, it will report any time it is queried and
it will print the value provided to it.
10. Use
`kubectl get configmap predictive-horizontal-pod-autoscaler-dynamic-holt-winters-data -o=json | jq -r '.data.data | fromjson | .modelHistories["dynamic-holt-winters"].replicaHistory[] | .time,.replicas'`
to see the replica history for the autoscaler stored in a configmap and tracked by the autoscaler.
1. Run this command to spin up the app/deployment to manage, called `php-apache`:

```bash
kubectl apply -f deployment.yaml
```

2. Run this command to build the tuning image and import it into your Kubernetes cluster:

```bash
docker build -t tuning tuning && k3d image import tuning
```

3. Run this command to deploy the tuning service:

```bash
kubectl apply -f tuning/tuning.yaml
```

3. Run this command to start the autoscaler, pointing at the previously created deployment:

```bash
kubectl apply -f phpa.yaml
```

4. Run this command to build the load tester image and import it into your Kubernetes cluster:

```bash
docker build -t load-tester load && k3d image import load-tester
```

5. Run this command to deploy the load tester, note the time as it will run for 30 seconds every minute:

```bash
kubectl apply -f load/load.yaml
```

6. Run this command to see the autoscaler working and the log output it produces:

```bash
kubectl logs -l name=predictive-horizontal-pod-autoscaler -f
```

7. Run this command to see the logs of the tuning service, it will report any time it is queried and it will print the
value provided to it:

```bash
kubectl logs -l run=tuning -f
```

8. Run this command to see the replica history for the autoscaler stored in a configmap and tracked by the autoscaler:

```bash
kubectl get configmap predictive-horizontal-pod-autoscaler-dynamic-holt-winters-data -o=json | jq -r '.data.data | fromjson | .modelHistories["HoltWintersPrediction"].replicaHistory[] | .time,.replicas'
```

Every minute the load tester will increase the load on the application we are autoscaling for 30 seconds. The PHPA will
initially without any data just act like a Horizontal Pod Autoscaler and will reactively scale up to meet this demand
Expand Down Expand Up @@ -99,7 +138,9 @@ spec:
minReplicas: 1
maxReplicas: 10
syncPeriod: 20000
downscaleStabilization: 30
behavior:
scaleDown:
stabilizationWindowSeconds: 30
metrics:
- type: Resource
resource:
Expand Down Expand Up @@ -131,8 +172,9 @@ spec:
between.
- `syncPeriod` is how frequently this autoscaler will run in milliseconds, so this autoscaler will run every 20000
milliseconds (20 seconds).
- `downscaleStabilization` handles how quickly an autoscaler can scale down, ensuring that it will pick the highest evaluation that has occurred within the last time period described, by default it will pick the highest evaluation over
the past 5 minutes. In this case it will pick the highest evaluation over the past 30 seconds.
- `behavior.scaleDown.stabilizationWindowSeconds` handles how quickly an autoscaler can scale down, ensuring that it
will pick the highest evaluation that has occurred within the last time period described, by default it will pick the
highest evaluation over the past 5 minutes. In this case it will pick the highest evaluation over the past 30 seconds.
- `metrics` defines the metrics that the PHPA should use to scale with, in this example it will try to keep average
CPU utilization at 50% per pod.
- `models` - predictive models to apply.
Expand Down
4 changes: 3 additions & 1 deletion examples/dynamic-holt-winters/phpa.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ spec:
minReplicas: 1
maxReplicas: 10
syncPeriod: 20000
downscaleStabilization: 30
behavior:
scaleDown:
stabilizationWindowSeconds: 30
metrics:
- type: Resource
resource:
Expand Down
2 changes: 1 addition & 1 deletion examples/dynamic-holt-winters/tuning/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Flask==1.0
Flask==2.2.2

0 comments on commit 2612922

Please sign in to comment.