From 86796b999f3c64c683b33809735a1fa99cc8eccb Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Mon, 7 Apr 2025 17:02:08 +0000 Subject: [PATCH 01/33] feat(recommender): add OOMMinBumpUp&OOMBumpUpRatio to CRD Signed-off-by: Omer Aplatony --- .../deploy/vpa-v1-crd-gen.yaml | 14 +- vertical-pod-autoscaler/docs/api.md | 2 + vertical-pod-autoscaler/docs/flags.md | 4 +- .../e2e/v1/admission_controller.go | 163 +++++++++++++++--- .../pkg/apis/autoscaling.k8s.io/v1/types.go | 10 ++ .../pkg/recommender/main.go | 4 +- .../model/aggregate_container_state.go | 32 ++++ .../pkg/recommender/model/container.go | 16 +- 8 files changed, 216 insertions(+), 29 deletions(-) diff --git a/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml b/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml index 70345adccc..29b7121965 100644 --- a/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml +++ b/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml @@ -4,7 +4,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes/kubernetes/pull/63797 - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.2 name: verticalpodautoscalercheckpoints.autoscaling.k8s.io spec: group: autoscaling.k8s.io @@ -225,7 +225,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes/kubernetes/pull/63797 - controller-gen.kubebuilder.io/version: v0.16.5 + controller-gen.kubebuilder.io/version: v0.17.2 name: verticalpodautoscalers.autoscaling.k8s.io spec: group: autoscaling.k8s.io @@ -372,6 +372,16 @@ spec: - Auto - "Off" type: string + oomBumpUpRatio: + description: OOMBumpUpRatio is the ratio to increase resources + when OOM is detected. + minimum: 1 + type: number + oomMinBumpUp: + description: OOMMinBumpUp is the minimum increase in resources + when OOM is detected. + minimum: 0 + type: number type: object type: array type: object diff --git a/vertical-pod-autoscaler/docs/api.md b/vertical-pod-autoscaler/docs/api.md index 53863e326d..7c4445ae30 100644 --- a/vertical-pod-autoscaler/docs/api.md +++ b/vertical-pod-autoscaler/docs/api.md @@ -48,6 +48,8 @@ _Appears in:_ | `maxAllowed` _[ResourceList](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#resourcelist-v1-core)_ | Specifies the maximum amount of resources that will be recommended
for the container. The default is no maximum. | | | | `controlledResources` _[ResourceName](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#resourcename-v1-core)_ | Specifies the type of recommendations that will be computed
(and possibly applied) by VPA.
If not specified, the default of [ResourceCPU, ResourceMemory] will be used. | | | | `controlledValues` _[ContainerControlledValues](#containercontrolledvalues)_ | Specifies which resource values should be controlled.
The default is "RequestsAndLimits". | | Enum: [RequestsAndLimits RequestsOnly]
| +| `oomBumpUpRatio` _float_ | OOMBumpUpRatio is the ratio to increase resources when OOM is detected. | | Minimum: 1
| +| `oomMinBumpUp` _float_ | OOMMinBumpUp is the minimum increase in resources when OOM is detected. | | Minimum: 0
| #### ContainerScalingMode diff --git a/vertical-pod-autoscaler/docs/flags.md b/vertical-pod-autoscaler/docs/flags.md index 586b61c278..70663f1345 100644 --- a/vertical-pod-autoscaler/docs/flags.md +++ b/vertical-pod-autoscaler/docs/flags.md @@ -93,8 +93,8 @@ This document is auto-generated from the flag definitions in the VPA recommender | `--metric-for-pod-labels` | "up{job=\"kubernetes-pods\"}" | Which metric to look for pod labels in metrics | | `--min-checkpoints` | 10 | Minimum number of checkpoints to write per recommender's main loop | | `--one-output` | | If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true) | -| `--oom-bump-up-ratio` | 1.2 | The memory bump up ratio when OOM occurred, default is 1.2. | -| `--oom-min-bump-up-bytes` | 1.048576e+08 | The minimal increase of memory when OOM occurred in bytes, default is 100 * 1024 * 1024 | +| `--oom-bump-up-ratio` | 1.2 | Default memory bump up ratio when OOM occurs. This value applies to all VPAs unless overridden in the VPA spec. Default is 1.2. | +| `--oom-min-bump-up-bytes` | 1.048576e+08 | Default minimal increase of memory (in bytes) when OOM occurs. This value applies to all VPAs unless overridden in the VPA spec. Default is 100 * 1024 * 1024 (100Mi). | | `--password` | | The password used in the prometheus server basic auth | | `--pod-label-prefix` | "pod_label_" | Which prefix to look for pod labels in metrics | | `--pod-name-label` | "kubernetes_pod_name" | Label name to look for pod names | diff --git a/vertical-pod-autoscaler/e2e/v1/admission_controller.go b/vertical-pod-autoscaler/e2e/v1/admission_controller.go index e3d526b3f0..acdfed5c64 100644 --- a/vertical-pod-autoscaler/e2e/v1/admission_controller.go +++ b/vertical-pod-autoscaler/e2e/v1/admission_controller.go @@ -831,26 +831,149 @@ var _ = AdmissionControllerE2eDescribe("Admission-controller", func() { err := InstallRawVPA(f, validVPA) gomega.Expect(err).NotTo(gomega.HaveOccurred(), "Valid VPA object rejected") - ginkgo.By("Setting up invalid VPA object") - // The invalid object differs by name and minAllowed - there is an invalid "requests" field. - invalidVPA := []byte(`{ - "kind": "VerticalPodAutoscaler", - "apiVersion": "autoscaling.k8s.io/v1", - "metadata": {"name": "hamster-vpa-invalid"}, - "spec": { - "targetRef": { - "apiVersion": "apps/v1", - "kind": "Deployment", - "name":"hamster" - }, - "resourcePolicy": { - "containerPolicies": [{"containerName": "*", "minAllowed":{"requests":{"cpu":"50m"}}}] - } - } - }`) - err2 := InstallRawVPA(f, invalidVPA) - gomega.Expect(err2).To(gomega.HaveOccurred(), "Invalid VPA object accepted") - gomega.Expect(err2.Error()).To(gomega.MatchRegexp(`.*admission webhook .*vpa.* denied the request: .*`)) + ginkgo.By("Setting up invalid VPA objects") + testCases := []struct { + name string + vpaJSON string + expectedErr string + }{ + { + name: "Invalid oomBumpUpRatio (negative value)", + vpaJSON: `{ + "apiVersion": "autoscaling.k8s.io/v1", + "kind": "VerticalPodAutoscaler", + "metadata": {"name": "oom-test-vpa"}, + "spec": { + "targetRef": { + "apiVersion": "apps/v1", + "kind": "Deployment", + "name": "oom-test" + }, + "updatePolicy": { + "updateMode": "Auto" + }, + "resourcePolicy": { + "containerPolicies": [{ + "containerName": "*", + "oomBumpUpRatio": -1, + "oomMinBumpUp": 104857600 + }] + } + } + }`, + expectedErr: "spec.resourcePolicy.containerPolicies[0].oomBumpUpRatio: Invalid value: -1: spec.resourcePolicy.containerPolicies[0].oomBumpUpRatio in body should be greater than or equal to 1", + }, + { + name: "Invalid oomBumpUpRatio (string value)", + vpaJSON: `{ + "apiVersion": "autoscaling.k8s.io/v1", + "kind": "VerticalPodAutoscaler", + "metadata": {"name": "oom-test-vpa"}, + "spec": { + "targetRef": { + "apiVersion": "apps/v1", + "kind": "Deployment", + "name": "oom-test" + }, + "updatePolicy": { + "updateMode": "Auto" + }, + "resourcePolicy": { + "containerPolicies": [{ + "containerName": "*", + "oomBumpUpRatio": "12", + "oomMinBumpUp": 104857600 + }] + } + } + }`, + expectedErr: "json: cannot unmarshal string into Go struct field ContainerResourcePolicy.spec.resourcePolicy.containerPolicies.oomBumpUpRatio of type float64", + }, + { + name: "Invalid oomBumpUpRatio (less than 1)", + vpaJSON: `{ + "apiVersion": "autoscaling.k8s.io/v1", + "kind": "VerticalPodAutoscaler", + "metadata": {"name": "oom-test-vpa"}, + "spec": { + "targetRef": { + "apiVersion": "apps/v1", + "kind": "Deployment", + "name": "oom-test" + }, + "updatePolicy": { + "updateMode": "Auto" + }, + "resourcePolicy": { + "containerPolicies": [{ + "containerName": "*", + "oomBumpUpRatio": 0.5, + "oomMinBumpUp": 104857600 + }] + } + } + }`, + expectedErr: "spec.resourcePolicy.containerPolicies[0].oomBumpUpRatio: Invalid value: 0.5: spec.resourcePolicy.containerPolicies[0].oomBumpUpRatio in body should be greater than or equal to 1", + }, + { + name: "Invalid oomMinBumpUp (negative value)", + vpaJSON: `{ + "apiVersion": "autoscaling.k8s.io/v1", + "kind": "VerticalPodAutoscaler", + "metadata": {"name": "oom-test-vpa"}, + "spec": { + "targetRef": { + "apiVersion": "apps/v1", + "kind": "Deployment", + "name": "oom-test" + }, + "updatePolicy": { + "updateMode": "Auto" + }, + "resourcePolicy": { + "containerPolicies": [{ + "containerName": "*", + "oomBumpUpRatio": 2, + "oomMinBumpUp": -1 + }] + } + } + }`, + expectedErr: "spec.resourcePolicy.containerPolicies[0].oomMinBumpUp: Invalid value: -1: spec.resourcePolicy.containerPolicies[0].oomMinBumpUp in body should be greater than or equal to 0", + }, + { + name: "Invalid minAllowed (invalid requests field)", + vpaJSON: `{ + "apiVersion": "autoscaling.k8s.io/v1", + "kind": "VerticalPodAutoscaler", + "metadata": {"name": "hamster-vpa-invalid"}, + "spec": { + "targetRef": { + "apiVersion": "apps/v1", + "kind": "Deployment", + "name": "hamster" + }, + "resourcePolicy": { + "containerPolicies": [{ + "containerName": "*", + "minAllowed": { + "requests": { + "cpu": "50m" + } + } + }] + } + } + }`, + expectedErr: "admission webhook .*vpa.* denied the request:", + }, + } + for _, tc := range testCases { + ginkgo.By(fmt.Sprintf("Testing %s", tc.name)) + err := InstallRawVPA(f, []byte(tc.vpaJSON)) + gomega.Expect(err).To(gomega.HaveOccurred(), "Invalid VPA object accepted") + gomega.Expect(err.Error()).To(gomega.MatchRegexp(tc.expectedErr)) + } }) ginkgo.It("reloads the webhook leaf and CA certificate", func(ctx ginkgo.SpecContext) { diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go index db9d43a686..b5f2ca5cff 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go @@ -214,6 +214,16 @@ type ContainerResourcePolicy struct { // The default is "RequestsAndLimits". // +optional ControlledValues *ContainerControlledValues `json:"controlledValues,omitempty" protobuf:"bytes,6,rep,name=controlledValues"` + + // OOMBumpUpRatio is the ratio to increase resources when OOM is detected. + // +kubebuilder:validation:Minimum=1.0 + // +optional + OOMBumpUpRatio *float64 `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,1,opt,name=oomBumpUpRatio"` + + // OOMMinBumpUp is the minimum increase in resources when OOM is detected. + // +kubebuilder:validation:Minimum=0 + // +optional + OOMMinBumpUp *float64 `json:"oomMinBumpUp,omitempty" protobuf:"bytes,2,opt,name=oomMinBumpUp"` } const ( diff --git a/vertical-pod-autoscaler/pkg/recommender/main.go b/vertical-pod-autoscaler/pkg/recommender/main.go index cfd5663825..45141a00c5 100644 --- a/vertical-pod-autoscaler/pkg/recommender/main.go +++ b/vertical-pod-autoscaler/pkg/recommender/main.go @@ -96,8 +96,8 @@ var ( memoryAggregationIntervalCount = flag.Int64("memory-aggregation-interval-count", model.DefaultMemoryAggregationIntervalCount, `The number of consecutive memory-aggregation-intervals which make up the MemoryAggregationWindowLength which in turn is the period for memory usage aggregation by VPA. In other words, MemoryAggregationWindowLength = memory-aggregation-interval * memory-aggregation-interval-count.`) memoryHistogramDecayHalfLife = flag.Duration("memory-histogram-decay-half-life", model.DefaultMemoryHistogramDecayHalfLife, `The amount of time it takes a historical memory usage sample to lose half of its weight. In other words, a fresh usage sample is twice as 'important' as one with age equal to the half life period.`) cpuHistogramDecayHalfLife = flag.Duration("cpu-histogram-decay-half-life", model.DefaultCPUHistogramDecayHalfLife, `The amount of time it takes a historical CPU usage sample to lose half of its weight.`) - oomBumpUpRatio = flag.Float64("oom-bump-up-ratio", model.DefaultOOMBumpUpRatio, `The memory bump up ratio when OOM occurred, default is 1.2.`) - oomMinBumpUp = flag.Float64("oom-min-bump-up-bytes", model.DefaultOOMMinBumpUp, `The minimal increase of memory when OOM occurred in bytes, default is 100 * 1024 * 1024`) + oomBumpUpRatio = flag.Float64("oom-bump-up-ratio", model.DefaultOOMBumpUpRatio, `Default memory bump up ratio when OOM occurs. This value applies to all VPAs unless overridden in the VPA spec. Default is 1.2.`) + oomMinBumpUp = flag.Float64("oom-min-bump-up-bytes", model.DefaultOOMMinBumpUp, `Default minimal increase of memory (in bytes) when OOM occurs. This value applies to all VPAs unless overridden in the VPA spec. Default is 100 * 1024 * 1024 (100Mi).`) ) // Post processors flags diff --git a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go index c18e860f4f..7cea321a50 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go @@ -81,6 +81,10 @@ type ContainerStateAggregator interface { // GetUpdateMode returns the update mode of VPA controlling this aggregator, // nil if aggregator is not autoscaled. GetUpdateMode() *vpa_types.UpdateMode + // GetOomBumpUpRatio returns the OOM bump up ratio for this container + GetOomBumpUpRatio() float64 + // GetOOMMinBumpUp returns the minimum OOM bump up value for this container + GetOOMMinBumpUp() float64 } // AggregateContainerState holds input signals aggregated from a set of containers. @@ -109,6 +113,8 @@ type AggregateContainerState struct { IsUnderVPA bool UpdateMode *vpa_types.UpdateMode ScalingMode *vpa_types.ContainerScalingMode + OomBumpUpRatio float64 + OOMMinBumpUp float64 ControlledResources *[]ResourceName } @@ -143,6 +149,16 @@ func (a *AggregateContainerState) GetControlledResources() []ResourceName { return DefaultControlledResources } +// GetOomBumpUpRatio returns the ratio by which to increase the memory recommendation in case of OOM +func (a *AggregateContainerState) GetOomBumpUpRatio() float64 { + return a.OomBumpUpRatio +} + +// GetOOMMinBumpUp returns the minimum absolute increase in memory recommendation in case of OOM +func (a *AggregateContainerState) GetOOMMinBumpUp() float64 { + return a.OOMMinBumpUp +} + // MarkNotAutoscaled registers that this container state is not controlled by // a VPA object. func (a *AggregateContainerState) MarkNotAutoscaled() { @@ -175,6 +191,8 @@ func NewAggregateContainerState() *AggregateContainerState { AggregateCPUUsage: util.NewDecayingHistogram(config.CPUHistogramOptions, config.CPUHistogramDecayHalfLife), AggregateMemoryPeaks: util.NewDecayingHistogram(config.MemoryHistogramOptions, config.MemoryHistogramDecayHalfLife), CreationTime: time.Now(), + OomBumpUpRatio: config.OOMBumpUpRatio, + OOMMinBumpUp: config.OOMMinBumpUp, } } @@ -276,6 +294,12 @@ func (a *AggregateContainerState) UpdateFromPolicy(resourcePolicy *vpa_types.Con // ContainerScalingModeAuto is the default scaling mode scalingModeAuto := vpa_types.ContainerScalingModeAuto a.ScalingMode = &scalingModeAuto + if resourcePolicy != nil && resourcePolicy.OOMBumpUpRatio != nil { + a.OomBumpUpRatio = *resourcePolicy.OOMBumpUpRatio + } + if resourcePolicy != nil && resourcePolicy.OOMMinBumpUp != nil { + a.OOMMinBumpUp = *resourcePolicy.OOMMinBumpUp + } if resourcePolicy != nil && resourcePolicy.Mode != nil { a.ScalingMode = resourcePolicy.Mode } @@ -351,3 +375,11 @@ func (p *ContainerStateAggregatorProxy) GetScalingMode() *vpa_types.ContainerSca aggregator := p.cluster.findOrCreateAggregateContainerState(p.containerID) return aggregator.GetScalingMode() } + +func (p *ContainerStateAggregatorProxy) GetOOMMinBumpUp() float64 { + return 0 +} + +func (p *ContainerStateAggregatorProxy) GetOomBumpUpRatio() float64 { + return 0 +} diff --git a/vertical-pod-autoscaler/pkg/recommender/model/container.go b/vertical-pod-autoscaler/pkg/recommender/model/container.go index cbb1052432..24643863ac 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/container.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/container.go @@ -125,6 +125,14 @@ func (container *ContainerState) GetMaxMemoryPeak() ResourceAmount { return ResourceAmountMax(container.memoryPeak, container.oomPeak) } +func (container *ContainerState) GetOomBumpUpRatio() float64 { + return container.aggregator.GetOomBumpUpRatio() +} + +func (container *ContainerState) GetOOMMinBumpUp() float64 { + return container.aggregator.GetOOMMinBumpUp() +} + func (container *ContainerState) addMemorySample(sample *ContainerUsageSample, isOOM bool) bool { ts := sample.MeasureStart // We always process OOM samples. @@ -183,14 +191,16 @@ func (container *ContainerState) addMemorySample(sample *ContainerUsageSample, i // RecordOOM adds info regarding OOM event in the model as an artificial memory sample. func (container *ContainerState) RecordOOM(timestamp time.Time, requestedMemory ResourceAmount) error { // Discard old OOM - if timestamp.Before(container.WindowEnd.Add(-1 * GetAggregationsConfig().MemoryAggregationInterval)) { + config := GetAggregationsConfig() + // TODO(omerap12): remove MemoryAggregationInterval to per-container configuration as well + if timestamp.Before(container.WindowEnd.Add(-1 * config.MemoryAggregationInterval)) { return fmt.Errorf("OOM event will be discarded - it is too old (%v)", timestamp) } // Get max of the request and the recent usage-based memory peak. // Omitting oomPeak here to protect against recommendation running too high on subsequent OOMs. memoryUsed := ResourceAmountMax(requestedMemory, container.memoryPeak) - memoryNeeded := ResourceAmountMax(memoryUsed+MemoryAmountFromBytes(GetAggregationsConfig().OOMMinBumpUp), - ScaleResource(memoryUsed, GetAggregationsConfig().OOMBumpUpRatio)) + memoryNeeded := ResourceAmountMax(memoryUsed+MemoryAmountFromBytes(container.GetOOMMinBumpUp()), + ScaleResource(memoryUsed, container.GetOomBumpUpRatio())) oomMemorySample := ContainerUsageSample{ MeasureStart: timestamp, From 8228c32faaed8c71c58a8d9b9d8afcf05680eafe Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Mon, 7 Apr 2025 17:14:22 +0000 Subject: [PATCH 02/33] lint Signed-off-by: Omer Aplatony --- .../pkg/recommender/model/aggregate_container_state.go | 4 ++++ vertical-pod-autoscaler/pkg/recommender/model/container.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go index 7cea321a50..2149a44102 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go @@ -376,10 +376,14 @@ func (p *ContainerStateAggregatorProxy) GetScalingMode() *vpa_types.ContainerSca return aggregator.GetScalingMode() } +// GetOOMMinBumpUp returns the minimum amount to bump up resources when OOM is detected. +// This implementation returns 0 to satisfy the interface requirement. func (p *ContainerStateAggregatorProxy) GetOOMMinBumpUp() float64 { return 0 } +// GetOomBumpUpRatio returns the ratio to increase resources when OOM is detected. +// This implementation returns 0 to satisfy the interface requirement. func (p *ContainerStateAggregatorProxy) GetOomBumpUpRatio() float64 { return 0 } diff --git a/vertical-pod-autoscaler/pkg/recommender/model/container.go b/vertical-pod-autoscaler/pkg/recommender/model/container.go index 24643863ac..46d7c2a968 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/container.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/container.go @@ -125,10 +125,14 @@ func (container *ContainerState) GetMaxMemoryPeak() ResourceAmount { return ResourceAmountMax(container.memoryPeak, container.oomPeak) } +// GetOomBumpUpRatio returns the ratio to increase resources when OOM is detected. +// It delegates to the aggregator's implementation. func (container *ContainerState) GetOomBumpUpRatio() float64 { return container.aggregator.GetOomBumpUpRatio() } +// GetOOMMinBumpUp returns the minimum amount to bump up resources when OOM is detected. +// It delegates to the aggregator's implementation. func (container *ContainerState) GetOOMMinBumpUp() float64 { return container.aggregator.GetOOMMinBumpUp() } From 4c6bdfaf53e5b0a9b54d854cbab19de2ba913f21 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Mon, 7 Apr 2025 17:25:39 +0000 Subject: [PATCH 03/33] Add to test Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/pkg/recommender/model/container_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vertical-pod-autoscaler/pkg/recommender/model/container_test.go b/vertical-pod-autoscaler/pkg/recommender/model/container_test.go index 59a004d303..6ed7638680 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/container_test.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/container_test.go @@ -61,6 +61,8 @@ func newContainerTest() ContainerTest { aggregateContainerState := &AggregateContainerState{ AggregateCPUUsage: mockCPUHistogram, AggregateMemoryPeaks: mockMemoryHistogram, + OomBumpUpRatio: 1.5, // Default value, can be adjusted as needed + OOMMinBumpUp: 104857600, // Default value (100Mi), can be adjusted as needed } container := &ContainerState{ Request: TestRequest, From 7605f81d09ee406d406c8a95faa97745431acb22 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Mon, 7 Apr 2025 17:29:41 +0000 Subject: [PATCH 04/33] fmt Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/pkg/recommender/model/container_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vertical-pod-autoscaler/pkg/recommender/model/container_test.go b/vertical-pod-autoscaler/pkg/recommender/model/container_test.go index 6ed7638680..06ed41548a 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/container_test.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/container_test.go @@ -61,7 +61,7 @@ func newContainerTest() ContainerTest { aggregateContainerState := &AggregateContainerState{ AggregateCPUUsage: mockCPUHistogram, AggregateMemoryPeaks: mockMemoryHistogram, - OomBumpUpRatio: 1.5, // Default value, can be adjusted as needed + OomBumpUpRatio: 1.5, // Default value, can be adjusted as needed OOMMinBumpUp: 104857600, // Default value (100Mi), can be adjusted as needed } container := &ContainerState{ From e0eeaaf212e42fe3e0c54dc0dfa4d6b4470fe418 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Mon, 7 Apr 2025 17:41:20 +0000 Subject: [PATCH 05/33] align values with defaults Signed-off-by: Omer Aplatony --- .../pkg/recommender/model/container_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vertical-pod-autoscaler/pkg/recommender/model/container_test.go b/vertical-pod-autoscaler/pkg/recommender/model/container_test.go index 06ed41548a..bc058d5e76 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/container_test.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/container_test.go @@ -61,8 +61,8 @@ func newContainerTest() ContainerTest { aggregateContainerState := &AggregateContainerState{ AggregateCPUUsage: mockCPUHistogram, AggregateMemoryPeaks: mockMemoryHistogram, - OomBumpUpRatio: 1.5, // Default value, can be adjusted as needed - OOMMinBumpUp: 104857600, // Default value (100Mi), can be adjusted as needed + OomBumpUpRatio: 1.2, // Default value, can be adjusted as needed + OOMMinBumpUp: 1.048576e+08, // Default value (100Mi), can be adjusted as needed } container := &ContainerState{ Request: TestRequest, From 7ccaf49dee41cd17f53dfbd2dcbd7a335c0b0b44 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Tue, 8 Apr 2025 05:03:17 +0000 Subject: [PATCH 06/33] fixed functions Signed-off-by: Omer Aplatony --- .../model/aggregate_container_state.go | 18 ++++++++++-------- .../pkg/recommender/model/container.go | 8 ++++---- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go index 2149a44102..96b9ac9432 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go @@ -83,8 +83,8 @@ type ContainerStateAggregator interface { GetUpdateMode() *vpa_types.UpdateMode // GetOomBumpUpRatio returns the OOM bump up ratio for this container GetOomBumpUpRatio() float64 - // GetOOMMinBumpUp returns the minimum OOM bump up value for this container - GetOOMMinBumpUp() float64 + // GetOomMinBumpUp returns the minimum OOM bump up value for this container + GetOomMinBumpUp() float64 } // AggregateContainerState holds input signals aggregated from a set of containers. @@ -154,8 +154,8 @@ func (a *AggregateContainerState) GetOomBumpUpRatio() float64 { return a.OomBumpUpRatio } -// GetOOMMinBumpUp returns the minimum absolute increase in memory recommendation in case of OOM -func (a *AggregateContainerState) GetOOMMinBumpUp() float64 { +// GetOomMinBumpUp returns the minimum absolute increase in memory recommendation in case of OOM +func (a *AggregateContainerState) GetOomMinBumpUp() float64 { return a.OOMMinBumpUp } @@ -376,14 +376,16 @@ func (p *ContainerStateAggregatorProxy) GetScalingMode() *vpa_types.ContainerSca return aggregator.GetScalingMode() } -// GetOOMMinBumpUp returns the minimum amount to bump up resources when OOM is detected. +// GetOomMinBumpUp returns the minimum amount to bump up resources when OOM is detected. // This implementation returns 0 to satisfy the interface requirement. -func (p *ContainerStateAggregatorProxy) GetOOMMinBumpUp() float64 { - return 0 +func (p *ContainerStateAggregatorProxy) GetOomMinBumpUp() float64 { + aggregator := p.cluster.findOrCreateAggregateContainerState(p.containerID) + return aggregator.GetOomMinBumpUp() } // GetOomBumpUpRatio returns the ratio to increase resources when OOM is detected. // This implementation returns 0 to satisfy the interface requirement. func (p *ContainerStateAggregatorProxy) GetOomBumpUpRatio() float64 { - return 0 + aggregator := p.cluster.findOrCreateAggregateContainerState(p.containerID) + return aggregator.GetOomBumpUpRatio() } diff --git a/vertical-pod-autoscaler/pkg/recommender/model/container.go b/vertical-pod-autoscaler/pkg/recommender/model/container.go index 46d7c2a968..f3ca928ffb 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/container.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/container.go @@ -131,10 +131,10 @@ func (container *ContainerState) GetOomBumpUpRatio() float64 { return container.aggregator.GetOomBumpUpRatio() } -// GetOOMMinBumpUp returns the minimum amount to bump up resources when OOM is detected. +// GetOomMinBumpUp returns the minimum amount to bump up resources when OOM is detected. // It delegates to the aggregator's implementation. -func (container *ContainerState) GetOOMMinBumpUp() float64 { - return container.aggregator.GetOOMMinBumpUp() +func (container *ContainerState) GetOomMinBumpUp() float64 { + return container.aggregator.GetOomMinBumpUp() } func (container *ContainerState) addMemorySample(sample *ContainerUsageSample, isOOM bool) bool { @@ -203,7 +203,7 @@ func (container *ContainerState) RecordOOM(timestamp time.Time, requestedMemory // Get max of the request and the recent usage-based memory peak. // Omitting oomPeak here to protect against recommendation running too high on subsequent OOMs. memoryUsed := ResourceAmountMax(requestedMemory, container.memoryPeak) - memoryNeeded := ResourceAmountMax(memoryUsed+MemoryAmountFromBytes(container.GetOOMMinBumpUp()), + memoryNeeded := ResourceAmountMax(memoryUsed+MemoryAmountFromBytes(container.GetOomMinBumpUp()), ScaleResource(memoryUsed, container.GetOomBumpUpRatio())) oomMemorySample := ContainerUsageSample{ From 6787e30aefd5817d402ce582765917ca004d3ea7 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Wed, 9 Apr 2025 19:06:33 +0000 Subject: [PATCH 07/33] Add e2e test and fixed typos Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/e2e/v1/full_vpa.go | 47 +++++++++++++++++++ .../pkg/apis/autoscaling.k8s.io/v1/types.go | 4 +- .../v1/zz_generated.deepcopy.go | 10 ++++ .../model/aggregate_container_state.go | 22 ++++----- .../pkg/recommender/model/container.go | 8 ++-- .../pkg/recommender/model/container_test.go | 2 +- .../pkg/utils/test/test_vpa.go | 18 +++++++ 7 files changed, 93 insertions(+), 18 deletions(-) diff --git a/vertical-pod-autoscaler/e2e/v1/full_vpa.go b/vertical-pod-autoscaler/e2e/v1/full_vpa.go index f390ec5a7b..ad049438bd 100644 --- a/vertical-pod-autoscaler/e2e/v1/full_vpa.go +++ b/vertical-pod-autoscaler/e2e/v1/full_vpa.go @@ -315,6 +315,53 @@ var _ = FullVpaE2eDescribe("OOMing pods under VPA", func() { }) }) +var _ = FullVpaE2eDescribe("OOMing pods under VPA with custom OOM settings", func() { + const replicas = 3 + + f := framework.NewDefaultFramework("vertical-pod-autoscaling") + f.NamespacePodSecurityEnforceLevel = podsecurity.LevelBaseline + + ginkgo.BeforeEach(func() { + ns := f.Namespace.Name + ginkgo.By("Setting up a hamster deployment") + + runOomingReplicationController( + f.ClientSet, + ns, + "hamster", + replicas) + ginkgo.By("Setting up a VPA CRD with custom OOM settings") + targetRef := &autoscaling.CrossVersionObjectReference{ + APIVersion: "v1", + Kind: "Deployment", + Name: "hamster", + } + + containerName := GetHamsterContainerNameByIndex(0) + vpaCRD := test.VerticalPodAutoscaler(). + WithName("hamster-vpa"). + WithNamespace(f.Namespace.Name). + WithTargetRef(targetRef). + WithContainer(containerName). + WithOOMBumpUpRatio(2). + WithOOMMinBumpUp(200 * 1024 * 1024). // 200Mi in bytes + Get() + + InstallVPA(f, vpaCRD) + }) + + ginkgo.It("have memory requests growing with OOMs according to custom settings", func() { + listOptions := metav1.ListOptions{ + LabelSelector: "name=hamster", + FieldSelector: getPodSelectorExcludingDonePodsOrDie(), + } + err := waitForResourceRequestInRangeInPods( + f, oomTestTimeout, listOptions, apiv1.ResourceMemory, + ParseQuantityOrDie("1800Mi"), ParseQuantityOrDie("15000Mi")) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + }) +}) + func waitForPodsMatch(f *framework.Framework, timeout time.Duration, listOptions metav1.ListOptions, matcher func(pod apiv1.Pod) bool) error { return wait.PollUntilContextTimeout(context.Background(), pollInterval, timeout, true, func(ctx context.Context) (done bool, err error) { ns := f.Namespace.Name diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go index b5f2ca5cff..63102cd725 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go @@ -218,12 +218,12 @@ type ContainerResourcePolicy struct { // OOMBumpUpRatio is the ratio to increase resources when OOM is detected. // +kubebuilder:validation:Minimum=1.0 // +optional - OOMBumpUpRatio *float64 `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,1,opt,name=oomBumpUpRatio"` + OOMBumpUpRatio *float64 `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,7,opt,name=oomBumpUpRatio"` // OOMMinBumpUp is the minimum increase in resources when OOM is detected. // +kubebuilder:validation:Minimum=0 // +optional - OOMMinBumpUp *float64 `json:"oomMinBumpUp,omitempty" protobuf:"bytes,2,opt,name=oomMinBumpUp"` + OOMMinBumpUp *float64 `json:"oomMinBumpUp,omitempty" protobuf:"bytes,8,opt,name=oomMinBumpUp"` } const ( diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go index 732ebc2377..433c0edb38 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go @@ -63,6 +63,16 @@ func (in *ContainerResourcePolicy) DeepCopyInto(out *ContainerResourcePolicy) { *out = new(ContainerControlledValues) **out = **in } + if in.OOMBumpUpRatio != nil { + in, out := &in.OOMBumpUpRatio, &out.OOMBumpUpRatio + *out = new(float64) + **out = **in + } + if in.OOMMinBumpUp != nil { + in, out := &in.OOMMinBumpUp, &out.OOMMinBumpUp + *out = new(float64) + **out = **in + } return } diff --git a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go index 96b9ac9432..d199ca58fe 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go @@ -81,8 +81,8 @@ type ContainerStateAggregator interface { // GetUpdateMode returns the update mode of VPA controlling this aggregator, // nil if aggregator is not autoscaled. GetUpdateMode() *vpa_types.UpdateMode - // GetOomBumpUpRatio returns the OOM bump up ratio for this container - GetOomBumpUpRatio() float64 + // GetOOMBumpUpRatio returns the OOM bump up ratio for this container + GetOOMBumpUpRatio() float64 // GetOomMinBumpUp returns the minimum OOM bump up value for this container GetOomMinBumpUp() float64 } @@ -113,7 +113,7 @@ type AggregateContainerState struct { IsUnderVPA bool UpdateMode *vpa_types.UpdateMode ScalingMode *vpa_types.ContainerScalingMode - OomBumpUpRatio float64 + OOMBumpUpRatio float64 OOMMinBumpUp float64 ControlledResources *[]ResourceName } @@ -149,9 +149,9 @@ func (a *AggregateContainerState) GetControlledResources() []ResourceName { return DefaultControlledResources } -// GetOomBumpUpRatio returns the ratio by which to increase the memory recommendation in case of OOM -func (a *AggregateContainerState) GetOomBumpUpRatio() float64 { - return a.OomBumpUpRatio +// GetOOMBumpUpRatio returns the ratio by which to increase the memory recommendation in case of OOM +func (a *AggregateContainerState) GetOOMBumpUpRatio() float64 { + return a.OOMBumpUpRatio } // GetOomMinBumpUp returns the minimum absolute increase in memory recommendation in case of OOM @@ -191,7 +191,7 @@ func NewAggregateContainerState() *AggregateContainerState { AggregateCPUUsage: util.NewDecayingHistogram(config.CPUHistogramOptions, config.CPUHistogramDecayHalfLife), AggregateMemoryPeaks: util.NewDecayingHistogram(config.MemoryHistogramOptions, config.MemoryHistogramDecayHalfLife), CreationTime: time.Now(), - OomBumpUpRatio: config.OOMBumpUpRatio, + OOMBumpUpRatio: config.OOMBumpUpRatio, OOMMinBumpUp: config.OOMMinBumpUp, } } @@ -295,7 +295,7 @@ func (a *AggregateContainerState) UpdateFromPolicy(resourcePolicy *vpa_types.Con scalingModeAuto := vpa_types.ContainerScalingModeAuto a.ScalingMode = &scalingModeAuto if resourcePolicy != nil && resourcePolicy.OOMBumpUpRatio != nil { - a.OomBumpUpRatio = *resourcePolicy.OOMBumpUpRatio + a.OOMBumpUpRatio = *resourcePolicy.OOMBumpUpRatio } if resourcePolicy != nil && resourcePolicy.OOMMinBumpUp != nil { a.OOMMinBumpUp = *resourcePolicy.OOMMinBumpUp @@ -383,9 +383,9 @@ func (p *ContainerStateAggregatorProxy) GetOomMinBumpUp() float64 { return aggregator.GetOomMinBumpUp() } -// GetOomBumpUpRatio returns the ratio to increase resources when OOM is detected. +// GetOOMBumpUpRatio returns the ratio to increase resources when OOM is detected. // This implementation returns 0 to satisfy the interface requirement. -func (p *ContainerStateAggregatorProxy) GetOomBumpUpRatio() float64 { +func (p *ContainerStateAggregatorProxy) GetOOMBumpUpRatio() float64 { aggregator := p.cluster.findOrCreateAggregateContainerState(p.containerID) - return aggregator.GetOomBumpUpRatio() + return aggregator.GetOOMBumpUpRatio() } diff --git a/vertical-pod-autoscaler/pkg/recommender/model/container.go b/vertical-pod-autoscaler/pkg/recommender/model/container.go index f3ca928ffb..7568547c5a 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/container.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/container.go @@ -125,10 +125,10 @@ func (container *ContainerState) GetMaxMemoryPeak() ResourceAmount { return ResourceAmountMax(container.memoryPeak, container.oomPeak) } -// GetOomBumpUpRatio returns the ratio to increase resources when OOM is detected. +// GetOOMBumpUpRatio returns the ratio to increase resources when OOM is detected. // It delegates to the aggregator's implementation. -func (container *ContainerState) GetOomBumpUpRatio() float64 { - return container.aggregator.GetOomBumpUpRatio() +func (container *ContainerState) GetOOMBumpUpRatio() float64 { + return container.aggregator.GetOOMBumpUpRatio() } // GetOomMinBumpUp returns the minimum amount to bump up resources when OOM is detected. @@ -204,7 +204,7 @@ func (container *ContainerState) RecordOOM(timestamp time.Time, requestedMemory // Omitting oomPeak here to protect against recommendation running too high on subsequent OOMs. memoryUsed := ResourceAmountMax(requestedMemory, container.memoryPeak) memoryNeeded := ResourceAmountMax(memoryUsed+MemoryAmountFromBytes(container.GetOomMinBumpUp()), - ScaleResource(memoryUsed, container.GetOomBumpUpRatio())) + ScaleResource(memoryUsed, container.GetOOMBumpUpRatio())) oomMemorySample := ContainerUsageSample{ MeasureStart: timestamp, diff --git a/vertical-pod-autoscaler/pkg/recommender/model/container_test.go b/vertical-pod-autoscaler/pkg/recommender/model/container_test.go index bc058d5e76..45203f72d3 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/container_test.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/container_test.go @@ -61,7 +61,7 @@ func newContainerTest() ContainerTest { aggregateContainerState := &AggregateContainerState{ AggregateCPUUsage: mockCPUHistogram, AggregateMemoryPeaks: mockMemoryHistogram, - OomBumpUpRatio: 1.2, // Default value, can be adjusted as needed + OOMBumpUpRatio: 1.2, // Default value, can be adjusted as needed OOMMinBumpUp: 1.048576e+08, // Default value (100Mi), can be adjusted as needed } container := &ContainerState{ diff --git a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go index 6bb3279baf..0049ffe901 100644 --- a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go +++ b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go @@ -47,6 +47,8 @@ type VerticalPodAutoscalerBuilder interface { WithGroupVersion(gv meta.GroupVersion) VerticalPodAutoscalerBuilder WithEvictionRequirements([]*vpa_types.EvictionRequirement) VerticalPodAutoscalerBuilder WithMinReplicas(minReplicas *int32) VerticalPodAutoscalerBuilder + WithOOMBumpUpRatio(ratio float64) VerticalPodAutoscalerBuilder + WithOOMMinBumpUp(minBumpUp float64) VerticalPodAutoscalerBuilder AppendCondition(conditionType vpa_types.VerticalPodAutoscalerConditionType, status core.ConditionStatus, reason, message string, lastTransitionTime time.Time) VerticalPodAutoscalerBuilder AppendRecommendation(vpa_types.RecommendedContainerResources) VerticalPodAutoscalerBuilder @@ -87,6 +89,8 @@ type verticalPodAutoscalerBuilder struct { targetRef *autoscaling.CrossVersionObjectReference appendedRecommendations []vpa_types.RecommendedContainerResources recommender string + oomBumpUpRatio *float64 + oomMinBumpUp *float64 } func (b *verticalPodAutoscalerBuilder) WithName(vpaName string) VerticalPodAutoscalerBuilder { @@ -214,6 +218,18 @@ func (b *verticalPodAutoscalerBuilder) WithMinReplicas(minReplicas *int32) Verti return &c } +func (b *verticalPodAutoscalerBuilder) WithOOMBumpUpRatio(ratio float64) VerticalPodAutoscalerBuilder { + c := *b + c.oomBumpUpRatio = &ratio + return &c +} + +func (b *verticalPodAutoscalerBuilder) WithOOMMinBumpUp(minBumpUp float64) VerticalPodAutoscalerBuilder { + c := *b + c.oomMinBumpUp = &minBumpUp + return &c +} + func (b *verticalPodAutoscalerBuilder) AppendCondition(conditionType vpa_types.VerticalPodAutoscalerConditionType, status core.ConditionStatus, reason, message string, lastTransitionTime time.Time) VerticalPodAutoscalerBuilder { c := *b @@ -250,6 +266,8 @@ func (b *verticalPodAutoscalerBuilder) Get() *vpa_types.VerticalPodAutoscaler { MaxAllowed: b.maxAllowed[containerName], ControlledValues: b.controlledValues[containerName], Mode: &scalingModeAuto, + OOMBumpUpRatio: b.oomBumpUpRatio, + OOMMinBumpUp: b.oomMinBumpUp, } if scalingMode, ok := b.scalingMode[containerName]; ok { containerResourcePolicy.Mode = scalingMode From 0d00a1e384e11506f7331d1d88e76b59a471f84c Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Fri, 11 Apr 2025 14:34:41 +0000 Subject: [PATCH 08/33] fmt: fixed function name Signed-off-by: Omer Aplatony --- .../recommender/model/aggregate_container_state.go | 14 +++++++------- .../pkg/recommender/model/container.go | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go index d199ca58fe..f7046a42b7 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go @@ -83,8 +83,8 @@ type ContainerStateAggregator interface { GetUpdateMode() *vpa_types.UpdateMode // GetOOMBumpUpRatio returns the OOM bump up ratio for this container GetOOMBumpUpRatio() float64 - // GetOomMinBumpUp returns the minimum OOM bump up value for this container - GetOomMinBumpUp() float64 + // GetOOMMinBumpUp returns the minimum OOM bump up value for this container + GetOOMMinBumpUp() float64 } // AggregateContainerState holds input signals aggregated from a set of containers. @@ -154,8 +154,8 @@ func (a *AggregateContainerState) GetOOMBumpUpRatio() float64 { return a.OOMBumpUpRatio } -// GetOomMinBumpUp returns the minimum absolute increase in memory recommendation in case of OOM -func (a *AggregateContainerState) GetOomMinBumpUp() float64 { +// GetOOMMinBumpUp returns the minimum absolute increase in memory recommendation in case of OOM +func (a *AggregateContainerState) GetOOMMinBumpUp() float64 { return a.OOMMinBumpUp } @@ -376,11 +376,11 @@ func (p *ContainerStateAggregatorProxy) GetScalingMode() *vpa_types.ContainerSca return aggregator.GetScalingMode() } -// GetOomMinBumpUp returns the minimum amount to bump up resources when OOM is detected. +// GetOOMMinBumpUp returns the minimum amount to bump up resources when OOM is detected. // This implementation returns 0 to satisfy the interface requirement. -func (p *ContainerStateAggregatorProxy) GetOomMinBumpUp() float64 { +func (p *ContainerStateAggregatorProxy) GetOOMMinBumpUp() float64 { aggregator := p.cluster.findOrCreateAggregateContainerState(p.containerID) - return aggregator.GetOomMinBumpUp() + return aggregator.GetOOMMinBumpUp() } // GetOOMBumpUpRatio returns the ratio to increase resources when OOM is detected. diff --git a/vertical-pod-autoscaler/pkg/recommender/model/container.go b/vertical-pod-autoscaler/pkg/recommender/model/container.go index 7568547c5a..6f0d64b65f 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/container.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/container.go @@ -131,10 +131,10 @@ func (container *ContainerState) GetOOMBumpUpRatio() float64 { return container.aggregator.GetOOMBumpUpRatio() } -// GetOomMinBumpUp returns the minimum amount to bump up resources when OOM is detected. +// GetOOMMinBumpUp returns the minimum amount to bump up resources when OOM is detected. // It delegates to the aggregator's implementation. -func (container *ContainerState) GetOomMinBumpUp() float64 { - return container.aggregator.GetOomMinBumpUp() +func (container *ContainerState) GetOOMMinBumpUp() float64 { + return container.aggregator.GetOOMMinBumpUp() } func (container *ContainerState) addMemorySample(sample *ContainerUsageSample, isOOM bool) bool { @@ -203,7 +203,7 @@ func (container *ContainerState) RecordOOM(timestamp time.Time, requestedMemory // Get max of the request and the recent usage-based memory peak. // Omitting oomPeak here to protect against recommendation running too high on subsequent OOMs. memoryUsed := ResourceAmountMax(requestedMemory, container.memoryPeak) - memoryNeeded := ResourceAmountMax(memoryUsed+MemoryAmountFromBytes(container.GetOomMinBumpUp()), + memoryNeeded := ResourceAmountMax(memoryUsed+MemoryAmountFromBytes(container.GetOOMMinBumpUp()), ScaleResource(memoryUsed, container.GetOOMBumpUpRatio())) oomMemorySample := ContainerUsageSample{ From c2d0b5aac3c271ca83d7e703695e398eea4bbb0c Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Sat, 14 Jun 2025 14:52:40 +0000 Subject: [PATCH 09/33] run generate flags Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/docs/flags.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vertical-pod-autoscaler/docs/flags.md b/vertical-pod-autoscaler/docs/flags.md index 7f10b2a966..9df1af676c 100644 --- a/vertical-pod-autoscaler/docs/flags.md +++ b/vertical-pod-autoscaler/docs/flags.md @@ -95,8 +95,8 @@ This document is auto-generated from the flag definitions in the VPA recommender | `metric-for-pod-labels` | string | "up{job=\"kubernetes-pods\"}" | Which metric to look for pod labels in metrics | | `min-checkpoints` | int | 10 | Minimum number of checkpoints to write per recommender's main loop. WARNING: this flag is deprecated and doesn't have any effect. It will be removed in a future release. Refer to update-worker-count to influence the minimum number of checkpoints written per loop. | | `one-output` | severity | | If true, only write logs to their native level (vs also writing to each lower severity level; no effect when -logtostderr=true) | -| `oom-bump-up-ratio` | float | 1.2 | The memory bump up ratio when OOM occurred, default is 1.2. | -| `oom-min-bump-up-bytes` | float | 1.048576e+08 | The minimal increase of memory when OOM occurred in bytes, default is 100 * 1024 * 1024 | +| `oom-bump-up-ratio` | float | 1.2 | Default memory bump up ratio when OOM occurs. This value applies to all VPAs unless overridden in the VPA spec. Default is 1.2. | +| `oom-min-bump-up-bytes` | float | 1.048576e+08 | Default minimal increase of memory (in bytes) when OOM occurs. This value applies to all VPAs unless overridden in the VPA spec. Default is 100 * 1024 * 1024 (100Mi). | | `password` | string | | The password used in the prometheus server basic auth | | `pod-label-prefix` | string | "pod_label_" | Which prefix to look for pod labels in metrics | | `pod-name-label` | string | "kubernetes_pod_name" | Label name to look for pod names | From d3211d6c9eda591e386396455f545e1b5605050c Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Wed, 18 Jun 2025 09:01:21 +0000 Subject: [PATCH 10/33] in-progress Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/docs/flags.md | 6 +++--- vertical-pod-autoscaler/e2e/v1/full_vpa.go | 6 ++++++ vertical-pod-autoscaler/pkg/features/features.go | 9 +++++++++ .../pkg/features/versioned_features.go | 3 +++ .../model/aggregate_container_state.go | 15 +++++++++------ 5 files changed, 30 insertions(+), 9 deletions(-) diff --git a/vertical-pod-autoscaler/docs/flags.md b/vertical-pod-autoscaler/docs/flags.md index 9df1af676c..6448def6d9 100644 --- a/vertical-pod-autoscaler/docs/flags.md +++ b/vertical-pod-autoscaler/docs/flags.md @@ -14,7 +14,7 @@ This document is auto-generated from the flag definitions in the VPA admission-c | `address` | string | ":8944" | The address to expose Prometheus metrics. | | `alsologtostderr` | | | log to standard error as well as files (no effect when -logtostderr=true) | | `client-ca-file` | string | "/etc/tls-certs/caCert.pem" | Path to CA PEM file. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=false) | | `ignored-vpa-object-namespaces` | string | | A comma-separated list of namespaces to ignore when searching for VPA objects. Leave empty to avoid ignoring any namespaces. These namespaces will not be cleaned by the garbage collector. | | `kube-api-burst` | float | 100 | QPS burst limit when making requests to Kubernetes apiserver | | `kube-api-qps` | float | 50 | QPS limit when making requests to Kubernetes apiserver | @@ -68,7 +68,7 @@ This document is auto-generated from the flag definitions in the VPA recommender | `cpu-integer-post-processor-enabled` | | | Enable the cpu-integer recommendation post processor. The post processor will round up CPU recommendations to a whole CPU for pods which were opted in by setting an appropriate label on VPA object (experimental) | | `external-metrics-cpu-metric` | string | | ALPHA. Metric to use with external metrics provider for CPU usage. | | `external-metrics-memory-metric` | string | | ALPHA. Metric to use with external metrics provider for memory usage. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=false) | | `history-length` | string | "8d" | How much time back prometheus have to be queried to get historical metrics | | `history-resolution` | string | "1h" | Resolution at which Prometheus is queried for historical metrics | | `humanize-memory` | | | Convert memory values in recommendations to the highest appropriate SI unit with up to 2 decimal places for better readability. | @@ -140,7 +140,7 @@ This document is auto-generated from the flag definitions in the VPA updater cod | `eviction-rate-burst` | int | 1 | Burst of pods that can be evicted. | | `eviction-rate-limit` | float | | Number of pods that can be evicted per seconds. A rate limit set to 0 or -1 will disable
the rate limiter. (default -1) | | `eviction-tolerance` | float | 0.5 | Fraction of replica count that can be evicted for update, if more than one pod can be evicted. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=false) | | `ignored-vpa-object-namespaces` | string | | A comma-separated list of namespaces to ignore when searching for VPA objects. Leave empty to avoid ignoring any namespaces. These namespaces will not be cleaned by the garbage collector. | | `in-recommendation-bounds-eviction-lifetime-threshold` | | 12h0m0s | duration Pods that live for at least that long can be evicted even if their request is within the [MinRecommended...MaxRecommended] range | | `kube-api-burst` | float | 100 | QPS burst limit when making requests to Kubernetes apiserver | diff --git a/vertical-pod-autoscaler/e2e/v1/full_vpa.go b/vertical-pod-autoscaler/e2e/v1/full_vpa.go index 12b77cdae1..d65c8fa928 100644 --- a/vertical-pod-autoscaler/e2e/v1/full_vpa.go +++ b/vertical-pod-autoscaler/e2e/v1/full_vpa.go @@ -27,6 +27,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" + "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/features" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/test" "k8s.io/kubernetes/test/e2e/framework" podsecurity "k8s.io/pod-security-admission/api" @@ -392,6 +393,7 @@ var _ = FullVpaE2eDescribe("OOMing pods under VPA", func() { }) }) + var _ = FullVpaE2eDescribe("OOMing pods under VPA with custom OOM settings", func() { const replicas = 3 @@ -399,6 +401,10 @@ var _ = FullVpaE2eDescribe("OOMing pods under VPA with custom OOM settings", fun f.NamespacePodSecurityEnforceLevel = podsecurity.LevelBaseline ginkgo.BeforeEach(func() { + if !features.Enabled(features.PerVPAConfig) { + ginkgo.Skip("Test requires PerVPAConfig feature gate to be enabled") + } + ns := f.Namespace.Name ginkgo.By("Setting up a hamster deployment") diff --git a/vertical-pod-autoscaler/pkg/features/features.go b/vertical-pod-autoscaler/pkg/features/features.go index 513973f639..0d8ee7bcff 100644 --- a/vertical-pod-autoscaler/pkg/features/features.go +++ b/vertical-pod-autoscaler/pkg/features/features.go @@ -46,6 +46,15 @@ const ( // InPlaceOrRecreate enables the InPlaceOrRecreate update mode to be used. // Requires KEP-1287 InPlacePodVerticalScaling feature-gate to be enabled on the cluster. InPlaceOrRecreate featuregate.Feature = "InPlaceOrRecreate" + + // alpha: v1.5.0 + // components: admission-controller, recommender, updater + // PerVPAConfig enables the ability to specify component-specific configuration + // parameters at the individual VPA object level. This allows for different + // optimization strategies to be applied to different workloads within the + // same cluster. + PerVPAConfig featuregate.Feature = "PerVPAConfig" + ) // MutableFeatureGate is a mutable, versioned, global FeatureGate. diff --git a/vertical-pod-autoscaler/pkg/features/versioned_features.go b/vertical-pod-autoscaler/pkg/features/versioned_features.go index c3fd990f80..0225889741 100644 --- a/vertical-pod-autoscaler/pkg/features/versioned_features.go +++ b/vertical-pod-autoscaler/pkg/features/versioned_features.go @@ -30,4 +30,7 @@ var defaultVersionedFeatureGates = map[featuregate.Feature]featuregate.Versioned InPlaceOrRecreate: { {Version: version.MustParse("1.4"), Default: false, PreRelease: featuregate.Alpha}, }, + PerVPAConfig : { + {Version: version.MustParse("1.5"), Default: false, PreRelease: featuregate.Alpha}, + }, } diff --git a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go index f7046a42b7..50dba61ccd 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go @@ -43,6 +43,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" + "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/features" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/recommender/util" ) @@ -294,12 +295,6 @@ func (a *AggregateContainerState) UpdateFromPolicy(resourcePolicy *vpa_types.Con // ContainerScalingModeAuto is the default scaling mode scalingModeAuto := vpa_types.ContainerScalingModeAuto a.ScalingMode = &scalingModeAuto - if resourcePolicy != nil && resourcePolicy.OOMBumpUpRatio != nil { - a.OOMBumpUpRatio = *resourcePolicy.OOMBumpUpRatio - } - if resourcePolicy != nil && resourcePolicy.OOMMinBumpUp != nil { - a.OOMMinBumpUp = *resourcePolicy.OOMMinBumpUp - } if resourcePolicy != nil && resourcePolicy.Mode != nil { a.ScalingMode = resourcePolicy.Mode } @@ -307,6 +302,14 @@ func (a *AggregateContainerState) UpdateFromPolicy(resourcePolicy *vpa_types.Con if resourcePolicy != nil && resourcePolicy.ControlledResources != nil { a.ControlledResources = ResourceNamesApiToModel(*resourcePolicy.ControlledResources) } + + // Per VPA components - feature flag "PerVPAConfig" must be enabled + if resourcePolicy != nil && resourcePolicy.OOMBumpUpRatio != nil && features.Enabled(features.PerVPAConfig) { + a.OOMBumpUpRatio = *resourcePolicy.OOMBumpUpRatio + } + if resourcePolicy != nil && resourcePolicy.OOMMinBumpUp != nil && features.Enabled(features.PerVPAConfig){ + a.OOMMinBumpUp = *resourcePolicy.OOMMinBumpUp + } } // AggregateStateByContainerName takes a set of AggregateContainerStates and merge them From 95c22faab3676a498b45b190c7fa1ecef0fcfac6 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Wed, 18 Jun 2025 09:02:09 +0000 Subject: [PATCH 11/33] in-progress Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/pkg/features/versioned_features.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vertical-pod-autoscaler/pkg/features/versioned_features.go b/vertical-pod-autoscaler/pkg/features/versioned_features.go index 0225889741..65bc404b6a 100644 --- a/vertical-pod-autoscaler/pkg/features/versioned_features.go +++ b/vertical-pod-autoscaler/pkg/features/versioned_features.go @@ -31,6 +31,6 @@ var defaultVersionedFeatureGates = map[featuregate.Feature]featuregate.Versioned {Version: version.MustParse("1.4"), Default: false, PreRelease: featuregate.Alpha}, }, PerVPAConfig : { - {Version: version.MustParse("1.5"), Default: false, PreRelease: featuregate.Alpha}, + {Version: version.MustParse("1.5"), Default: true, PreRelease: featuregate.Alpha}, }, } From 53c610eff75e665111a6ba77aa2df69483e77da8 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Sat, 26 Jul 2025 16:05:56 +0000 Subject: [PATCH 12/33] update e2e test Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/e2e/v1/full_vpa.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/vertical-pod-autoscaler/e2e/v1/full_vpa.go b/vertical-pod-autoscaler/e2e/v1/full_vpa.go index d65c8fa928..7e3b7c017f 100644 --- a/vertical-pod-autoscaler/e2e/v1/full_vpa.go +++ b/vertical-pod-autoscaler/e2e/v1/full_vpa.go @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/util/wait" vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/features" + "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/recommender/model" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/test" "k8s.io/kubernetes/test/e2e/framework" podsecurity "k8s.io/pod-security-admission/api" @@ -377,7 +378,6 @@ var _ = FullVpaE2eDescribe("OOMing pods under VPA", func() { WithTargetRef(targetRef). WithContainer(containerName). Get() - InstallVPA(f, vpaCRD) }) @@ -390,10 +390,26 @@ var _ = FullVpaE2eDescribe("OOMing pods under VPA", func() { f, oomTestTimeout, listOptions, apiv1.ResourceMemory, ParseQuantityOrDie("1400Mi"), ParseQuantityOrDie("10000Mi")) gomega.Expect(err).NotTo(gomega.HaveOccurred()) + // Checking that the pod recommendation is bigger then DefaultOOMBumpUpRatio ( this is the default which are ovverided by oom) + vpa, err := getVpaClientSet(f).AutoscalingV1().VerticalPodAutoscalers(f.Namespace.Name).Get(context.TODO(), "hamster-vpa", metav1.GetOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + // Check that the recommendation is using the custom bump ratio and not the default + for _, container := range vpa.Status.Recommendation.ContainerRecommendations { + currentMemory := container.Target.Memory().Value() + + // Calculate what the memory would be with default bump ratio + defaultBumpMemory := float64(currentMemory) * model.DefaultOOMBumpUpRatio + + // The actual memory should be higher than what it would be with default bump ratio + // because we're using a custom ratio that should be higher than 1.2 + gomega.Expect(float64(currentMemory)).To( + gomega.BeNumerically(">", defaultBumpMemory), + "Memory recommendation is not using the custom OOM bump ratio") + } }) }) - var _ = FullVpaE2eDescribe("OOMing pods under VPA with custom OOM settings", func() { const replicas = 3 From bf5cb236610f1dbe8c5241866ff972b38771486f Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Sun, 27 Jul 2025 08:53:22 +0000 Subject: [PATCH 13/33] update e2e Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/e2e/v1/full_vpa.go | 73 +------------------ vertical-pod-autoscaler/e2e/v1/recommender.go | 63 ++++++++++++++++ 2 files changed, 65 insertions(+), 71 deletions(-) diff --git a/vertical-pod-autoscaler/e2e/v1/full_vpa.go b/vertical-pod-autoscaler/e2e/v1/full_vpa.go index 7e3b7c017f..7f44b04e56 100644 --- a/vertical-pod-autoscaler/e2e/v1/full_vpa.go +++ b/vertical-pod-autoscaler/e2e/v1/full_vpa.go @@ -27,8 +27,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" - "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/features" - "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/recommender/model" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/test" "k8s.io/kubernetes/test/e2e/framework" podsecurity "k8s.io/pod-security-admission/api" @@ -378,6 +376,7 @@ var _ = FullVpaE2eDescribe("OOMing pods under VPA", func() { WithTargetRef(targetRef). WithContainer(containerName). Get() + InstallVPA(f, vpaCRD) }) @@ -390,74 +389,6 @@ var _ = FullVpaE2eDescribe("OOMing pods under VPA", func() { f, oomTestTimeout, listOptions, apiv1.ResourceMemory, ParseQuantityOrDie("1400Mi"), ParseQuantityOrDie("10000Mi")) gomega.Expect(err).NotTo(gomega.HaveOccurred()) - // Checking that the pod recommendation is bigger then DefaultOOMBumpUpRatio ( this is the default which are ovverided by oom) - vpa, err := getVpaClientSet(f).AutoscalingV1().VerticalPodAutoscalers(f.Namespace.Name).Get(context.TODO(), "hamster-vpa", metav1.GetOptions{}) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - - // Check that the recommendation is using the custom bump ratio and not the default - for _, container := range vpa.Status.Recommendation.ContainerRecommendations { - currentMemory := container.Target.Memory().Value() - - // Calculate what the memory would be with default bump ratio - defaultBumpMemory := float64(currentMemory) * model.DefaultOOMBumpUpRatio - - // The actual memory should be higher than what it would be with default bump ratio - // because we're using a custom ratio that should be higher than 1.2 - gomega.Expect(float64(currentMemory)).To( - gomega.BeNumerically(">", defaultBumpMemory), - "Memory recommendation is not using the custom OOM bump ratio") - } - }) -}) - -var _ = FullVpaE2eDescribe("OOMing pods under VPA with custom OOM settings", func() { - const replicas = 3 - - f := framework.NewDefaultFramework("vertical-pod-autoscaling") - f.NamespacePodSecurityEnforceLevel = podsecurity.LevelBaseline - - ginkgo.BeforeEach(func() { - if !features.Enabled(features.PerVPAConfig) { - ginkgo.Skip("Test requires PerVPAConfig feature gate to be enabled") - } - - ns := f.Namespace.Name - ginkgo.By("Setting up a hamster deployment") - - runOomingReplicationController( - f.ClientSet, - ns, - "hamster", - replicas) - ginkgo.By("Setting up a VPA CRD with custom OOM settings") - targetRef := &autoscaling.CrossVersionObjectReference{ - APIVersion: "v1", - Kind: "Deployment", - Name: "hamster", - } - - containerName := GetHamsterContainerNameByIndex(0) - vpaCRD := test.VerticalPodAutoscaler(). - WithName("hamster-vpa"). - WithNamespace(f.Namespace.Name). - WithTargetRef(targetRef). - WithContainer(containerName). - WithOOMBumpUpRatio(2). - WithOOMMinBumpUp(200 * 1024 * 1024). // 200Mi in bytes - Get() - - InstallVPA(f, vpaCRD) - }) - - ginkgo.It("have memory requests growing with OOMs according to custom settings", func() { - listOptions := metav1.ListOptions{ - LabelSelector: "name=hamster", - FieldSelector: getPodSelectorExcludingDonePodsOrDie(), - } - err := waitForResourceRequestInRangeInPods( - f, oomTestTimeout, listOptions, apiv1.ResourceMemory, - ParseQuantityOrDie("1800Mi"), ParseQuantityOrDie("15000Mi")) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) }) }) @@ -502,4 +433,4 @@ func waitForResourceRequestInRangeInPods(f *framework.Framework, timeout time.Du return fmt.Errorf("error waiting for %s request in range of (%v,%v) for pods: %+v", resourceName, lowerBound, upperBound, listOptions) } return nil -} +} \ No newline at end of file diff --git a/vertical-pod-autoscaler/e2e/v1/recommender.go b/vertical-pod-autoscaler/e2e/v1/recommender.go index 694f257b3c..f2a7de0f77 100644 --- a/vertical-pod-autoscaler/e2e/v1/recommender.go +++ b/vertical-pod-autoscaler/e2e/v1/recommender.go @@ -28,6 +28,8 @@ import ( "k8s.io/apimachinery/pkg/fields" vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" vpa_clientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned" + "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/features" + "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/recommender/model" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/test" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" @@ -411,6 +413,67 @@ var _ = RecommenderE2eDescribe("VPA CRD object", func() { }) }) +var _ = RecommenderE2eDescribe("OOM with custom config)", func() { + const replicas = 3 + f := framework.NewDefaultFramework("vertical-pod-autoscaling") + f.NamespacePodSecurityEnforceLevel = podsecurity.LevelBaseline + + var ( + vpaCRD *vpa_types.VerticalPodAutoscaler + vpaClientSet vpa_clientset.Interface + ) + + ginkgo.BeforeEach(func() { + if !features.Enabled(features.PerVPAConfig) { + ginkgo.Skip("Test requires PerVPAConfig feature gate to be enabled") + } + ns := f.Namespace.Name + vpaClientSet = getVpaClientSet(f) + ginkgo.By("Setting up a hamster deployment") + runOomingReplicationController( + f.ClientSet, + ns, + "hamster", + replicas) + ginkgo.By("Setting up a VPA CRD") + targetRef := &autoscaling.CrossVersionObjectReference{ + APIVersion: "v1", + Kind: "Deployment", + Name: "hamster", + } + containerName := GetHamsterContainerNameByIndex(0) + vpaCRD := test.VerticalPodAutoscaler(). + WithName("hamster-vpa"). + WithNamespace(f.Namespace.Name). + WithTargetRef(targetRef). + WithContainer(containerName). + WithOOMBumpUpRatio(2). + Get() + InstallVPA(f, vpaCRD) + }) + ginkgo.It("have memory requests growing with OOMs more than the default", func() { + listOptions := metav1.ListOptions{ + LabelSelector: "name=hamster", + FieldSelector: getPodSelectorExcludingDonePodsOrDie(), + } + err := waitForResourceRequestInRangeInPods( + f, oomTestTimeout, listOptions, apiv1.ResourceMemory, + ParseQuantityOrDie("1024Mi"), ParseQuantityOrDie("1024Mi")) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + ginkgo.By("Waiting for recommendation to be filled") + vpa, err := WaitForRecommendationPresent(vpaClientSet, vpaCRD) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations).Should(gomega.HaveLen(1)) + + currentMemory := vpa.Status.Recommendation.ContainerRecommendations[0].Target.Memory().Value() + oomReplicationControllerRequestLimit := float64(1024 * 1024 * 1024) // This is hard coded inside runOomingReplicationController function and should be changed + defaultBumpMemory := oomReplicationControllerRequestLimit * model.DefaultOOMBumpUpRatio + + // The memory should be bigger then the default oom bump ratio + gomega.Expect(currentMemory).Should(gomega.BeNumerically(">", defaultBumpMemory)) + }) +}) + func deleteRecommender(c clientset.Interface) error { namespace := "kube-system" listOptions := metav1.ListOptions{} From 8463a5df50af44e5a87d9ff7b6a5af821c97c979 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Sun, 27 Jul 2025 09:01:01 +0000 Subject: [PATCH 14/33] update e2e Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/e2e/v1/recommender.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vertical-pod-autoscaler/e2e/v1/recommender.go b/vertical-pod-autoscaler/e2e/v1/recommender.go index f2a7de0f77..bf621ed3d6 100644 --- a/vertical-pod-autoscaler/e2e/v1/recommender.go +++ b/vertical-pod-autoscaler/e2e/v1/recommender.go @@ -29,7 +29,6 @@ import ( vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" vpa_clientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/features" - "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/recommender/model" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/test" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" @@ -413,7 +412,7 @@ var _ = RecommenderE2eDescribe("VPA CRD object", func() { }) }) -var _ = RecommenderE2eDescribe("OOM with custom config)", func() { +var _ = RecommenderE2eDescribe("OOM with custom config", func() { const replicas = 3 f := framework.NewDefaultFramework("vertical-pod-autoscaling") f.NamespacePodSecurityEnforceLevel = podsecurity.LevelBaseline @@ -466,11 +465,12 @@ var _ = RecommenderE2eDescribe("OOM with custom config)", func() { gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations).Should(gomega.HaveLen(1)) currentMemory := vpa.Status.Recommendation.ContainerRecommendations[0].Target.Memory().Value() - oomReplicationControllerRequestLimit := float64(1024 * 1024 * 1024) // This is hard coded inside runOomingReplicationController function and should be changed - defaultBumpMemory := oomReplicationControllerRequestLimit * model.DefaultOOMBumpUpRatio + oomReplicationControllerRequestLimit := int64(1024 * 1024 * 1024) // from runOomingReplicationController + defaultBumpMemory := float64(oomReplicationControllerRequestLimit) * 1.2 // DefaultOOMBumpUpRatio + customBumpMemory := float64(oomReplicationControllerRequestLimit) * 2.0 // Custom ratio from VPA config - // The memory should be bigger then the default oom bump ratio - gomega.Expect(currentMemory).Should(gomega.BeNumerically(">", defaultBumpMemory)) + gomega.Expect(currentMemory).Should(gomega.BeNumerically(">", int64(defaultBumpMemory)), + fmt.Sprintf("Memory recommendation should be at bigger than default bump up ratio (2x). Got: %d, Expected: >= %d", currentMemory, int64(customBumpMemory))) }) }) From fad96154fe06dbc46e311ab51dc5c8ea1e2c760d Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Sun, 27 Jul 2025 19:25:56 +0000 Subject: [PATCH 15/33] migrate to quantity Signed-off-by: Omer Aplatony --- .../resource/vpa/handler.go | 17 +++++++++++++++++ .../pkg/apis/autoscaling.k8s.io/v1/types.go | 7 +++---- .../v1/zz_generated.deepcopy.go | 8 ++++---- .../versioned/fake/clientset_generated.go | 7 ++++++- .../v1/autoscaling.k8s.io_client.go | 12 +++--------- .../v1beta1/autoscaling.k8s.io_client.go | 12 +++--------- .../v1beta2/autoscaling.k8s.io_client.go | 12 +++--------- .../v1alpha1/poc.autoscaling.k8s.io_client.go | 12 +++--------- .../v1/verticalpodautoscaler.go | 16 ++++++++++++++-- .../v1/verticalpodautoscalercheckpoint.go | 16 ++++++++++++++-- .../v1beta1/verticalpodautoscaler.go | 16 ++++++++++++++-- .../v1beta1/verticalpodautoscalercheckpoint.go | 16 ++++++++++++++-- .../v1beta2/verticalpodautoscaler.go | 16 ++++++++++++++-- .../v1beta2/verticalpodautoscalercheckpoint.go | 16 ++++++++++++++-- .../v1alpha1/verticalpodautoscaler.go | 16 ++++++++++++++-- .../v1alpha1/verticalpodautoscalercheckpoint.go | 16 ++++++++++++++-- .../model/aggregate_container_state.go | 14 +++++++++++--- .../pkg/utils/test/test_vpa.go | 17 +++++++++-------- 18 files changed, 174 insertions(+), 72 deletions(-) diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go index b553a236f9..a2202c17aa 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go @@ -137,6 +137,23 @@ func ValidateVPA(vpa *vpa_types.VerticalPodAutoscaler, isCreate bool) error { if policy.ContainerName == "" { return fmt.Errorf("ContainerPolicies.ContainerName is required") } + + // Validate OOMBumpUpRatio + if policy.OOMBumpUpRatio != nil { + ratio := float64(policy.OOMBumpUpRatio.MilliValue()) / 1000.0 + if ratio <= 1.0 { + return fmt.Errorf("OOMBumpUpRatio must be greater than 1.0, got %v", ratio) + } + } + + // Validate OOMMinBumpUp + if policy.OOMMinBumpUp != nil { + minBump := policy.OOMMinBumpUp.Value() + if minBump <= 0 { + return fmt.Errorf("OOMMinBumpUp must be greater than 0, got %v bytes", minBump) + } + } + mode := policy.Mode if mode != nil { if _, found := possibleScalingModes[*mode]; !found { diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go index b2f4aca24a..cc35946321 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go @@ -19,6 +19,7 @@ package v1 import ( autoscaling "k8s.io/api/autoscaling/v1" v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -223,14 +224,12 @@ type ContainerResourcePolicy struct { ControlledValues *ContainerControlledValues `json:"controlledValues,omitempty" protobuf:"bytes,6,rep,name=controlledValues"` // OOMBumpUpRatio is the ratio to increase resources when OOM is detected. - // +kubebuilder:validation:Minimum=1.0 // +optional - OOMBumpUpRatio *float64 `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,7,opt,name=oomBumpUpRatio"` + OOMBumpUpRatio *resource.Quantity `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,7,opt,name=oomBumpUpRatio"` // OOMMinBumpUp is the minimum increase in resources when OOM is detected. - // +kubebuilder:validation:Minimum=0 // +optional - OOMMinBumpUp *float64 `json:"oomMinBumpUp,omitempty" protobuf:"bytes,8,opt,name=oomMinBumpUp"` + OOMMinBumpUp *resource.Quantity `json:"oomMinBumpUp,omitempty" protobuf:"bytes,8,opt,name=oomMinBumpUp"` } const ( diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go index 433c0edb38..86ff44f69e 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go @@ -65,13 +65,13 @@ func (in *ContainerResourcePolicy) DeepCopyInto(out *ContainerResourcePolicy) { } if in.OOMBumpUpRatio != nil { in, out := &in.OOMBumpUpRatio, &out.OOMBumpUpRatio - *out = new(float64) - **out = **in + x := (*in).DeepCopy() + *out = &x } if in.OOMMinBumpUp != nil { in, out := &in.OOMMinBumpUp, &out.OOMMinBumpUp - *out = new(float64) - **out = **in + x := (*in).DeepCopy() + *out = &x } return } diff --git a/vertical-pod-autoscaler/pkg/client/clientset/versioned/fake/clientset_generated.go b/vertical-pod-autoscaler/pkg/client/clientset/versioned/fake/clientset_generated.go index a8b101ba6b..a844d75136 100644 --- a/vertical-pod-autoscaler/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/vertical-pod-autoscaler/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -19,6 +19,7 @@ limitations under the License. package fake import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" clientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned" @@ -55,9 +56,13 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset { cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} cs.AddReactor("*", "*", testing.ObjectReaction(o)) cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + var opts metav1.ListOptions + if watchActcion, ok := action.(testing.WatchActionImpl); ok { + opts = watchActcion.ListOptions + } gvr := action.GetResource() ns := action.GetNamespace() - watch, err := o.Watch(gvr, ns) + watch, err := o.Watch(gvr, ns, opts) if err != nil { return false, nil, err } diff --git a/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1/autoscaling.k8s.io_client.go b/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1/autoscaling.k8s.io_client.go index 1ac1f8198c..f2932dc051 100644 --- a/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1/autoscaling.k8s.io_client.go +++ b/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1/autoscaling.k8s.io_client.go @@ -50,9 +50,7 @@ func (c *AutoscalingV1Client) VerticalPodAutoscalerCheckpoints(namespace string) // where httpClient was generated with rest.HTTPClientFor(c). func NewForConfig(c *rest.Config) (*AutoscalingV1Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) httpClient, err := rest.HTTPClientFor(&config) if err != nil { return nil, err @@ -64,9 +62,7 @@ func NewForConfig(c *rest.Config) (*AutoscalingV1Client, error) { // Note the http client provided takes precedence over the configured transport values. func NewForConfigAndClient(c *rest.Config, h *http.Client) (*AutoscalingV1Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) client, err := rest.RESTClientForConfigAndClient(&config, h) if err != nil { return nil, err @@ -89,7 +85,7 @@ func New(c rest.Interface) *AutoscalingV1Client { return &AutoscalingV1Client{c} } -func setConfigDefaults(config *rest.Config) error { +func setConfigDefaults(config *rest.Config) { gv := autoscalingk8siov1.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" @@ -98,8 +94,6 @@ func setConfigDefaults(config *rest.Config) error { if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() } - - return nil } // RESTClient returns a RESTClient that is used to communicate diff --git a/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1beta1/autoscaling.k8s.io_client.go b/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1beta1/autoscaling.k8s.io_client.go index 6b63e563d8..72ab4a4424 100644 --- a/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1beta1/autoscaling.k8s.io_client.go +++ b/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1beta1/autoscaling.k8s.io_client.go @@ -50,9 +50,7 @@ func (c *AutoscalingV1beta1Client) VerticalPodAutoscalerCheckpoints(namespace st // where httpClient was generated with rest.HTTPClientFor(c). func NewForConfig(c *rest.Config) (*AutoscalingV1beta1Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) httpClient, err := rest.HTTPClientFor(&config) if err != nil { return nil, err @@ -64,9 +62,7 @@ func NewForConfig(c *rest.Config) (*AutoscalingV1beta1Client, error) { // Note the http client provided takes precedence over the configured transport values. func NewForConfigAndClient(c *rest.Config, h *http.Client) (*AutoscalingV1beta1Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) client, err := rest.RESTClientForConfigAndClient(&config, h) if err != nil { return nil, err @@ -89,7 +85,7 @@ func New(c rest.Interface) *AutoscalingV1beta1Client { return &AutoscalingV1beta1Client{c} } -func setConfigDefaults(config *rest.Config) error { +func setConfigDefaults(config *rest.Config) { gv := autoscalingk8siov1beta1.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" @@ -98,8 +94,6 @@ func setConfigDefaults(config *rest.Config) error { if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() } - - return nil } // RESTClient returns a RESTClient that is used to communicate diff --git a/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1beta2/autoscaling.k8s.io_client.go b/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1beta2/autoscaling.k8s.io_client.go index dd548845f2..0ed2efcc49 100644 --- a/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1beta2/autoscaling.k8s.io_client.go +++ b/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/autoscaling.k8s.io/v1beta2/autoscaling.k8s.io_client.go @@ -50,9 +50,7 @@ func (c *AutoscalingV1beta2Client) VerticalPodAutoscalerCheckpoints(namespace st // where httpClient was generated with rest.HTTPClientFor(c). func NewForConfig(c *rest.Config) (*AutoscalingV1beta2Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) httpClient, err := rest.HTTPClientFor(&config) if err != nil { return nil, err @@ -64,9 +62,7 @@ func NewForConfig(c *rest.Config) (*AutoscalingV1beta2Client, error) { // Note the http client provided takes precedence over the configured transport values. func NewForConfigAndClient(c *rest.Config, h *http.Client) (*AutoscalingV1beta2Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) client, err := rest.RESTClientForConfigAndClient(&config, h) if err != nil { return nil, err @@ -89,7 +85,7 @@ func New(c rest.Interface) *AutoscalingV1beta2Client { return &AutoscalingV1beta2Client{c} } -func setConfigDefaults(config *rest.Config) error { +func setConfigDefaults(config *rest.Config) { gv := autoscalingk8siov1beta2.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" @@ -98,8 +94,6 @@ func setConfigDefaults(config *rest.Config) error { if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() } - - return nil } // RESTClient returns a RESTClient that is used to communicate diff --git a/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/poc.autoscaling.k8s.io/v1alpha1/poc.autoscaling.k8s.io_client.go b/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/poc.autoscaling.k8s.io/v1alpha1/poc.autoscaling.k8s.io_client.go index 23374e70aa..fc712073e8 100644 --- a/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/poc.autoscaling.k8s.io/v1alpha1/poc.autoscaling.k8s.io_client.go +++ b/vertical-pod-autoscaler/pkg/client/clientset/versioned/typed/poc.autoscaling.k8s.io/v1alpha1/poc.autoscaling.k8s.io_client.go @@ -50,9 +50,7 @@ func (c *PocV1alpha1Client) VerticalPodAutoscalerCheckpoints(namespace string) V // where httpClient was generated with rest.HTTPClientFor(c). func NewForConfig(c *rest.Config) (*PocV1alpha1Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) httpClient, err := rest.HTTPClientFor(&config) if err != nil { return nil, err @@ -64,9 +62,7 @@ func NewForConfig(c *rest.Config) (*PocV1alpha1Client, error) { // Note the http client provided takes precedence over the configured transport values. func NewForConfigAndClient(c *rest.Config, h *http.Client) (*PocV1alpha1Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) client, err := rest.RESTClientForConfigAndClient(&config, h) if err != nil { return nil, err @@ -89,7 +85,7 @@ func New(c rest.Interface) *PocV1alpha1Client { return &PocV1alpha1Client{c} } -func setConfigDefaults(config *rest.Config) error { +func setConfigDefaults(config *rest.Config) { gv := pocautoscalingk8siov1alpha1.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" @@ -98,8 +94,6 @@ func setConfigDefaults(config *rest.Config) error { if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() } - - return nil } // RESTClient returns a RESTClient that is used to communicate diff --git a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1/verticalpodautoscaler.go b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1/verticalpodautoscaler.go index 854eee4935..6bb96d1ca4 100644 --- a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1/verticalpodautoscaler.go +++ b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1/verticalpodautoscaler.go @@ -62,13 +62,25 @@ func NewFilteredVerticalPodAutoscalerInformer(client versioned.Interface, namesp if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1().VerticalPodAutoscalers(namespace).List(context.TODO(), options) + return client.AutoscalingV1().VerticalPodAutoscalers(namespace).List(context.Background(), options) }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1().VerticalPodAutoscalers(namespace).Watch(context.TODO(), options) + return client.AutoscalingV1().VerticalPodAutoscalers(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1().VerticalPodAutoscalers(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options metav1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1().VerticalPodAutoscalers(namespace).Watch(ctx, options) }, }, &apisautoscalingk8siov1.VerticalPodAutoscaler{}, diff --git a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1/verticalpodautoscalercheckpoint.go b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1/verticalpodautoscalercheckpoint.go index 5a6796aebf..19b6e4f4a3 100644 --- a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1/verticalpodautoscalercheckpoint.go +++ b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1/verticalpodautoscalercheckpoint.go @@ -62,13 +62,25 @@ func NewFilteredVerticalPodAutoscalerCheckpointInformer(client versioned.Interfa if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1().VerticalPodAutoscalerCheckpoints(namespace).List(context.TODO(), options) + return client.AutoscalingV1().VerticalPodAutoscalerCheckpoints(namespace).List(context.Background(), options) }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1().VerticalPodAutoscalerCheckpoints(namespace).Watch(context.TODO(), options) + return client.AutoscalingV1().VerticalPodAutoscalerCheckpoints(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1().VerticalPodAutoscalerCheckpoints(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options metav1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1().VerticalPodAutoscalerCheckpoints(namespace).Watch(ctx, options) }, }, &apisautoscalingk8siov1.VerticalPodAutoscalerCheckpoint{}, diff --git a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta1/verticalpodautoscaler.go b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta1/verticalpodautoscaler.go index 870dea95bb..d94f9146fe 100644 --- a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta1/verticalpodautoscaler.go +++ b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta1/verticalpodautoscaler.go @@ -62,13 +62,25 @@ func NewFilteredVerticalPodAutoscalerInformer(client versioned.Interface, namesp if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1beta1().VerticalPodAutoscalers(namespace).List(context.TODO(), options) + return client.AutoscalingV1beta1().VerticalPodAutoscalers(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1beta1().VerticalPodAutoscalers(namespace).Watch(context.TODO(), options) + return client.AutoscalingV1beta1().VerticalPodAutoscalers(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1beta1().VerticalPodAutoscalers(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1beta1().VerticalPodAutoscalers(namespace).Watch(ctx, options) }, }, &apisautoscalingk8siov1beta1.VerticalPodAutoscaler{}, diff --git a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta1/verticalpodautoscalercheckpoint.go b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta1/verticalpodautoscalercheckpoint.go index 73f3dbdbb1..82d4995ffe 100644 --- a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta1/verticalpodautoscalercheckpoint.go +++ b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta1/verticalpodautoscalercheckpoint.go @@ -62,13 +62,25 @@ func NewFilteredVerticalPodAutoscalerCheckpointInformer(client versioned.Interfa if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1beta1().VerticalPodAutoscalerCheckpoints(namespace).List(context.TODO(), options) + return client.AutoscalingV1beta1().VerticalPodAutoscalerCheckpoints(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1beta1().VerticalPodAutoscalerCheckpoints(namespace).Watch(context.TODO(), options) + return client.AutoscalingV1beta1().VerticalPodAutoscalerCheckpoints(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1beta1().VerticalPodAutoscalerCheckpoints(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1beta1().VerticalPodAutoscalerCheckpoints(namespace).Watch(ctx, options) }, }, &apisautoscalingk8siov1beta1.VerticalPodAutoscalerCheckpoint{}, diff --git a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta2/verticalpodautoscaler.go b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta2/verticalpodautoscaler.go index 2a92542b09..e2b1c93ab0 100644 --- a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta2/verticalpodautoscaler.go +++ b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta2/verticalpodautoscaler.go @@ -62,13 +62,25 @@ func NewFilteredVerticalPodAutoscalerInformer(client versioned.Interface, namesp if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1beta2().VerticalPodAutoscalers(namespace).List(context.TODO(), options) + return client.AutoscalingV1beta2().VerticalPodAutoscalers(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1beta2().VerticalPodAutoscalers(namespace).Watch(context.TODO(), options) + return client.AutoscalingV1beta2().VerticalPodAutoscalers(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1beta2().VerticalPodAutoscalers(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1beta2().VerticalPodAutoscalers(namespace).Watch(ctx, options) }, }, &apisautoscalingk8siov1beta2.VerticalPodAutoscaler{}, diff --git a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta2/verticalpodautoscalercheckpoint.go b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta2/verticalpodautoscalercheckpoint.go index 3dcb75699a..f2eb9c9ea7 100644 --- a/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta2/verticalpodautoscalercheckpoint.go +++ b/vertical-pod-autoscaler/pkg/client/informers/externalversions/autoscaling.k8s.io/v1beta2/verticalpodautoscalercheckpoint.go @@ -62,13 +62,25 @@ func NewFilteredVerticalPodAutoscalerCheckpointInformer(client versioned.Interfa if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1beta2().VerticalPodAutoscalerCheckpoints(namespace).List(context.TODO(), options) + return client.AutoscalingV1beta2().VerticalPodAutoscalerCheckpoints(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.AutoscalingV1beta2().VerticalPodAutoscalerCheckpoints(namespace).Watch(context.TODO(), options) + return client.AutoscalingV1beta2().VerticalPodAutoscalerCheckpoints(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1beta2().VerticalPodAutoscalerCheckpoints(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AutoscalingV1beta2().VerticalPodAutoscalerCheckpoints(namespace).Watch(ctx, options) }, }, &apisautoscalingk8siov1beta2.VerticalPodAutoscalerCheckpoint{}, diff --git a/vertical-pod-autoscaler/pkg/client/informers/externalversions/poc.autoscaling.k8s.io/v1alpha1/verticalpodautoscaler.go b/vertical-pod-autoscaler/pkg/client/informers/externalversions/poc.autoscaling.k8s.io/v1alpha1/verticalpodautoscaler.go index f3a8746dda..df5bd0dbd9 100644 --- a/vertical-pod-autoscaler/pkg/client/informers/externalversions/poc.autoscaling.k8s.io/v1alpha1/verticalpodautoscaler.go +++ b/vertical-pod-autoscaler/pkg/client/informers/externalversions/poc.autoscaling.k8s.io/v1alpha1/verticalpodautoscaler.go @@ -62,13 +62,25 @@ func NewFilteredVerticalPodAutoscalerInformer(client versioned.Interface, namesp if tweakListOptions != nil { tweakListOptions(&options) } - return client.PocV1alpha1().VerticalPodAutoscalers(namespace).List(context.TODO(), options) + return client.PocV1alpha1().VerticalPodAutoscalers(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.PocV1alpha1().VerticalPodAutoscalers(namespace).Watch(context.TODO(), options) + return client.PocV1alpha1().VerticalPodAutoscalers(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.PocV1alpha1().VerticalPodAutoscalers(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.PocV1alpha1().VerticalPodAutoscalers(namespace).Watch(ctx, options) }, }, &apispocautoscalingk8siov1alpha1.VerticalPodAutoscaler{}, diff --git a/vertical-pod-autoscaler/pkg/client/informers/externalversions/poc.autoscaling.k8s.io/v1alpha1/verticalpodautoscalercheckpoint.go b/vertical-pod-autoscaler/pkg/client/informers/externalversions/poc.autoscaling.k8s.io/v1alpha1/verticalpodautoscalercheckpoint.go index 92012987fc..eab9796af0 100644 --- a/vertical-pod-autoscaler/pkg/client/informers/externalversions/poc.autoscaling.k8s.io/v1alpha1/verticalpodautoscalercheckpoint.go +++ b/vertical-pod-autoscaler/pkg/client/informers/externalversions/poc.autoscaling.k8s.io/v1alpha1/verticalpodautoscalercheckpoint.go @@ -62,13 +62,25 @@ func NewFilteredVerticalPodAutoscalerCheckpointInformer(client versioned.Interfa if tweakListOptions != nil { tweakListOptions(&options) } - return client.PocV1alpha1().VerticalPodAutoscalerCheckpoints(namespace).List(context.TODO(), options) + return client.PocV1alpha1().VerticalPodAutoscalerCheckpoints(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.PocV1alpha1().VerticalPodAutoscalerCheckpoints(namespace).Watch(context.TODO(), options) + return client.PocV1alpha1().VerticalPodAutoscalerCheckpoints(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.PocV1alpha1().VerticalPodAutoscalerCheckpoints(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.PocV1alpha1().VerticalPodAutoscalerCheckpoints(namespace).Watch(ctx, options) }, }, &apispocautoscalingk8siov1alpha1.VerticalPodAutoscalerCheckpoint{}, diff --git a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go index 50dba61ccd..bc566e2b89 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go @@ -40,6 +40,7 @@ import ( "time" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" @@ -289,6 +290,13 @@ func (a *AggregateContainerState) isEmpty() bool { return a.TotalSamplesCount == 0 } +func (a *AggregateContainerState) convertQuantityToFloat64(quantity *resource.Quantity) float64 { + if quantity == nil { + return 0.0 + } + return float64(quantity.MilliValue()) / 1000.0 +} + // UpdateFromPolicy updates container state scaling mode and controlled resources based on resource // policy of the VPA object. func (a *AggregateContainerState) UpdateFromPolicy(resourcePolicy *vpa_types.ContainerResourcePolicy) { @@ -305,10 +313,10 @@ func (a *AggregateContainerState) UpdateFromPolicy(resourcePolicy *vpa_types.Con // Per VPA components - feature flag "PerVPAConfig" must be enabled if resourcePolicy != nil && resourcePolicy.OOMBumpUpRatio != nil && features.Enabled(features.PerVPAConfig) { - a.OOMBumpUpRatio = *resourcePolicy.OOMBumpUpRatio + a.OOMBumpUpRatio = a.convertQuantityToFloat64(resourcePolicy.OOMBumpUpRatio) } - if resourcePolicy != nil && resourcePolicy.OOMMinBumpUp != nil && features.Enabled(features.PerVPAConfig){ - a.OOMMinBumpUp = *resourcePolicy.OOMMinBumpUp + if resourcePolicy != nil && resourcePolicy.OOMMinBumpUp != nil && features.Enabled(features.PerVPAConfig) { + a.OOMMinBumpUp = a.convertQuantityToFloat64(resourcePolicy.OOMMinBumpUp) } } diff --git a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go index 0049ffe901..5a32bc0a20 100644 --- a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go +++ b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go @@ -21,6 +21,7 @@ import ( autoscaling "k8s.io/api/autoscaling/v1" core "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" meta "k8s.io/apimachinery/pkg/apis/meta/v1" vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" @@ -47,8 +48,8 @@ type VerticalPodAutoscalerBuilder interface { WithGroupVersion(gv meta.GroupVersion) VerticalPodAutoscalerBuilder WithEvictionRequirements([]*vpa_types.EvictionRequirement) VerticalPodAutoscalerBuilder WithMinReplicas(minReplicas *int32) VerticalPodAutoscalerBuilder - WithOOMBumpUpRatio(ratio float64) VerticalPodAutoscalerBuilder - WithOOMMinBumpUp(minBumpUp float64) VerticalPodAutoscalerBuilder + WithOOMBumpUpRatio(ratio *resource.Quantity) VerticalPodAutoscalerBuilder + WithOOMMinBumpUp(minBumpUp *resource.Quantity) VerticalPodAutoscalerBuilder AppendCondition(conditionType vpa_types.VerticalPodAutoscalerConditionType, status core.ConditionStatus, reason, message string, lastTransitionTime time.Time) VerticalPodAutoscalerBuilder AppendRecommendation(vpa_types.RecommendedContainerResources) VerticalPodAutoscalerBuilder @@ -89,8 +90,8 @@ type verticalPodAutoscalerBuilder struct { targetRef *autoscaling.CrossVersionObjectReference appendedRecommendations []vpa_types.RecommendedContainerResources recommender string - oomBumpUpRatio *float64 - oomMinBumpUp *float64 + oomBumpUpRatio *resource.Quantity + oomMinBumpUp *resource.Quantity } func (b *verticalPodAutoscalerBuilder) WithName(vpaName string) VerticalPodAutoscalerBuilder { @@ -218,15 +219,15 @@ func (b *verticalPodAutoscalerBuilder) WithMinReplicas(minReplicas *int32) Verti return &c } -func (b *verticalPodAutoscalerBuilder) WithOOMBumpUpRatio(ratio float64) VerticalPodAutoscalerBuilder { +func (b *verticalPodAutoscalerBuilder) WithOOMBumpUpRatio(ratio *resource.Quantity) VerticalPodAutoscalerBuilder { c := *b - c.oomBumpUpRatio = &ratio + c.oomBumpUpRatio = ratio return &c } -func (b *verticalPodAutoscalerBuilder) WithOOMMinBumpUp(minBumpUp float64) VerticalPodAutoscalerBuilder { +func (b *verticalPodAutoscalerBuilder) WithOOMMinBumpUp(minBumpUp *resource.Quantity) VerticalPodAutoscalerBuilder { c := *b - c.oomMinBumpUp = &minBumpUp + c.oomMinBumpUp = minBumpUp return &c } From 1679398adcb121a45f4467f73bd27e385b5212f5 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Thu, 31 Jul 2025 10:24:27 +0000 Subject: [PATCH 16/33] lint OOM -> Oom Signed-off-by: Omer Aplatony --- .../deploy/vpa-v1-crd-gen.yaml | 18 ++++++++++++------ .../resource/vpa/handler.go | 12 ++++++------ .../pkg/apis/autoscaling.k8s.io/v1/types.go | 8 ++++---- .../v1/zz_generated.deepcopy.go | 8 ++++---- .../model/aggregate_container_state.go | 8 ++++---- .../pkg/utils/test/test_vpa.go | 4 ++-- 6 files changed, 32 insertions(+), 26 deletions(-) diff --git a/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml b/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml index 34c50f7c5e..5004d48634 100644 --- a/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml +++ b/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml @@ -4,7 +4,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes/kubernetes/pull/63797 - controller-gen.kubebuilder.io/version: v0.17.2 + controller-gen.kubebuilder.io/version: v0.18.0 name: verticalpodautoscalercheckpoints.autoscaling.k8s.io spec: group: autoscaling.k8s.io @@ -225,7 +225,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes/kubernetes/pull/63797 - controller-gen.kubebuilder.io/version: v0.17.2 + controller-gen.kubebuilder.io/version: v0.18.0 name: verticalpodautoscalers.autoscaling.k8s.io spec: group: autoscaling.k8s.io @@ -373,15 +373,21 @@ spec: - "Off" type: string oomBumpUpRatio: + anyOf: + - type: integer + - type: string description: OOMBumpUpRatio is the ratio to increase resources when OOM is detected. - minimum: 1 - type: number + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true oomMinBumpUp: + anyOf: + - type: integer + - type: string description: OOMMinBumpUp is the minimum increase in resources when OOM is detected. - minimum: 0 - type: number + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true type: object type: array type: object diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go index a2202c17aa..077bf2d63e 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go @@ -139,18 +139,18 @@ func ValidateVPA(vpa *vpa_types.VerticalPodAutoscaler, isCreate bool) error { } // Validate OOMBumpUpRatio - if policy.OOMBumpUpRatio != nil { - ratio := float64(policy.OOMBumpUpRatio.MilliValue()) / 1000.0 + if policy.OomBumpUpRatio != nil { + ratio := float64(policy.OomBumpUpRatio.MilliValue()) / 1000.0 if ratio <= 1.0 { - return fmt.Errorf("OOMBumpUpRatio must be greater than 1.0, got %v", ratio) + return fmt.Errorf("OomBumpUpRatio must be greater than 1.0, got %v", ratio) } } // Validate OOMMinBumpUp - if policy.OOMMinBumpUp != nil { - minBump := policy.OOMMinBumpUp.Value() + if policy.OomMinBumpUp != nil { + minBump := policy.OomMinBumpUp.Value() if minBump <= 0 { - return fmt.Errorf("OOMMinBumpUp must be greater than 0, got %v bytes", minBump) + return fmt.Errorf("OomMinBumpUp must be greater than 0, got %v bytes", minBump) } } diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go index cc35946321..9dd6167229 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go @@ -223,13 +223,13 @@ type ContainerResourcePolicy struct { // +optional ControlledValues *ContainerControlledValues `json:"controlledValues,omitempty" protobuf:"bytes,6,rep,name=controlledValues"` - // OOMBumpUpRatio is the ratio to increase resources when OOM is detected. + // oomBumpUpRatio is the ratio to increase resources when OOM is detected. // +optional - OOMBumpUpRatio *resource.Quantity `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,7,opt,name=oomBumpUpRatio"` + OomBumpUpRatio *resource.Quantity `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,7,opt,name=oomBumpUpRatio"` - // OOMMinBumpUp is the minimum increase in resources when OOM is detected. + // OomMinBumpUp is the minimum increase in resources when OOM is detected. // +optional - OOMMinBumpUp *resource.Quantity `json:"oomMinBumpUp,omitempty" protobuf:"bytes,8,opt,name=oomMinBumpUp"` + OomMinBumpUp *resource.Quantity `json:"oomMinBumpUp,omitempty" protobuf:"bytes,8,opt,name=oomMinBumpUp"` } const ( diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go index 86ff44f69e..8a802e3209 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go @@ -63,13 +63,13 @@ func (in *ContainerResourcePolicy) DeepCopyInto(out *ContainerResourcePolicy) { *out = new(ContainerControlledValues) **out = **in } - if in.OOMBumpUpRatio != nil { - in, out := &in.OOMBumpUpRatio, &out.OOMBumpUpRatio + if in.OomBumpUpRatio != nil { + in, out := &in.OomBumpUpRatio, &out.OomBumpUpRatio x := (*in).DeepCopy() *out = &x } - if in.OOMMinBumpUp != nil { - in, out := &in.OOMMinBumpUp, &out.OOMMinBumpUp + if in.OomMinBumpUp != nil { + in, out := &in.OomMinBumpUp, &out.OomMinBumpUp x := (*in).DeepCopy() *out = &x } diff --git a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go index bc566e2b89..490e341da1 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go @@ -312,11 +312,11 @@ func (a *AggregateContainerState) UpdateFromPolicy(resourcePolicy *vpa_types.Con } // Per VPA components - feature flag "PerVPAConfig" must be enabled - if resourcePolicy != nil && resourcePolicy.OOMBumpUpRatio != nil && features.Enabled(features.PerVPAConfig) { - a.OOMBumpUpRatio = a.convertQuantityToFloat64(resourcePolicy.OOMBumpUpRatio) + if resourcePolicy != nil && resourcePolicy.OomBumpUpRatio != nil && features.Enabled(features.PerVPAConfig) { + a.OOMBumpUpRatio = a.convertQuantityToFloat64(resourcePolicy.OomBumpUpRatio) } - if resourcePolicy != nil && resourcePolicy.OOMMinBumpUp != nil && features.Enabled(features.PerVPAConfig) { - a.OOMMinBumpUp = a.convertQuantityToFloat64(resourcePolicy.OOMMinBumpUp) + if resourcePolicy != nil && resourcePolicy.OomMinBumpUp != nil && features.Enabled(features.PerVPAConfig) { + a.OOMMinBumpUp = a.convertQuantityToFloat64(resourcePolicy.OomMinBumpUp) } } diff --git a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go index 5a32bc0a20..92593f62f1 100644 --- a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go +++ b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go @@ -267,8 +267,8 @@ func (b *verticalPodAutoscalerBuilder) Get() *vpa_types.VerticalPodAutoscaler { MaxAllowed: b.maxAllowed[containerName], ControlledValues: b.controlledValues[containerName], Mode: &scalingModeAuto, - OOMBumpUpRatio: b.oomBumpUpRatio, - OOMMinBumpUp: b.oomMinBumpUp, + OomBumpUpRatio: b.oomBumpUpRatio, + OomMinBumpUp: b.oomMinBumpUp, } if scalingMode, ok := b.scalingMode[containerName]; ok { containerResourcePolicy.Mode = scalingMode From 87f8bd6c61286ff924e8d0ce5ec2de27da8eb11d Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Thu, 31 Jul 2025 11:07:23 +0000 Subject: [PATCH 17/33] fmt & fix e2e Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/e2e/v1/full_vpa.go | 2 +- vertical-pod-autoscaler/e2e/v1/recommender.go | 5 +++-- vertical-pod-autoscaler/pkg/features/features.go | 1 - vertical-pod-autoscaler/pkg/features/versioned_features.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/vertical-pod-autoscaler/e2e/v1/full_vpa.go b/vertical-pod-autoscaler/e2e/v1/full_vpa.go index 7f44b04e56..ec1467f58a 100644 --- a/vertical-pod-autoscaler/e2e/v1/full_vpa.go +++ b/vertical-pod-autoscaler/e2e/v1/full_vpa.go @@ -433,4 +433,4 @@ func waitForResourceRequestInRangeInPods(f *framework.Framework, timeout time.Du return fmt.Errorf("error waiting for %s request in range of (%v,%v) for pods: %+v", resourceName, lowerBound, upperBound, listOptions) } return nil -} \ No newline at end of file +} diff --git a/vertical-pod-autoscaler/e2e/v1/recommender.go b/vertical-pod-autoscaler/e2e/v1/recommender.go index bf621ed3d6..70542351b1 100644 --- a/vertical-pod-autoscaler/e2e/v1/recommender.go +++ b/vertical-pod-autoscaler/e2e/v1/recommender.go @@ -24,6 +24,7 @@ import ( autoscaling "k8s.io/api/autoscaling/v1" apiv1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" @@ -446,7 +447,7 @@ var _ = RecommenderE2eDescribe("OOM with custom config", func() { WithNamespace(f.Namespace.Name). WithTargetRef(targetRef). WithContainer(containerName). - WithOOMBumpUpRatio(2). + WithOOMBumpUpRatio(resource.NewQuantity(2, resource.DecimalSI)). Get() InstallVPA(f, vpaCRD) }) @@ -465,7 +466,7 @@ var _ = RecommenderE2eDescribe("OOM with custom config", func() { gomega.Expect(vpa.Status.Recommendation.ContainerRecommendations).Should(gomega.HaveLen(1)) currentMemory := vpa.Status.Recommendation.ContainerRecommendations[0].Target.Memory().Value() - oomReplicationControllerRequestLimit := int64(1024 * 1024 * 1024) // from runOomingReplicationController + oomReplicationControllerRequestLimit := int64(1024 * 1024 * 1024) // from runOomingReplicationController defaultBumpMemory := float64(oomReplicationControllerRequestLimit) * 1.2 // DefaultOOMBumpUpRatio customBumpMemory := float64(oomReplicationControllerRequestLimit) * 2.0 // Custom ratio from VPA config diff --git a/vertical-pod-autoscaler/pkg/features/features.go b/vertical-pod-autoscaler/pkg/features/features.go index 0d8ee7bcff..908dfe80dc 100644 --- a/vertical-pod-autoscaler/pkg/features/features.go +++ b/vertical-pod-autoscaler/pkg/features/features.go @@ -54,7 +54,6 @@ const ( // optimization strategies to be applied to different workloads within the // same cluster. PerVPAConfig featuregate.Feature = "PerVPAConfig" - ) // MutableFeatureGate is a mutable, versioned, global FeatureGate. diff --git a/vertical-pod-autoscaler/pkg/features/versioned_features.go b/vertical-pod-autoscaler/pkg/features/versioned_features.go index 65bc404b6a..32e4a57d4d 100644 --- a/vertical-pod-autoscaler/pkg/features/versioned_features.go +++ b/vertical-pod-autoscaler/pkg/features/versioned_features.go @@ -30,7 +30,7 @@ var defaultVersionedFeatureGates = map[featuregate.Feature]featuregate.Versioned InPlaceOrRecreate: { {Version: version.MustParse("1.4"), Default: false, PreRelease: featuregate.Alpha}, }, - PerVPAConfig : { + PerVPAConfig: { {Version: version.MustParse("1.5"), Default: true, PreRelease: featuregate.Alpha}, }, } From 52110a7fa7551df4652cf9ebe300e092b0d23c8a Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Thu, 31 Jul 2025 13:33:43 +0000 Subject: [PATCH 18/33] OomBumpUpRatio->OOMBumpRatio Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/e2e/v1/recommender.go | 2 +- .../pkg/admission-controller/resource/vpa/handler.go | 12 ++++++------ .../pkg/apis/autoscaling.k8s.io/v1/types.go | 6 +++--- .../autoscaling.k8s.io/v1/zz_generated.deepcopy.go | 8 ++++---- .../recommender/model/aggregate_container_state.go | 8 ++++---- vertical-pod-autoscaler/pkg/utils/test/test_vpa.go | 12 ++++++------ 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/vertical-pod-autoscaler/e2e/v1/recommender.go b/vertical-pod-autoscaler/e2e/v1/recommender.go index 70542351b1..2ff550d55a 100644 --- a/vertical-pod-autoscaler/e2e/v1/recommender.go +++ b/vertical-pod-autoscaler/e2e/v1/recommender.go @@ -447,7 +447,7 @@ var _ = RecommenderE2eDescribe("OOM with custom config", func() { WithNamespace(f.Namespace.Name). WithTargetRef(targetRef). WithContainer(containerName). - WithOOMBumpUpRatio(resource.NewQuantity(2, resource.DecimalSI)). + WithOOMBumpRatio(resource.NewQuantity(2, resource.DecimalSI)). Get() InstallVPA(f, vpaCRD) }) diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go index 077bf2d63e..848691ae27 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go @@ -138,17 +138,17 @@ func ValidateVPA(vpa *vpa_types.VerticalPodAutoscaler, isCreate bool) error { return fmt.Errorf("ContainerPolicies.ContainerName is required") } - // Validate OOMBumpUpRatio - if policy.OomBumpUpRatio != nil { - ratio := float64(policy.OomBumpUpRatio.MilliValue()) / 1000.0 + // Validate OOMBumpRatio + if policy.OOMBumpRatio != nil { + ratio := float64(policy.OOMBumpRatio.MilliValue()) / 1000.0 if ratio <= 1.0 { - return fmt.Errorf("OomBumpUpRatio must be greater than 1.0, got %v", ratio) + return fmt.Errorf("OOMBumpRatio must be greater than 1.0, got %v", ratio) } } // Validate OOMMinBumpUp - if policy.OomMinBumpUp != nil { - minBump := policy.OomMinBumpUp.Value() + if policy.OOMMinBumpUp != nil { + minBump := policy.OOMMinBumpUp.Value() if minBump <= 0 { return fmt.Errorf("OomMinBumpUp must be greater than 0, got %v bytes", minBump) } diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go index 9dd6167229..94fcfd0356 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go @@ -225,11 +225,11 @@ type ContainerResourcePolicy struct { // oomBumpUpRatio is the ratio to increase resources when OOM is detected. // +optional - OomBumpUpRatio *resource.Quantity `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,7,opt,name=oomBumpUpRatio"` + OOMBumpRatio *resource.Quantity `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,7,opt,name=oomBumpUpRatio"` - // OomMinBumpUp is the minimum increase in resources when OOM is detected. + // oomMinBumpUp is the minimum increase in resources when OOM is detected. // +optional - OomMinBumpUp *resource.Quantity `json:"oomMinBumpUp,omitempty" protobuf:"bytes,8,opt,name=oomMinBumpUp"` + OOMMinBumpUp *resource.Quantity `json:"oomMinBumpUp,omitempty" protobuf:"bytes,8,opt,name=oomMinBumpUp"` } const ( diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go index 8a802e3209..89d63edc4f 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go @@ -63,13 +63,13 @@ func (in *ContainerResourcePolicy) DeepCopyInto(out *ContainerResourcePolicy) { *out = new(ContainerControlledValues) **out = **in } - if in.OomBumpUpRatio != nil { - in, out := &in.OomBumpUpRatio, &out.OomBumpUpRatio + if in.OOMBumpRatio != nil { + in, out := &in.OOMBumpRatio, &out.OOMBumpRatio x := (*in).DeepCopy() *out = &x } - if in.OomMinBumpUp != nil { - in, out := &in.OomMinBumpUp, &out.OomMinBumpUp + if in.OOMMinBumpUp != nil { + in, out := &in.OOMMinBumpUp, &out.OOMMinBumpUp x := (*in).DeepCopy() *out = &x } diff --git a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go index 490e341da1..3c01504f90 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go @@ -312,11 +312,11 @@ func (a *AggregateContainerState) UpdateFromPolicy(resourcePolicy *vpa_types.Con } // Per VPA components - feature flag "PerVPAConfig" must be enabled - if resourcePolicy != nil && resourcePolicy.OomBumpUpRatio != nil && features.Enabled(features.PerVPAConfig) { - a.OOMBumpUpRatio = a.convertQuantityToFloat64(resourcePolicy.OomBumpUpRatio) + if resourcePolicy != nil && resourcePolicy.OOMBumpRatio != nil && features.Enabled(features.PerVPAConfig) { + a.OOMBumpUpRatio = a.convertQuantityToFloat64(resourcePolicy.OOMBumpRatio) } - if resourcePolicy != nil && resourcePolicy.OomMinBumpUp != nil && features.Enabled(features.PerVPAConfig) { - a.OOMMinBumpUp = a.convertQuantityToFloat64(resourcePolicy.OomMinBumpUp) + if resourcePolicy != nil && resourcePolicy.OOMMinBumpUp != nil && features.Enabled(features.PerVPAConfig) { + a.OOMMinBumpUp = a.convertQuantityToFloat64(resourcePolicy.OOMMinBumpUp) } } diff --git a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go index 92593f62f1..c8a2f14f5f 100644 --- a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go +++ b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go @@ -48,7 +48,7 @@ type VerticalPodAutoscalerBuilder interface { WithGroupVersion(gv meta.GroupVersion) VerticalPodAutoscalerBuilder WithEvictionRequirements([]*vpa_types.EvictionRequirement) VerticalPodAutoscalerBuilder WithMinReplicas(minReplicas *int32) VerticalPodAutoscalerBuilder - WithOOMBumpUpRatio(ratio *resource.Quantity) VerticalPodAutoscalerBuilder + WithOOMBumpRatio(ratio *resource.Quantity) VerticalPodAutoscalerBuilder WithOOMMinBumpUp(minBumpUp *resource.Quantity) VerticalPodAutoscalerBuilder AppendCondition(conditionType vpa_types.VerticalPodAutoscalerConditionType, status core.ConditionStatus, reason, message string, lastTransitionTime time.Time) VerticalPodAutoscalerBuilder @@ -90,7 +90,7 @@ type verticalPodAutoscalerBuilder struct { targetRef *autoscaling.CrossVersionObjectReference appendedRecommendations []vpa_types.RecommendedContainerResources recommender string - oomBumpUpRatio *resource.Quantity + oomBumpRatio *resource.Quantity oomMinBumpUp *resource.Quantity } @@ -219,9 +219,9 @@ func (b *verticalPodAutoscalerBuilder) WithMinReplicas(minReplicas *int32) Verti return &c } -func (b *verticalPodAutoscalerBuilder) WithOOMBumpUpRatio(ratio *resource.Quantity) VerticalPodAutoscalerBuilder { +func (b *verticalPodAutoscalerBuilder) WithOOMBumpRatio(ratio *resource.Quantity) VerticalPodAutoscalerBuilder { c := *b - c.oomBumpUpRatio = ratio + c.oomBumpRatio = ratio return &c } @@ -267,8 +267,8 @@ func (b *verticalPodAutoscalerBuilder) Get() *vpa_types.VerticalPodAutoscaler { MaxAllowed: b.maxAllowed[containerName], ControlledValues: b.controlledValues[containerName], Mode: &scalingModeAuto, - OomBumpUpRatio: b.oomBumpUpRatio, - OomMinBumpUp: b.oomMinBumpUp, + OOMBumpRatio: b.oomBumpRatio, + OOMMinBumpUp: b.oomMinBumpUp, } if scalingMode, ok := b.scalingMode[containerName]; ok { containerResourcePolicy.Mode = scalingMode From 6dbfc45866974095f1533463fb0d884591be6745 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Fri, 1 Aug 2025 10:57:40 +0000 Subject: [PATCH 19/33] OOMBumpRatio -> OOMBumpUpRatio Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/docs/flags.md | 6 +++--- .../pkg/admission-controller/resource/vpa/handler.go | 8 ++++---- .../pkg/apis/autoscaling.k8s.io/v1/types.go | 4 ++-- .../autoscaling.k8s.io/v1/zz_generated.deepcopy.go | 4 ++-- .../pkg/recommender/model/aggregate_container_state.go | 4 ++-- vertical-pod-autoscaler/pkg/utils/test/test_vpa.go | 10 +++++----- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/vertical-pod-autoscaler/docs/flags.md b/vertical-pod-autoscaler/docs/flags.md index 6448def6d9..23623fcc86 100644 --- a/vertical-pod-autoscaler/docs/flags.md +++ b/vertical-pod-autoscaler/docs/flags.md @@ -14,7 +14,7 @@ This document is auto-generated from the flag definitions in the VPA admission-c | `address` | string | ":8944" | The address to expose Prometheus metrics. | | `alsologtostderr` | | | log to standard error as well as files (no effect when -logtostderr=true) | | `client-ca-file` | string | "/etc/tls-certs/caCert.pem" | Path to CA PEM file. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=false) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=true) | | `ignored-vpa-object-namespaces` | string | | A comma-separated list of namespaces to ignore when searching for VPA objects. Leave empty to avoid ignoring any namespaces. These namespaces will not be cleaned by the garbage collector. | | `kube-api-burst` | float | 100 | QPS burst limit when making requests to Kubernetes apiserver | | `kube-api-qps` | float | 50 | QPS limit when making requests to Kubernetes apiserver | @@ -68,7 +68,7 @@ This document is auto-generated from the flag definitions in the VPA recommender | `cpu-integer-post-processor-enabled` | | | Enable the cpu-integer recommendation post processor. The post processor will round up CPU recommendations to a whole CPU for pods which were opted in by setting an appropriate label on VPA object (experimental) | | `external-metrics-cpu-metric` | string | | ALPHA. Metric to use with external metrics provider for CPU usage. | | `external-metrics-memory-metric` | string | | ALPHA. Metric to use with external metrics provider for memory usage. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=false) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=true) | | `history-length` | string | "8d" | How much time back prometheus have to be queried to get historical metrics | | `history-resolution` | string | "1h" | Resolution at which Prometheus is queried for historical metrics | | `humanize-memory` | | | Convert memory values in recommendations to the highest appropriate SI unit with up to 2 decimal places for better readability. | @@ -140,7 +140,7 @@ This document is auto-generated from the flag definitions in the VPA updater cod | `eviction-rate-burst` | int | 1 | Burst of pods that can be evicted. | | `eviction-rate-limit` | float | | Number of pods that can be evicted per seconds. A rate limit set to 0 or -1 will disable
the rate limiter. (default -1) | | `eviction-tolerance` | float | 0.5 | Fraction of replica count that can be evicted for update, if more than one pod can be evicted. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=false) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=true) | | `ignored-vpa-object-namespaces` | string | | A comma-separated list of namespaces to ignore when searching for VPA objects. Leave empty to avoid ignoring any namespaces. These namespaces will not be cleaned by the garbage collector. | | `in-recommendation-bounds-eviction-lifetime-threshold` | | 12h0m0s | duration Pods that live for at least that long can be evicted even if their request is within the [MinRecommended...MaxRecommended] range | | `kube-api-burst` | float | 100 | QPS burst limit when making requests to Kubernetes apiserver | diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go index 848691ae27..b213798db9 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go @@ -138,11 +138,11 @@ func ValidateVPA(vpa *vpa_types.VerticalPodAutoscaler, isCreate bool) error { return fmt.Errorf("ContainerPolicies.ContainerName is required") } - // Validate OOMBumpRatio - if policy.OOMBumpRatio != nil { - ratio := float64(policy.OOMBumpRatio.MilliValue()) / 1000.0 + // Validate OOMBumpUpRatio + if policy.OOMBumpUpRatio != nil { + ratio := float64(policy.OOMBumpUpRatio.MilliValue()) / 1000.0 if ratio <= 1.0 { - return fmt.Errorf("OOMBumpRatio must be greater than 1.0, got %v", ratio) + return fmt.Errorf("OOMBumpUpRatio must be greater than 1.0, got %v", ratio) } } diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go index 94fcfd0356..a2e3ba7c67 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go @@ -223,9 +223,9 @@ type ContainerResourcePolicy struct { // +optional ControlledValues *ContainerControlledValues `json:"controlledValues,omitempty" protobuf:"bytes,6,rep,name=controlledValues"` - // oomBumpUpRatio is the ratio to increase resources when OOM is detected. + // OOMBumpUpRatio is the ratio to increase resources when OOM is detected. // +optional - OOMBumpRatio *resource.Quantity `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,7,opt,name=oomBumpUpRatio"` + OOMBumpUpRatio *resource.Quantity `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,7,opt,name=oomBumpUpRatio"` // oomMinBumpUp is the minimum increase in resources when OOM is detected. // +optional diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go index 89d63edc4f..86ff44f69e 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go @@ -63,8 +63,8 @@ func (in *ContainerResourcePolicy) DeepCopyInto(out *ContainerResourcePolicy) { *out = new(ContainerControlledValues) **out = **in } - if in.OOMBumpRatio != nil { - in, out := &in.OOMBumpRatio, &out.OOMBumpRatio + if in.OOMBumpUpRatio != nil { + in, out := &in.OOMBumpUpRatio, &out.OOMBumpUpRatio x := (*in).DeepCopy() *out = &x } diff --git a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go index 3c01504f90..bc566e2b89 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go @@ -312,8 +312,8 @@ func (a *AggregateContainerState) UpdateFromPolicy(resourcePolicy *vpa_types.Con } // Per VPA components - feature flag "PerVPAConfig" must be enabled - if resourcePolicy != nil && resourcePolicy.OOMBumpRatio != nil && features.Enabled(features.PerVPAConfig) { - a.OOMBumpUpRatio = a.convertQuantityToFloat64(resourcePolicy.OOMBumpRatio) + if resourcePolicy != nil && resourcePolicy.OOMBumpUpRatio != nil && features.Enabled(features.PerVPAConfig) { + a.OOMBumpUpRatio = a.convertQuantityToFloat64(resourcePolicy.OOMBumpUpRatio) } if resourcePolicy != nil && resourcePolicy.OOMMinBumpUp != nil && features.Enabled(features.PerVPAConfig) { a.OOMMinBumpUp = a.convertQuantityToFloat64(resourcePolicy.OOMMinBumpUp) diff --git a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go index c8a2f14f5f..5a32bc0a20 100644 --- a/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go +++ b/vertical-pod-autoscaler/pkg/utils/test/test_vpa.go @@ -48,7 +48,7 @@ type VerticalPodAutoscalerBuilder interface { WithGroupVersion(gv meta.GroupVersion) VerticalPodAutoscalerBuilder WithEvictionRequirements([]*vpa_types.EvictionRequirement) VerticalPodAutoscalerBuilder WithMinReplicas(minReplicas *int32) VerticalPodAutoscalerBuilder - WithOOMBumpRatio(ratio *resource.Quantity) VerticalPodAutoscalerBuilder + WithOOMBumpUpRatio(ratio *resource.Quantity) VerticalPodAutoscalerBuilder WithOOMMinBumpUp(minBumpUp *resource.Quantity) VerticalPodAutoscalerBuilder AppendCondition(conditionType vpa_types.VerticalPodAutoscalerConditionType, status core.ConditionStatus, reason, message string, lastTransitionTime time.Time) VerticalPodAutoscalerBuilder @@ -90,7 +90,7 @@ type verticalPodAutoscalerBuilder struct { targetRef *autoscaling.CrossVersionObjectReference appendedRecommendations []vpa_types.RecommendedContainerResources recommender string - oomBumpRatio *resource.Quantity + oomBumpUpRatio *resource.Quantity oomMinBumpUp *resource.Quantity } @@ -219,9 +219,9 @@ func (b *verticalPodAutoscalerBuilder) WithMinReplicas(minReplicas *int32) Verti return &c } -func (b *verticalPodAutoscalerBuilder) WithOOMBumpRatio(ratio *resource.Quantity) VerticalPodAutoscalerBuilder { +func (b *verticalPodAutoscalerBuilder) WithOOMBumpUpRatio(ratio *resource.Quantity) VerticalPodAutoscalerBuilder { c := *b - c.oomBumpRatio = ratio + c.oomBumpUpRatio = ratio return &c } @@ -267,7 +267,7 @@ func (b *verticalPodAutoscalerBuilder) Get() *vpa_types.VerticalPodAutoscaler { MaxAllowed: b.maxAllowed[containerName], ControlledValues: b.controlledValues[containerName], Mode: &scalingModeAuto, - OOMBumpRatio: b.oomBumpRatio, + OOMBumpUpRatio: b.oomBumpUpRatio, OOMMinBumpUp: b.oomMinBumpUp, } if scalingMode, ok := b.scalingMode[containerName]; ok { From bdfdc9f6ebbcea55811f3a01810cc00a27efbecf Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Fri, 1 Aug 2025 11:31:22 +0000 Subject: [PATCH 20/33] fixed typo Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/e2e/v1/recommender.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vertical-pod-autoscaler/e2e/v1/recommender.go b/vertical-pod-autoscaler/e2e/v1/recommender.go index 2ff550d55a..70542351b1 100644 --- a/vertical-pod-autoscaler/e2e/v1/recommender.go +++ b/vertical-pod-autoscaler/e2e/v1/recommender.go @@ -447,7 +447,7 @@ var _ = RecommenderE2eDescribe("OOM with custom config", func() { WithNamespace(f.Namespace.Name). WithTargetRef(targetRef). WithContainer(containerName). - WithOOMBumpRatio(resource.NewQuantity(2, resource.DecimalSI)). + WithOOMBumpUpRatio(resource.NewQuantity(2, resource.DecimalSI)). Get() InstallVPA(f, vpaCRD) }) From 58aec440cbeb2242fd7759d0eeaa91465be932b7 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Mon, 4 Aug 2025 21:23:01 +0300 Subject: [PATCH 21/33] fixed comment Co-authored-by: Adrian Moisey --- vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go index a2e3ba7c67..3e26e6d732 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go @@ -223,7 +223,7 @@ type ContainerResourcePolicy struct { // +optional ControlledValues *ContainerControlledValues `json:"controlledValues,omitempty" protobuf:"bytes,6,rep,name=controlledValues"` - // OOMBumpUpRatio is the ratio to increase resources when OOM is detected. + // oomBumpUpRatio is the ratio to increase resources when OOM is detected. // +optional OOMBumpUpRatio *resource.Quantity `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,7,opt,name=oomBumpUpRatio"` From fa913df09f25f4eefc66bf77085b3f5aafab40cd Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Tue, 5 Aug 2025 09:37:01 +0300 Subject: [PATCH 22/33] fixed typo Co-authored-by: Adrian Moisey --- .../pkg/admission-controller/resource/vpa/handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go index b213798db9..a2bf6c3ee8 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go @@ -149,8 +149,8 @@ func ValidateVPA(vpa *vpa_types.VerticalPodAutoscaler, isCreate bool) error { // Validate OOMMinBumpUp if policy.OOMMinBumpUp != nil { minBump := policy.OOMMinBumpUp.Value() - if minBump <= 0 { - return fmt.Errorf("OomMinBumpUp must be greater than 0, got %v bytes", minBump) + if minBump < 0 { + return fmt.Errorf("OOMMinBumpUp must be greater than or equal to 0, got %v bytes", minBump) } } From 7ebd1d033b5c969ba970434f9a2e811340a1b96b Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Tue, 5 Aug 2025 09:37:29 +0300 Subject: [PATCH 23/33] fixed comment Co-authored-by: Adrian Moisey --- .../pkg/apis/autoscaling.k8s.io/v1/types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go index 3e26e6d732..7d6414563d 100644 --- a/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go +++ b/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go @@ -223,11 +223,11 @@ type ContainerResourcePolicy struct { // +optional ControlledValues *ContainerControlledValues `json:"controlledValues,omitempty" protobuf:"bytes,6,rep,name=controlledValues"` - // oomBumpUpRatio is the ratio to increase resources when OOM is detected. + // oomBumpUpRatio is the ratio to increase memory when OOM is detected. // +optional OOMBumpUpRatio *resource.Quantity `json:"oomBumpUpRatio,omitempty" protobuf:"bytes,7,opt,name=oomBumpUpRatio"` - // oomMinBumpUp is the minimum increase in resources when OOM is detected. + // oomMinBumpUp is the minimum increase in memory when OOM is detected. // +optional OOMMinBumpUp *resource.Quantity `json:"oomMinBumpUp,omitempty" protobuf:"bytes,8,opt,name=oomMinBumpUp"` } From 561c1b2d98f7e38386a325c5305f24fd51c375b0 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Tue, 5 Aug 2025 06:52:25 +0000 Subject: [PATCH 24/33] changed controller-gen version Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml b/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml index 5004d48634..3f98413f7f 100644 --- a/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml +++ b/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml @@ -4,7 +4,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes/kubernetes/pull/63797 - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: verticalpodautoscalercheckpoints.autoscaling.k8s.io spec: group: autoscaling.k8s.io @@ -225,7 +225,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes/kubernetes/pull/63797 - controller-gen.kubebuilder.io/version: v0.18.0 + controller-gen.kubebuilder.io/version: v0.16.5 name: verticalpodautoscalers.autoscaling.k8s.io spec: group: autoscaling.k8s.io @@ -376,7 +376,7 @@ spec: anyOf: - type: integer - type: string - description: OOMBumpUpRatio is the ratio to increase resources + description: oomBumpUpRatio is the ratio to increase memory when OOM is detected. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true @@ -384,7 +384,7 @@ spec: anyOf: - type: integer - type: string - description: OOMMinBumpUp is the minimum increase in resources + description: oomMinBumpUp is the minimum increase in memory when OOM is detected. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true From c04e788b8d2365ddb758564bd9c75ca6f9f423aa Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Wed, 6 Aug 2025 19:14:30 +0300 Subject: [PATCH 25/33] fixed error message Co-authored-by: Adrian Moisey --- .../pkg/admission-controller/resource/vpa/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go index a2bf6c3ee8..35e3708272 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go @@ -150,7 +150,7 @@ func ValidateVPA(vpa *vpa_types.VerticalPodAutoscaler, isCreate bool) error { if policy.OOMMinBumpUp != nil { minBump := policy.OOMMinBumpUp.Value() if minBump < 0 { - return fmt.Errorf("OOMMinBumpUp must be greater than or equal to 0, got %v bytes", minBump) + return fmt.Errorf("OOMMinBumpUp must be greater than or equal to 0, got %v bytes", minBump) } } From b73a5b5c411a5ece5e0d38bf7fbce7f4bde1f69e Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Wed, 6 Aug 2025 16:36:48 +0000 Subject: [PATCH 26/33] Fixed feature flag default Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/pkg/features/features.go | 3 ++- vertical-pod-autoscaler/pkg/features/versioned_features.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/vertical-pod-autoscaler/pkg/features/features.go b/vertical-pod-autoscaler/pkg/features/features.go index 908dfe80dc..438e56ead1 100644 --- a/vertical-pod-autoscaler/pkg/features/features.go +++ b/vertical-pod-autoscaler/pkg/features/features.go @@ -48,7 +48,8 @@ const ( InPlaceOrRecreate featuregate.Feature = "InPlaceOrRecreate" // alpha: v1.5.0 - // components: admission-controller, recommender, updater + // components: recommender + // PerVPAConfig enables the ability to specify component-specific configuration // parameters at the individual VPA object level. This allows for different // optimization strategies to be applied to different workloads within the diff --git a/vertical-pod-autoscaler/pkg/features/versioned_features.go b/vertical-pod-autoscaler/pkg/features/versioned_features.go index 32e4a57d4d..8edc036b92 100644 --- a/vertical-pod-autoscaler/pkg/features/versioned_features.go +++ b/vertical-pod-autoscaler/pkg/features/versioned_features.go @@ -31,6 +31,6 @@ var defaultVersionedFeatureGates = map[featuregate.Feature]featuregate.Versioned {Version: version.MustParse("1.4"), Default: false, PreRelease: featuregate.Alpha}, }, PerVPAConfig: { - {Version: version.MustParse("1.5"), Default: true, PreRelease: featuregate.Alpha}, + {Version: version.MustParse("1.5"), Default: false, PreRelease: featuregate.Alpha}, }, } From 91ac55dd877d6ffc42cbd490dc3b00f2300b0d42 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Fri, 8 Aug 2025 12:58:27 +0300 Subject: [PATCH 27/33] Minimum OOMBumpUpRatio is 1 Co-authored-by: Adrian Moisey --- .../pkg/admission-controller/resource/vpa/handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go index 35e3708272..2a4a56e7d8 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go @@ -141,8 +141,8 @@ func ValidateVPA(vpa *vpa_types.VerticalPodAutoscaler, isCreate bool) error { // Validate OOMBumpUpRatio if policy.OOMBumpUpRatio != nil { ratio := float64(policy.OOMBumpUpRatio.MilliValue()) / 1000.0 - if ratio <= 1.0 { - return fmt.Errorf("OOMBumpUpRatio must be greater than 1.0, got %v", ratio) + if ratio < 1.0 { + return fmt.Errorf("OOMBumpUpRatio must be greater than or equal to 1.0, got %v", ratio) } } From c843b16ef9980b893a864a56c642b64fe7f7f980 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Fri, 8 Aug 2025 10:06:12 +0000 Subject: [PATCH 28/33] update flags Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/docs/flags.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vertical-pod-autoscaler/docs/flags.md b/vertical-pod-autoscaler/docs/flags.md index 23623fcc86..6448def6d9 100644 --- a/vertical-pod-autoscaler/docs/flags.md +++ b/vertical-pod-autoscaler/docs/flags.md @@ -14,7 +14,7 @@ This document is auto-generated from the flag definitions in the VPA admission-c | `address` | string | ":8944" | The address to expose Prometheus metrics. | | `alsologtostderr` | | | log to standard error as well as files (no effect when -logtostderr=true) | | `client-ca-file` | string | "/etc/tls-certs/caCert.pem" | Path to CA PEM file. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=true) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=false) | | `ignored-vpa-object-namespaces` | string | | A comma-separated list of namespaces to ignore when searching for VPA objects. Leave empty to avoid ignoring any namespaces. These namespaces will not be cleaned by the garbage collector. | | `kube-api-burst` | float | 100 | QPS burst limit when making requests to Kubernetes apiserver | | `kube-api-qps` | float | 50 | QPS limit when making requests to Kubernetes apiserver | @@ -68,7 +68,7 @@ This document is auto-generated from the flag definitions in the VPA recommender | `cpu-integer-post-processor-enabled` | | | Enable the cpu-integer recommendation post processor. The post processor will round up CPU recommendations to a whole CPU for pods which were opted in by setting an appropriate label on VPA object (experimental) | | `external-metrics-cpu-metric` | string | | ALPHA. Metric to use with external metrics provider for CPU usage. | | `external-metrics-memory-metric` | string | | ALPHA. Metric to use with external metrics provider for memory usage. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=true) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=false) | | `history-length` | string | "8d" | How much time back prometheus have to be queried to get historical metrics | | `history-resolution` | string | "1h" | Resolution at which Prometheus is queried for historical metrics | | `humanize-memory` | | | Convert memory values in recommendations to the highest appropriate SI unit with up to 2 decimal places for better readability. | @@ -140,7 +140,7 @@ This document is auto-generated from the flag definitions in the VPA updater cod | `eviction-rate-burst` | int | 1 | Burst of pods that can be evicted. | | `eviction-rate-limit` | float | | Number of pods that can be evicted per seconds. A rate limit set to 0 or -1 will disable
the rate limiter. (default -1) | | `eviction-tolerance` | float | 0.5 | Fraction of replica count that can be evicted for update, if more than one pod can be evicted. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=true) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=false) | | `ignored-vpa-object-namespaces` | string | | A comma-separated list of namespaces to ignore when searching for VPA objects. Leave empty to avoid ignoring any namespaces. These namespaces will not be cleaned by the garbage collector. | | `in-recommendation-bounds-eviction-lifetime-threshold` | | 12h0m0s | duration Pods that live for at least that long can be evicted even if their request is within the [MinRecommended...MaxRecommended] range | | `kube-api-burst` | float | 100 | QPS burst limit when making requests to Kubernetes apiserver | From 68b52f8c5b0b4597c98a915c7f4de90349547ca7 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Sat, 9 Aug 2025 16:16:11 +0000 Subject: [PATCH 29/33] fixed e2e tests for PerVPAConfig Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/e2e/v1/common.go | 14 ++++++++++++++ vertical-pod-autoscaler/e2e/v1/recommender.go | 11 +++-------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/vertical-pod-autoscaler/e2e/v1/common.go b/vertical-pod-autoscaler/e2e/v1/common.go index ce5e8e7607..433251b503 100644 --- a/vertical-pod-autoscaler/e2e/v1/common.go +++ b/vertical-pod-autoscaler/e2e/v1/common.go @@ -646,6 +646,20 @@ func checkInPlaceOrRecreateTestsEnabled(f *framework.Framework, checkAdmission, } } +// checkPerVPAConfigTestsEnabled checks if the PerVPAConfig feature gate is enabled +// in the VPA recommender. +func checkPerVPAConfigTestsEnabled(f *framework.Framework) { + ginkgo.By("Checking PerVPAConfig feature gate is enabled for recommender") + deploy, err := f.ClientSet.AppsV1().Deployments(VpaNamespace).Get(context.TODO(), "vpa-recommender", metav1.GetOptions{}) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(deploy.Spec.Template.Spec.Containers).To(gomega.HaveLen(1)) + vpaRecommenderPod := deploy.Spec.Template.Spec.Containers[0] + gomega.Expect(vpaRecommenderPod.Name).To(gomega.Equal("recommender")) + if !anyContainsSubstring(vpaRecommenderPod.Args, fmt.Sprintf("%s=true", string(features.PerVPAConfig))) { + ginkgo.Skip("Skipping suite: PerVPAConfig feature gate is not enabled for the VPA recommender") + } +} + func anyContainsSubstring(arr []string, substr string) bool { for _, s := range arr { if strings.Contains(s, substr) { diff --git a/vertical-pod-autoscaler/e2e/v1/recommender.go b/vertical-pod-autoscaler/e2e/v1/recommender.go index 70542351b1..f4bc2c0aaf 100644 --- a/vertical-pod-autoscaler/e2e/v1/recommender.go +++ b/vertical-pod-autoscaler/e2e/v1/recommender.go @@ -29,7 +29,6 @@ import ( "k8s.io/apimachinery/pkg/fields" vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" vpa_clientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned" - "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/features" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/test" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" @@ -413,20 +412,16 @@ var _ = RecommenderE2eDescribe("VPA CRD object", func() { }) }) -var _ = RecommenderE2eDescribe("OOM with custom config", func() { +var _ = RecommenderE2eDescribe("OOM with custom config", ginkgo.Label("FG:PerVPAConfig"), func() { const replicas = 3 f := framework.NewDefaultFramework("vertical-pod-autoscaling") f.NamespacePodSecurityEnforceLevel = podsecurity.LevelBaseline - var ( vpaCRD *vpa_types.VerticalPodAutoscaler vpaClientSet vpa_clientset.Interface ) - ginkgo.BeforeEach(func() { - if !features.Enabled(features.PerVPAConfig) { - ginkgo.Skip("Test requires PerVPAConfig feature gate to be enabled") - } + checkPerVPAConfigTestsEnabled(f) ns := f.Namespace.Name vpaClientSet = getVpaClientSet(f) ginkgo.By("Setting up a hamster deployment") @@ -442,7 +437,7 @@ var _ = RecommenderE2eDescribe("OOM with custom config", func() { Name: "hamster", } containerName := GetHamsterContainerNameByIndex(0) - vpaCRD := test.VerticalPodAutoscaler(). + vpaCRD = test.VerticalPodAutoscaler(). WithName("hamster-vpa"). WithNamespace(f.Namespace.Name). WithTargetRef(targetRef). From 72889dde5d31b4b591193359a61b34fcd4ca6b7c Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Tue, 12 Aug 2025 06:16:00 +0000 Subject: [PATCH 30/33] update flags Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/docs/flags.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/vertical-pod-autoscaler/docs/flags.md b/vertical-pod-autoscaler/docs/flags.md index 1ef9bbc38e..ba8826af59 100644 --- a/vertical-pod-autoscaler/docs/flags.md +++ b/vertical-pod-autoscaler/docs/flags.md @@ -14,7 +14,7 @@ This document is auto-generated from the flag definitions in the VPA admission-c | `address` | string | ":8944" | The address to expose Prometheus metrics. | | `alsologtostderr` | | | log to standard error as well as files (no effect when -logtostderr=true) | | `client-ca-file` | string | "/etc/tls-certs/caCert.pem" | Path to CA PEM file. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (BETA - default=true) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (BETA - default=true)
PerVPAConfig=true\|false (ALPHA - default=false) | | `ignored-vpa-object-namespaces` | string | | A comma-separated list of namespaces to ignore when searching for VPA objects. Leave empty to avoid ignoring any namespaces. These namespaces will not be cleaned by the garbage collector. | | `kube-api-burst` | float | 100 | QPS burst limit when making requests to Kubernetes apiserver | | `kube-api-qps` | float | 50 | QPS limit when making requests to Kubernetes apiserver | @@ -68,8 +68,7 @@ This document is auto-generated from the flag definitions in the VPA recommender | `cpu-integer-post-processor-enabled` | | | Enable the cpu-integer recommendation post processor. The post processor will round up CPU recommendations to a whole CPU for pods which were opted in by setting an appropriate label on VPA object (experimental) | | `external-metrics-cpu-metric` | string | | ALPHA. Metric to use with external metrics provider for CPU usage. | | `external-metrics-memory-metric` | string | | ALPHA. Metric to use with external metrics provider for memory usage. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=false) | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (BETA - default=true) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (BETA - default=true)
PerVPAConfig=true\|false (ALPHA - default=false) | | `history-length` | string | "8d" | How much time back prometheus have to be queried to get historical metrics | | `history-resolution` | string | "1h" | Resolution at which Prometheus is queried for historical metrics | | `humanize-memory` | | | DEPRECATED: Convert memory values in recommendations to the highest appropriate SI unit with up to 2 decimal places for better readability. This flag is deprecated and will be removed in a future version. Use --round-memory-bytes instead. | @@ -145,8 +144,7 @@ This document is auto-generated from the flag definitions in the VPA updater cod | `eviction-rate-burst` | int | 1 | Burst of pods that can be evicted. | | `eviction-rate-limit` | float | | Number of pods that can be evicted per seconds. A rate limit set to 0 or -1 will disable
the rate limiter. (default -1) | | `eviction-tolerance` | float | 0.5 | Fraction of replica count that can be evicted for update, if more than one pod can be evicted. | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (ALPHA - default=false)
PerVPAConfig=true\|false (ALPHA - default=false) | -| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (BETA - default=true) | +| `feature-gates` | mapStringBool | | A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true\|false (ALPHA - default=false)
AllBeta=true\|false (BETA - default=false)
InPlaceOrRecreate=true\|false (BETA - default=true)
PerVPAConfig=true\|false (ALPHA - default=false) | | `ignored-vpa-object-namespaces` | string | | A comma-separated list of namespaces to ignore when searching for VPA objects. Leave empty to avoid ignoring any namespaces. These namespaces will not be cleaned by the garbage collector. | | `in-recommendation-bounds-eviction-lifetime-threshold` | | 12h0m0s | duration Pods that live for at least that long can be evicted even if their request is within the [MinRecommended...MaxRecommended] range | | `kube-api-burst` | float | 100 | QPS burst limit when making requests to Kubernetes apiserver | From 151ee25969207e05374106f8ec5b6fd057069d49 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Tue, 12 Aug 2025 06:39:06 +0000 Subject: [PATCH 31/33] Add logs when feature gate is disabled Signed-off-by: Omer Aplatony --- .../model/aggregate_container_state.go | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go index bc566e2b89..083322b742 100644 --- a/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go +++ b/vertical-pod-autoscaler/pkg/recommender/model/aggregate_container_state.go @@ -42,6 +42,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog/v2" vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/features" @@ -312,11 +313,22 @@ func (a *AggregateContainerState) UpdateFromPolicy(resourcePolicy *vpa_types.Con } // Per VPA components - feature flag "PerVPAConfig" must be enabled - if resourcePolicy != nil && resourcePolicy.OOMBumpUpRatio != nil && features.Enabled(features.PerVPAConfig) { - a.OOMBumpUpRatio = a.convertQuantityToFloat64(resourcePolicy.OOMBumpUpRatio) - } - if resourcePolicy != nil && resourcePolicy.OOMMinBumpUp != nil && features.Enabled(features.PerVPAConfig) { - a.OOMMinBumpUp = a.convertQuantityToFloat64(resourcePolicy.OOMMinBumpUp) + if resourcePolicy != nil { + if resourcePolicy.OOMBumpUpRatio != nil { + if features.Enabled(features.PerVPAConfig) { + a.OOMBumpUpRatio = a.convertQuantityToFloat64(resourcePolicy.OOMBumpUpRatio) + } else { + klog.InfoS("OOMBumpUpRatio is set but PerVPAConfig feature gate is disabled, falling back to default value") + } + } + + if resourcePolicy.OOMMinBumpUp != nil { + if features.Enabled(features.PerVPAConfig) { + a.OOMMinBumpUp = a.convertQuantityToFloat64(resourcePolicy.OOMMinBumpUp) + } else { + klog.InfoS("OOMMinBumpUp is set but PerVPAConfig feature gate is disabled, falling back to default value") + } + } } } From 6cbd4499c9cf3f3960e95b8c783e848dc62f3484 Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Tue, 12 Aug 2025 06:55:06 +0000 Subject: [PATCH 32/33] fixed e2e Signed-off-by: Omer Aplatony --- vertical-pod-autoscaler/e2e/v1/common.go | 1 + 1 file changed, 1 insertion(+) diff --git a/vertical-pod-autoscaler/e2e/v1/common.go b/vertical-pod-autoscaler/e2e/v1/common.go index c8dcc795b6..225442bfa9 100644 --- a/vertical-pod-autoscaler/e2e/v1/common.go +++ b/vertical-pod-autoscaler/e2e/v1/common.go @@ -37,6 +37,7 @@ import ( "k8s.io/apimachinery/pkg/util/wait" vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" vpa_clientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned" + "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/features" clientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/test/e2e/framework" framework_deployment "k8s.io/kubernetes/test/e2e/framework/deployment" From 697934c96b8a8facf98bac4e9068b3f838d6325a Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Thu, 21 Aug 2025 14:01:58 +0000 Subject: [PATCH 33/33] Add feature flag to admission controller Signed-off-by: Omer Aplatony --- .../resource/vpa/handler.go | 14 +++++ .../resource/vpa/handler_test.go | 55 +++++++++++++++++++ .../pkg/features/features.go | 2 +- 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go index 6b804db573..3acb9515a7 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go @@ -137,6 +137,11 @@ func ValidateVPA(vpa *vpa_types.VerticalPodAutoscaler, isCreate bool) error { return fmt.Errorf("containerPolicies.ContainerName is required") } + // check that perVPA is on if being used + if err := validatePerVPAFeatureFlag(&policy); err != nil { + return err + } + // Validate OOMBumpUpRatio if policy.OOMBumpUpRatio != nil { ratio := float64(policy.OOMBumpUpRatio.MilliValue()) / 1000.0 @@ -217,3 +222,12 @@ func validateMemoryResolution(val apires.Quantity) error { } return nil } + +func validatePerVPAFeatureFlag(policy *vpa_types.ContainerResourcePolicy) error { + featureFlagOn := features.Enabled(features.PerVPAConfig) + perVPA := policy.OOMBumpUpRatio != nil || policy.OOMMinBumpUp != nil + if !featureFlagOn && perVPA { + return fmt.Errorf("OOMBumpUpRatio and OOMMinBumpUp are not supported when feature flag %s is disabled", features.PerVPAConfig) + } + return nil +} diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go index 9074f37dff..78044ff61b 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go @@ -51,6 +51,7 @@ func TestValidateVPA(t *testing.T) { isCreate bool expectError error inPlaceOrRecreateFeatureGateDisabled bool + PerVPAConfigDisabled bool }{ { name: "empty update", @@ -319,10 +320,64 @@ func TestValidateVPA(t *testing.T) { }, }, }, + { + name: "per-vpa config active and used", + vpa: vpa_types.VerticalPodAutoscaler{ + Spec: vpa_types.VerticalPodAutoscalerSpec{ + UpdatePolicy: &vpa_types.PodUpdatePolicy{ + UpdateMode: &validUpdateMode, + }, + ResourcePolicy: &vpa_types.PodResourcePolicy{ + ContainerPolicies: []vpa_types.ContainerResourcePolicy{ + { + ContainerName: "loot box", + Mode: &validScalingMode, + MinAllowed: apiv1.ResourceList{ + cpu: resource.MustParse("10"), + }, + MaxAllowed: apiv1.ResourceList{ + cpu: resource.MustParse("100"), + }, + OOMBumpUpRatio: resource.NewQuantity(2, resource.DecimalSI), + }, + }, + }, + }, + }, + PerVPAConfigDisabled: false, + }, + { + name: "per-vpa config disabled and used", + vpa: vpa_types.VerticalPodAutoscaler{ + Spec: vpa_types.VerticalPodAutoscalerSpec{ + UpdatePolicy: &vpa_types.PodUpdatePolicy{ + UpdateMode: &validUpdateMode, + }, + ResourcePolicy: &vpa_types.PodResourcePolicy{ + ContainerPolicies: []vpa_types.ContainerResourcePolicy{ + { + ContainerName: "loot box", + Mode: &validScalingMode, + MinAllowed: apiv1.ResourceList{ + cpu: resource.MustParse("10"), + }, + MaxAllowed: apiv1.ResourceList{ + cpu: resource.MustParse("100"), + }, + OOMMinBumpUp: resource.NewQuantity(2, resource.DecimalSI), + }, + }, + }, + }, + }, + PerVPAConfigDisabled: true, + expectError: fmt.Errorf("OOMBumpUpRatio and OOMMinBumpUp are not supported when feature flag PerVPAConfig is disabled"), + }, } for _, tc := range tests { t.Run(fmt.Sprintf("test case: %s", tc.name), func(t *testing.T) { featuregatetesting.SetFeatureGateDuringTest(t, features.MutableFeatureGate, features.InPlaceOrRecreate, !tc.inPlaceOrRecreateFeatureGateDisabled) + featuregatetesting.SetFeatureGateDuringTest(t, features.MutableFeatureGate, features.PerVPAConfig, !tc.PerVPAConfigDisabled) err := ValidateVPA(&tc.vpa, tc.isCreate) if tc.expectError == nil { assert.NoError(t, err) diff --git a/vertical-pod-autoscaler/pkg/features/features.go b/vertical-pod-autoscaler/pkg/features/features.go index 2c6ddc6a1a..5f92b6013c 100644 --- a/vertical-pod-autoscaler/pkg/features/features.go +++ b/vertical-pod-autoscaler/pkg/features/features.go @@ -50,7 +50,7 @@ const ( InPlaceOrRecreate featuregate.Feature = "InPlaceOrRecreate" // alpha: v1.5.0 - // components: recommender + // components: recommender, updater // PerVPAConfig enables the ability to specify component-specific configuration // parameters at the individual VPA object level. This allows for different