Skip to content

Commit 40b4290

Browse files
authored
feat(recommender): add round memory bytes (#8298)
* feat(recommender): add round memory bytes * bug(recommender): treat memory as BinarySi * toc
1 parent 8e47b51 commit 40b4290

File tree

5 files changed

+92
-30
lines changed

5 files changed

+92
-30
lines changed

vertical-pod-autoscaler/docs/features.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- [Limits control](#limits-control)
66
- [Memory Value Humanization](#memory-value-humanization)
77
- [CPU Recommendation Rounding](#cpu-recommendation-rounding)
8+
- [Memory Recommendation Rounding](#memory-recommendation-rounding)
89
- [In-Place Updates](#in-place-updates-inplaceorrecreate)
910

1011
## Limits control
@@ -54,6 +55,22 @@ To enable this feature, set the --round-cpu-millicores flag when running the VPA
5455
--round-cpu-millicores=50
5556
```
5657

58+
## Memory Recommendation Rounding
59+
60+
VPA can provide Memory recommendations rounded up to user-specified values, making it easier to interpret and configure resources. This feature is controlled by the `--round-memory-bytes` flag in the recommender component.
61+
62+
When enabled, Memory recommendations will be:
63+
- Rounded up to the nearest multiple of the specified bytes value
64+
- Applied to target, lower bound, and upper bound recommendations
65+
66+
For example, with `--round-memory-bytes=134217728`, a memory recommendation of `200Mi` would be rounded up to `256Mi`, and a recommendation of `80Mi` would be rounded up to `128Mi`.
67+
68+
To enable this feature, set the `--round-memory-bytes` flag when running the VPA recommender:
69+
70+
```bash
71+
--round-memory-bytes=134217728
72+
```
73+
5774
## In-Place Updates (`InPlaceOrRecreate`)
5875

5976
> [!WARNING]

vertical-pod-autoscaler/docs/flags.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ This document is auto-generated from the flag definitions in the VPA recommender
118118
| `recommender-interval` | | 1m0s | duration How often metrics should be fetched |
119119
| `recommender-name` | string | "default" | Set the recommender name. Recommender will generate recommendations for VPAs that configure the same recommender name. If the recommender name is left as default it will also generate recommendations that don't explicitly specify recommender. You shouldn't run two recommenders with the same name in a cluster. |
120120
| `round-cpu-millicores` | int | 1 | CPU recommendation rounding factor in millicores. The CPU value will always be rounded up to the nearest multiple of this factor. |
121+
| `round-memory-bytes` | int | 1 | Memory recommendation rounding factor in bytes. The Memory value will always be rounded up to the nearest multiple of this factor. |
121122
| `skip-headers` | | | If true, avoid header prefixes in the log messages |
122123
| `skip-log-headers` | | | If true, avoid headers when opening log files (no effect when -logtostderr=true) |
123124
| `stderrthreshold` | severity | : info | set the log level threshold for writing to standard error |

vertical-pod-autoscaler/pkg/recommender/logic/recommender.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ var (
3939
confidenceIntervalMemory = flag.Duration("confidence-interval-memory", time.Hour*24, "The time interval used for computing the confidence multiplier for the memory lower and upper bound. Default: 24h")
4040
humanizeMemory = flag.Bool("humanize-memory", false, "Convert memory values in recommendations to the highest appropriate SI unit with up to 2 decimal places for better readability.")
4141
roundCPUMillicores = flag.Int("round-cpu-millicores", 1, `CPU recommendation rounding factor in millicores. The CPU value will always be rounded up to the nearest multiple of this factor.`)
42+
roundMemoryBytes = flag.Int("round-memory-bytes", 1, `Memory recommendation rounding factor in bytes. The Memory value will always be rounded up to the nearest multiple of this factor.`)
4243
)
4344

4445
// PodResourceRecommender computes resource recommendation for a Vpa object.
@@ -193,10 +194,10 @@ func MapToListOfRecommendedContainerResources(resources RecommendedPodResources)
193194
for _, name := range containerNames {
194195
containerResources = append(containerResources, vpa_types.RecommendedContainerResources{
195196
ContainerName: name,
196-
Target: model.ResourcesAsResourceList(resources[name].Target, *humanizeMemory, *roundCPUMillicores),
197-
LowerBound: model.ResourcesAsResourceList(resources[name].LowerBound, *humanizeMemory, *roundCPUMillicores),
198-
UpperBound: model.ResourcesAsResourceList(resources[name].UpperBound, *humanizeMemory, *roundCPUMillicores),
199-
UncappedTarget: model.ResourcesAsResourceList(resources[name].Target, *humanizeMemory, *roundCPUMillicores),
197+
Target: model.ResourcesAsResourceList(resources[name].Target, *humanizeMemory, *roundCPUMillicores, *roundMemoryBytes),
198+
LowerBound: model.ResourcesAsResourceList(resources[name].LowerBound, *humanizeMemory, *roundCPUMillicores, *roundMemoryBytes),
199+
UpperBound: model.ResourcesAsResourceList(resources[name].UpperBound, *humanizeMemory, *roundCPUMillicores, *roundMemoryBytes),
200+
UncappedTarget: model.ResourcesAsResourceList(resources[name].Target, *humanizeMemory, *roundCPUMillicores, *roundMemoryBytes),
200201
})
201202
}
202203
recommendation := &vpa_types.RecommendedPodResources{

vertical-pod-autoscaler/pkg/recommender/model/types.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func BytesFromMemoryAmount(memoryAmount ResourceAmount) float64 {
7373

7474
// QuantityFromMemoryAmount converts memory ResourceAmount to a resource.Quantity.
7575
func QuantityFromMemoryAmount(memoryAmount ResourceAmount) resource.Quantity {
76-
return *resource.NewScaledQuantity(int64(memoryAmount), 0)
76+
return *resource.NewQuantity(int64(memoryAmount), resource.BinarySI)
7777
}
7878

7979
// ScaleResource returns the resource amount multiplied by a given factor.
@@ -82,7 +82,7 @@ func ScaleResource(amount ResourceAmount, factor float64) ResourceAmount {
8282
}
8383

8484
// ResourcesAsResourceList converts internal Resources representation to ResourcesList.
85-
func ResourcesAsResourceList(resources Resources, humanizeMemory bool, roundCPUMillicores int) apiv1.ResourceList {
85+
func ResourcesAsResourceList(resources Resources, humanizeMemory bool, roundCPUMillicores, roundMemoryBytes int) apiv1.ResourceList {
8686
result := make(apiv1.ResourceList)
8787
for key, resourceAmount := range resources {
8888
var newKey apiv1.ResourceName
@@ -103,6 +103,15 @@ func ResourcesAsResourceList(resources Resources, humanizeMemory bool, roundCPUM
103103
case ResourceMemory:
104104
newKey = apiv1.ResourceMemory
105105
quantity = QuantityFromMemoryAmount(resourceAmount)
106+
if roundMemoryBytes != 1 && !quantity.IsZero() {
107+
roundedValues, err := RoundUpToScale(resourceAmount, roundMemoryBytes)
108+
if err != nil {
109+
klog.V(4).InfoS("Error rounding memory value; leaving unchanged", "rawValue", resourceAmount, "scale", roundMemoryBytes, "error", err)
110+
} else {
111+
klog.V(4).InfoS("Successfully rounded memory value", "rawValue", resourceAmount, "roundedValue", roundedValues)
112+
}
113+
quantity = QuantityFromMemoryAmount(roundedValues)
114+
}
106115
if humanizeMemory && !quantity.IsZero() {
107116
rawValues := quantity.Value()
108117
humanizedValue := HumanizeMemoryQuantity(rawValues)

vertical-pod-autoscaler/pkg/recommender/model/types_test.go

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,24 @@ type ResourcesAsResourceListTestCase struct {
2929
resources Resources
3030
humanize bool
3131
roundCPU int
32+
roundMemory int
3233
resourceList apiv1.ResourceList
3334
}
3435

3536
func TestResourcesAsResourceList(t *testing.T) {
3637
testCases := []ResourcesAsResourceListTestCase{
3738
{
38-
name: "basic resources without humanize and no cpu rounding",
39+
name: "basic resources without humanize and no rounding",
3940
resources: Resources{
4041
ResourceCPU: 1000,
41-
ResourceMemory: 1000,
42+
ResourceMemory: 1024,
4243
},
43-
humanize: false,
44-
roundCPU: 1,
44+
humanize: false,
45+
roundCPU: 1,
46+
roundMemory: 1,
4547
resourceList: apiv1.ResourceList{
4648
apiv1.ResourceCPU: *resource.NewMilliQuantity(1000, resource.DecimalSI),
47-
apiv1.ResourceMemory: *resource.NewQuantity(1000, resource.DecimalSI),
49+
apiv1.ResourceMemory: *resource.NewQuantity(1024, resource.BinarySI),
4850
},
4951
},
5052
{
@@ -53,21 +55,37 @@ func TestResourcesAsResourceList(t *testing.T) {
5355
ResourceCPU: 1000,
5456
ResourceMemory: 262144000, // 250Mi
5557
},
56-
humanize: true,
57-
roundCPU: 1,
58+
humanize: true,
59+
roundCPU: 1,
60+
roundMemory: 1,
5861
resourceList: apiv1.ResourceList{
5962
apiv1.ResourceCPU: *resource.NewMilliQuantity(1000, resource.DecimalSI),
6063
apiv1.ResourceMemory: resource.MustParse("250.00Mi"),
6164
},
6265
},
66+
{
67+
name: "basic resources with humanize and memory rounding to 256Mi",
68+
resources: Resources{
69+
ResourceCPU: 1000,
70+
ResourceMemory: 262144000, // 250Mi
71+
},
72+
humanize: true,
73+
roundCPU: 1,
74+
roundMemory: 268435456,
75+
resourceList: apiv1.ResourceList{
76+
apiv1.ResourceCPU: *resource.NewMilliQuantity(1000, resource.DecimalSI),
77+
apiv1.ResourceMemory: resource.MustParse("256.00Mi"),
78+
},
79+
},
6380
{
6481
name: "large memory value with humanize and cpu rounding to 3",
6582
resources: Resources{
6683
ResourceCPU: 1000,
6784
ResourceMemory: 839500000, // 800.61Mi
6885
},
69-
humanize: true,
70-
roundCPU: 3,
86+
humanize: true,
87+
roundCPU: 3,
88+
roundMemory: 1,
7189
resourceList: apiv1.ResourceList{
7290
apiv1.ResourceCPU: *resource.NewMilliQuantity(1002, resource.DecimalSI),
7391
apiv1.ResourceMemory: resource.MustParse("800.61Mi"),
@@ -79,11 +97,12 @@ func TestResourcesAsResourceList(t *testing.T) {
7997
ResourceCPU: 0,
8098
ResourceMemory: 0,
8199
},
82-
humanize: false,
83-
roundCPU: 2,
100+
humanize: false,
101+
roundCPU: 2,
102+
roundMemory: 1,
84103
resourceList: apiv1.ResourceList{
85104
apiv1.ResourceCPU: *resource.NewMilliQuantity(0, resource.DecimalSI),
86-
apiv1.ResourceMemory: *resource.NewQuantity(0, resource.DecimalSI),
105+
apiv1.ResourceMemory: *resource.NewQuantity(0, resource.BinarySI),
87106
},
88107
},
89108
{
@@ -92,17 +111,32 @@ func TestResourcesAsResourceList(t *testing.T) {
92111
ResourceCPU: 1231241,
93112
ResourceMemory: 839500000,
94113
},
95-
humanize: false,
96-
roundCPU: 13,
114+
humanize: false,
115+
roundCPU: 13,
116+
roundMemory: 1,
117+
resourceList: apiv1.ResourceList{
118+
apiv1.ResourceCPU: *resource.NewMilliQuantity(1231243, resource.DecimalSI),
119+
apiv1.ResourceMemory: *resource.NewQuantity(839500000, resource.BinarySI),
120+
},
121+
},
122+
{
123+
name: "large memory value without humanize and with memory rounding to 128Mi",
124+
resources: Resources{
125+
ResourceCPU: 1231241,
126+
ResourceMemory: 839500000,
127+
},
128+
humanize: false,
129+
roundCPU: 13,
130+
roundMemory: 134217728,
97131
resourceList: apiv1.ResourceList{
98132
apiv1.ResourceCPU: *resource.NewMilliQuantity(1231243, resource.DecimalSI),
99-
apiv1.ResourceMemory: *resource.NewQuantity(839500000, resource.DecimalSI),
133+
apiv1.ResourceMemory: resource.MustParse("896Mi"),
100134
},
101135
},
102136
}
103137
for _, tc := range testCases {
104138
t.Run(tc.name, func(t *testing.T) {
105-
result := ResourcesAsResourceList(tc.resources, tc.humanize, tc.roundCPU)
139+
result := ResourcesAsResourceList(tc.resources, tc.humanize, tc.roundCPU, tc.roundMemory)
106140
if !result[apiv1.ResourceCPU].Equal(tc.resourceList[apiv1.ResourceCPU]) {
107141
t.Errorf("expected %v, got %v", tc.resourceList[apiv1.ResourceCPU], result[apiv1.ResourceCPU])
108142
}
@@ -503,42 +537,42 @@ func TestQuantityFromMemoryAmount(t *testing.T) {
503537
{
504538
name: "should get 69",
505539
memoryAmount: 69,
506-
want: *resource.NewQuantity(69, resource.DecimalSI),
540+
want: *resource.NewQuantity(69, resource.BinarySI),
507541
},
508542
{
509543
name: "should get 12",
510544
memoryAmount: 12,
511-
want: *resource.NewQuantity(12, resource.DecimalSI),
545+
want: *resource.NewQuantity(12, resource.BinarySI),
512546
},
513547
{
514548
name: "should get 17",
515549
memoryAmount: 17,
516-
want: *resource.NewQuantity(17, resource.DecimalSI),
550+
want: *resource.NewQuantity(17, resource.BinarySI),
517551
},
518552
{
519553
name: "should get 4",
520554
memoryAmount: 4,
521-
want: *resource.NewQuantity(4, resource.DecimalSI),
555+
want: *resource.NewQuantity(4, resource.BinarySI),
522556
},
523557
{
524558
name: "should get 12",
525559
memoryAmount: 12,
526-
want: *resource.NewQuantity(12, resource.DecimalSI),
560+
want: *resource.NewQuantity(12, resource.BinarySI),
527561
},
528562
{
529563
name: "should get 1",
530564
memoryAmount: 1,
531-
want: *resource.NewQuantity(1, resource.DecimalSI),
565+
want: *resource.NewQuantity(1, resource.BinarySI),
532566
},
533567
{
534568
name: "should get 0",
535569
memoryAmount: 0,
536-
want: *resource.NewQuantity(0, resource.DecimalSI),
570+
want: *resource.NewQuantity(0, resource.BinarySI),
537571
},
538572
{
539573
name: "should get 123456789",
540574
memoryAmount: 123456789,
541-
want: *resource.NewQuantity(123456789, resource.DecimalSI),
575+
want: *resource.NewQuantity(123456789, resource.BinarySI),
542576
},
543577
}
544578
for _, tc := range tc {

0 commit comments

Comments
 (0)