diff --git a/pkg/recommender/recommender.go b/pkg/recommender/recommender.go index b791a1d7..d41d9d3e 100644 --- a/pkg/recommender/recommender.go +++ b/pkg/recommender/recommender.go @@ -143,7 +143,7 @@ func (s *Service) updateVPARecommendation(ctx context.Context, tortoise *v1beta3 recommendationMap := map[string]map[corev1.ResourceName]resource.Quantity{} for _, perContainer := range tortoise.Status.Conditions.ContainerRecommendationFromVPA { recommendationMap[perContainer.ContainerName] = map[corev1.ResourceName]resource.Quantity{} - for k, perResource := range perContainer.Recommendation { + for k, perResource := range perContainer.MaxRecommendation { recommendationMap[perContainer.ContainerName][k] = perResource.Quantity } } @@ -247,12 +247,32 @@ func (s *Service) calculateBestNewSize( } if p == v1beta3.AutoscalingTypeVertical { - // It's the simplest case. // The user configures Vertical on this container's resource. This is just vertical scaling. - // We always follow the recommendation from VPA. - newSize := float64(recommendedResourceRequest.MilliValue()) * (1 + s.bufferRatioOnVerticalResource) - jastified := s.justifyNewSize(resourceRequest.MilliValue(), int64(newSize), k, minAllocatedResources, containerName) - return jastified, fmt.Sprintf("change %v request (%v) (%v → %v) based on VPA suggestion", k, containerName, resourceRequest.MilliValue(), jastified), nil + // Basically we want to reduce the frequency of scaling up/down because vertical scaling has to restart deployment. + + // The ideal size is {VPA recommendation} * (1+buffer). + idealSize := float64(recommendedResourceRequest.MilliValue()) * (1 + s.bufferRatioOnVerticalResource) + if idealSize > float64(resourceRequest.MilliValue()) { + // Scale up always happens when idealSize goes higher than the current resource request. + // In this case, we don't just apply idealSize, but apply idealSize * (1+buffer) + // so that we increase the resource request more than actually needed, + // which reduces the need of scaling up in the future. + idealSize = idealSize * (1 + s.bufferRatioOnVerticalResource) + jastified := s.justifyNewSize(resourceRequest.MilliValue(), int64(idealSize), k, minAllocatedResources, containerName) + return jastified, fmt.Sprintf("change %v request (%v) (%v → %v) based on VPA suggestion", k, containerName, resourceRequest.MilliValue(), jastified), nil + } + + // Scale down - we ignore too small scale down to reduce the frequency of restarts. + + // previousIdealSize was the ideal size which was calculated when this resource request was applied. + previousIdealSize := float64(resourceRequest.MilliValue()) / (1 + s.bufferRatioOnVerticalResource) + if previousIdealSize*(1-s.bufferRatioOnVerticalResource) > idealSize { + // The current ideal size is too small campared to the previous ideal size. + jastified := s.justifyNewSize(resourceRequest.MilliValue(), int64(idealSize), k, minAllocatedResources, containerName) + return jastified, fmt.Sprintf("change %v request (%v) (%v → %v) based on VPA suggestion", k, containerName, resourceRequest.MilliValue(), jastified), nil + } + + return resourceRequest.MilliValue(), "The autoscaling policy for this resource is Off", nil } // p == v1beta3.AutoscalingTypeHorizontal diff --git a/pkg/recommender/recommender_test.go b/pkg/recommender/recommender_test.go index a49953f1..851ae30c 100644 --- a/pkg/recommender/recommender_test.go +++ b/pkg/recommender/recommender_test.go @@ -1624,7 +1624,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("500m"), }, @@ -1651,7 +1651,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("500m"), }, @@ -1730,7 +1730,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("500m"), }, @@ -1757,7 +1757,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("500m"), }, @@ -1844,7 +1844,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("400m"), }, @@ -1871,7 +1871,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("400m"), }, @@ -1951,7 +1951,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("400m"), }, @@ -1978,7 +1978,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("400m"), }, @@ -2050,7 +2050,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("400m"), }, @@ -2077,7 +2077,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("400m"), }, @@ -2136,7 +2136,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("500m"), }, @@ -2163,7 +2163,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("500m"), }, @@ -2214,7 +2214,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("1500m"), // exceed maxResourceSize }, @@ -2268,7 +2268,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("1500m"), }, @@ -2319,7 +2319,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("15m"), // smaller than MinAllocatedResources }, @@ -2373,7 +2373,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("15m"), // smaller than MinAllocatedResources }, @@ -2431,7 +2431,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("120m"), }, @@ -2458,7 +2458,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("120m"), }, @@ -2515,7 +2515,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("120m"), }, @@ -2542,7 +2542,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("120m"), }, @@ -2567,7 +2567,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { wantErr: false, }, { - name: "all vertical: reduce resources based on VPA recommendation plus bufferRatioOnVerticalResource", + name: "all vertical: scale resources based on VPA recommendation plus bufferRatioOnVerticalResource", fields: fields{ preferredMaxReplicas: 6, maxCPU: "1000m", @@ -2593,7 +2593,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("120m"), }, @@ -2620,7 +2620,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("120m"), }, @@ -2636,8 +2636,103 @@ func TestService_UpdateVPARecommendation(t *testing.T) { Vertical: v1beta3.VerticalRecommendations{ ContainerResourceRecommendation: []v1beta3.RecommendedContainerResources{ { - ContainerName: "test-container", - RecommendedResource: createResourceList("132m", "132Mi"), // VPA recommendation * (1 + bufferRatioOnVerticalResource) + ContainerName: "test-container", + // VPA recommendation * (1 + bufferRatioOnVerticalResource) * (1 + bufferRatioOnVerticalResource) + RecommendedResource: createResourceList("145m", "145.2Mi"), + }, + }, + }, + }).Build(), + wantErr: false, + }, + { + name: "all vertical: scale down resources based on VPA recommendation plus bufferRatioOnVerticalResource", + fields: fields{ + preferredMaxReplicas: 6, + maxCPU: "1000m", + maxMemory: "1Gi", + bufferRatioOnVerticalResource: 0.1, + }, + args: args{ + hpa: &v2.HorizontalPodAutoscaler{ + Spec: v2.HorizontalPodAutoscalerSpec{ + MinReplicas: ptr.To[int32](1), + Metrics: []v2.MetricSpec{}, + }, + }, + tortoise: utils.NewTortoiseBuilder().AddAutoscalingPolicy(v1beta3.ContainerAutoscalingPolicy{ + ContainerName: "test-container", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceCPU: v1beta3.AutoscalingTypeVertical, + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + }, + }).AddResourcePolicy(v1beta3.ContainerResourcePolicy{ + ContainerName: "test-container", + MinAllocatedResources: createResourceList("100m", "100Mi"), + }).AddContainerRecommendationFromVPA( + v1beta3.ContainerRecommendationFromVPA{ + ContainerName: "test-container", + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + corev1.ResourceCPU: { + // ideal = 110m + Quantity: resource.MustParse("100m"), + }, + corev1.ResourceMemory: { + // ideal = 121Mi + Quantity: resource.MustParse("110Mi"), + }, + }, + }, + ).AddContainerResourceRequests(v1beta3.ContainerResourceRequests{ + ContainerName: "test-container", + // Previous ideal = 132m, 132Mi + // Previous ideal * 0.9 = 118m, 118.8Mi + // + // → CPU will be scaled down to 110m + // → Memory will not be scaled down (because 118.8Mi < 121Mi) + Resource: createResourceList("145m", "145.2Mi"), + }).SetRecommendations(v1beta3.Recommendations{ + Vertical: v1beta3.VerticalRecommendations{ + ContainerResourceRecommendation: []v1beta3.RecommendedContainerResources{ + { + ContainerName: "test-container", + RecommendedResource: createResourceList("145m", "145.2Mi"), + }, + }, + }, + }).Build(), + }, + want: utils.NewTortoiseBuilder().AddAutoscalingPolicy(v1beta3.ContainerAutoscalingPolicy{ + ContainerName: "test-container", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceCPU: v1beta3.AutoscalingTypeVertical, + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + }, + }).AddResourcePolicy(v1beta3.ContainerResourcePolicy{ + ContainerName: "test-container", + MinAllocatedResources: createResourceList("100m", "100Mi"), + }).AddContainerRecommendationFromVPA( + v1beta3.ContainerRecommendationFromVPA{ + ContainerName: "test-container", + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + corev1.ResourceCPU: { + Quantity: resource.MustParse("100m"), + }, + corev1.ResourceMemory: { + Quantity: resource.MustParse("110Mi"), + }, + }, + }, + ).AddContainerResourceRequests(v1beta3.ContainerResourceRequests{ + ContainerName: "test-container", + Resource: createResourceList("145m", "145.2Mi"), + }).SetRecommendations(v1beta3.Recommendations{ + Vertical: v1beta3.VerticalRecommendations{ + ContainerResourceRecommendation: []v1beta3.RecommendedContainerResources{ + { + ContainerName: "test-container", + // VPA recommendation * (1 + bufferRatioOnVerticalResource) * (1 + bufferRatioOnVerticalResource) + RecommendedResource: createResourceList("110m", "145.2Mi"), }, }, }, @@ -2670,7 +2765,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("10m"), // too small }, @@ -2697,7 +2792,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("10m"), }, @@ -2747,7 +2842,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("2m"), }, @@ -2774,7 +2869,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("2m"), }, @@ -2824,7 +2919,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "istio-proxy", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("2m"), }, @@ -2851,7 +2946,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "istio-proxy", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("2m"), }, @@ -2899,7 +2994,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "istio-proxy", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ // It's too small. They're overwriteen based on maxAllowedScalingDownRatio. corev1.ResourceCPU: { Quantity: resource.MustParse("2m"), @@ -2924,7 +3019,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "istio-proxy", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("2m"), }, @@ -2977,7 +3072,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("80m"), // smaller than expectation (800m+) }, @@ -2989,7 +3084,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { ).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container2", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("800m"), }, @@ -3075,7 +3170,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { }).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("80m"), }, @@ -3087,7 +3182,7 @@ func TestService_UpdateVPARecommendation(t *testing.T) { ).AddContainerRecommendationFromVPA( v1beta3.ContainerRecommendationFromVPA{ ContainerName: "test-container2", - Recommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ + MaxRecommendation: map[corev1.ResourceName]v1beta3.ResourceQuantity{ corev1.ResourceCPU: { Quantity: resource.MustParse("800m"), },