Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions vertical-pod-autoscaler/docs/flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:<br>AllAlpha=true\|false (ALPHA - default=false)<br>AllBeta=true\|false (BETA - default=false)<br>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:<br>AllAlpha=true\|false (ALPHA - default=false)<br>AllBeta=true\|false (BETA - default=false)<br>InPlaceOrRecreate=true\|false (BETA - default=true)<br>NativeSidecar=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 |
Expand Down Expand Up @@ -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:<br>AllAlpha=true\|false (ALPHA - default=false)<br>AllBeta=true\|false (BETA - default=false)<br>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:<br>AllAlpha=true\|false (ALPHA - default=false)<br>AllBeta=true\|false (BETA - default=false)<br>InPlaceOrRecreate=true\|false (BETA - default=true)<br>NativeSidecar=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. |
Expand Down Expand Up @@ -144,7 +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<br>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:<br>AllAlpha=true\|false (ALPHA - default=false)<br>AllBeta=true\|false (BETA - default=false)<br>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:<br>AllAlpha=true\|false (ALPHA - default=false)<br>AllBeta=true\|false (BETA - default=false)<br>InPlaceOrRecreate=true\|false (BETA - default=true)<br>NativeSidecar=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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
resource_admission "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/pod/recommendation"
vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/recommender/model"
resourcehelpers "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/resources"
vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa"
)
Expand Down Expand Up @@ -54,7 +55,7 @@ func (*resourcesUpdatesPatchCalculator) PatchResourceTarget() PatchResourceTarge
func (c *resourcesUpdatesPatchCalculator) CalculatePatches(pod *core.Pod, vpa *vpa_types.VerticalPodAutoscaler) ([]resource_admission.PatchRecord, error) {
result := []resource_admission.PatchRecord{}

containersResources, annotationsPerContainer, err := c.recommendationProvider.GetContainersResourcesForPod(pod, vpa)
initContainersResources, containersResources, annotationsPerContainer, err := c.recommendationProvider.GetContainersResourcesForPod(pod, vpa)
if err != nil {
return []resource_admission.PatchRecord{}, fmt.Errorf("failed to calculate resource patch for pod %s/%s: %v", pod.Namespace, pod.Name, err)
}
Expand All @@ -64,8 +65,14 @@ func (c *resourcesUpdatesPatchCalculator) CalculatePatches(pod *core.Pod, vpa *v
}

updatesAnnotation := []string{}
for i, containerResources := range initContainersResources {
newPatches, newUpdatesAnnotation := getContainerPatch(pod, i, annotationsPerContainer, containerResources, model.ContainerTypeInitSidecar)
result = append(result, newPatches...)
updatesAnnotation = append(updatesAnnotation, newUpdatesAnnotation)
}

for i, containerResources := range containersResources {
newPatches, newUpdatesAnnotation := getContainerPatch(pod, i, annotationsPerContainer, containerResources)
newPatches, newUpdatesAnnotation := getContainerPatch(pod, i, annotationsPerContainer, containerResources, model.ContainerTypeStandard)
result = append(result, newPatches...)
updatesAnnotation = append(updatesAnnotation, newUpdatesAnnotation)
}
Expand All @@ -77,33 +84,40 @@ func (c *resourcesUpdatesPatchCalculator) CalculatePatches(pod *core.Pod, vpa *v
return result, nil
}

func getContainerPatch(pod *core.Pod, i int, annotationsPerContainer vpa_api_util.ContainerToAnnotationsMap, containerResources vpa_api_util.ContainerResources) ([]resource_admission.PatchRecord, string) {
func getContainerPatch(pod *core.Pod, i int, annotationsPerContainer vpa_api_util.ContainerToAnnotationsMap, containerResources vpa_api_util.ContainerResources, containerType model.ContainerType) ([]resource_admission.PatchRecord, string) {
var patches []resource_admission.PatchRecord
// Add empty resources object if missing.
requests, limits := resourcehelpers.ContainerRequestsAndLimits(pod.Spec.Containers[i].Name, pod)
var container *core.Container
if containerType == model.ContainerTypeStandard {
container = &pod.Spec.Containers[i]
} else {
container = &pod.Spec.InitContainers[i]
}

requests, limits := resourcehelpers.ContainerRequestsAndLimits(container.Name, pod)
if limits == nil && requests == nil {
patches = append(patches, GetPatchInitializingEmptyResources(i))
patches = append(patches, GetPatchInitializingEmptyResources(i, containerType))
}

annotations, found := annotationsPerContainer[pod.Spec.Containers[i].Name]
annotations, found := annotationsPerContainer[container.Name]
if !found {
annotations = make([]string, 0)
}

patches, annotations = appendPatchesAndAnnotations(patches, annotations, requests, i, containerResources.Requests, "requests", "request")
patches, annotations = appendPatchesAndAnnotations(patches, annotations, limits, i, containerResources.Limits, "limits", "limit")
patches, annotations = appendPatchesAndAnnotations(patches, annotations, requests, i, containerResources.Requests, "requests", "request", containerType)
patches, annotations = appendPatchesAndAnnotations(patches, annotations, limits, i, containerResources.Limits, "limits", "limit", containerType)

updatesAnnotation := fmt.Sprintf("container %d: ", i) + strings.Join(annotations, ", ")
updatesAnnotation := fmt.Sprintf("%s %d: ", containerType, i) + strings.Join(annotations, ", ")
return patches, updatesAnnotation
}

func appendPatchesAndAnnotations(patches []resource_admission.PatchRecord, annotations []string, current core.ResourceList, containerIndex int, resources core.ResourceList, fieldName, resourceName string) ([]resource_admission.PatchRecord, []string) {
func appendPatchesAndAnnotations(patches []resource_admission.PatchRecord, annotations []string, current core.ResourceList, containerIndex int, resources core.ResourceList, fieldName, resourceName string, containerType model.ContainerType) ([]resource_admission.PatchRecord, []string) {
// Add empty object if it's missing and we're about to fill it.
if current == nil && len(resources) > 0 {
patches = append(patches, GetPatchInitializingEmptyResourcesSubfield(containerIndex, fieldName))
patches = append(patches, GetPatchInitializingEmptyResourcesSubfield(containerIndex, fieldName, containerType))
}
for resource, request := range resources {
patches = append(patches, GetAddResourceRequirementValuePatch(containerIndex, fieldName, resource, request))
patches = append(patches, GetAddResourceRequirementValuePatch(containerIndex, fieldName, resource, request, containerType))
annotations = append(annotations, fmt.Sprintf("%s %s", resource, resourceName))
}
return patches, annotations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,14 @@ const (
)

type fakeRecommendationProvider struct {
initResources []vpa_api_util.ContainerResources
resources []vpa_api_util.ContainerResources
containerToAnnotations vpa_api_util.ContainerToAnnotationsMap
e error
}

func (frp *fakeRecommendationProvider) GetContainersResourcesForPod(pod *core.Pod, vpa *vpa_types.VerticalPodAutoscaler) ([]vpa_api_util.ContainerResources, vpa_api_util.ContainerToAnnotationsMap, error) {
return frp.resources, frp.containerToAnnotations, frp.e
func (frp *fakeRecommendationProvider) GetContainersResourcesForPod(pod *core.Pod, vpa *vpa_types.VerticalPodAutoscaler) ([]vpa_api_util.ContainerResources, []vpa_api_util.ContainerResources, vpa_api_util.ContainerToAnnotationsMap, error) {
return frp.initResources, frp.resources, frp.containerToAnnotations, frp.e
}

func addResourcesPatch(idx int) resource_admission.PatchRecord {
Expand Down Expand Up @@ -88,6 +89,44 @@ func addResourceLimitPatch(index int, res, amount string) resource_admission.Pat
}
}

func addInitResourcesPatch(idx int) resource_admission.PatchRecord {
return resource_admission.PatchRecord{
Op: "add",
Path: fmt.Sprintf("/spec/initContainers/%d/resources", idx),
Value: core.ResourceRequirements{},
}
}

func addInitRequestsPatch(idx int) resource_admission.PatchRecord {
return resource_admission.PatchRecord{
Op: "add",
Path: fmt.Sprintf("/spec/initContainers/%d/resources/requests", idx),
Value: core.ResourceList{},
}
}

func addInitResourceRequestPatch(index int, res, amount string) resource_admission.PatchRecord {
return resource_admission.PatchRecord{
Op: "add",
Path: fmt.Sprintf("/spec/initContainers/%d/resources/requests/%s", index, res),
Value: resource.MustParse(amount),
}
}

func addInitAnnotationRequest(updateResources [][]string, kind string) resource_admission.PatchRecord {
requests := make([]string, 0)
for idx, podResources := range updateResources {
podRequests := make([]string, 0)
for _, resource := range podResources {
podRequests = append(podRequests, resource+" "+kind)
}
requests = append(requests, fmt.Sprintf("init-sidecar %d: %s", idx, strings.Join(podRequests, ", ")))
}

vpaUpdates := fmt.Sprintf("Pod resources updated by name: %s", strings.Join(requests, "; "))
return GetAddAnnotationPatch(ResourceUpdatesAnnotation, vpaUpdates)
}

func addAnnotationRequest(updateResources [][]string, kind string) resource_admission.PatchRecord {
requests := make([]string, 0)
for idx, podResources := range updateResources {
Expand All @@ -107,6 +146,7 @@ func TestCalculatePatches_ResourceUpdates(t *testing.T) {
name string
pod *core.Pod
namespace string
initResources []vpa_api_util.ContainerResources
recommendResources []vpa_api_util.ContainerResources
recommendAnnotations vpa_api_util.ContainerToAnnotationsMap
recommendError error
Expand Down Expand Up @@ -136,6 +176,29 @@ func TestCalculatePatches_ResourceUpdates(t *testing.T) {
addAnnotationRequest([][]string{{cpu}}, request),
},
},
{
name: "new init cpu recommendation",
pod: &core.Pod{
Spec: core.PodSpec{
InitContainers: []core.Container{{}},
},
},
namespace: "default",
initResources: []vpa_api_util.ContainerResources{
{
Requests: core.ResourceList{
cpu: resource.MustParse("1"),
},
},
},
recommendAnnotations: vpa_api_util.ContainerToAnnotationsMap{},
expectPatches: []resource_admission.PatchRecord{
addInitResourcesPatch(0),
addInitRequestsPatch(0),
addInitResourceRequestPatch(0, cpu, "1"),
addInitAnnotationRequest([][]string{{cpu}}, request),
},
},
{
name: "replacement cpu recommendation",
pod: &core.Pod{
Expand Down Expand Up @@ -292,7 +355,7 @@ func TestCalculatePatches_ResourceUpdates(t *testing.T) {
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
frp := fakeRecommendationProvider{tc.recommendResources, tc.recommendAnnotations, tc.recommendError}
frp := fakeRecommendationProvider{tc.initResources, tc.recommendResources, tc.recommendAnnotations, tc.recommendError}
c := NewResourceUpdatesCalculator(&frp)
patches, err := c.CalculatePatches(tc.pod, test.VerticalPodAutoscaler().WithContainer("test").WithName("name").Get())
if tc.expectError == nil {
Expand Down Expand Up @@ -334,7 +397,7 @@ func TestGetPatches_TwoReplacementResources(t *testing.T) {
},
}
recommendAnnotations := vpa_api_util.ContainerToAnnotationsMap{}
frp := fakeRecommendationProvider{recommendResources, recommendAnnotations, nil}
frp := fakeRecommendationProvider{nil, recommendResources, recommendAnnotations, nil}
c := NewResourceUpdatesCalculator(&frp)
patches, err := c.CalculatePatches(pod, test.VerticalPodAutoscaler().WithName("name").WithContainer("test").Get())
assert.NoError(t, err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource"

resource_admission "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/recommender/model"
)

// GetAddEmptyAnnotationsPatch returns a patch initializing empty annotations.
Expand All @@ -44,28 +45,28 @@ func GetAddAnnotationPatch(annotationName, annotationValue string) resource_admi
}

// GetAddResourceRequirementValuePatch returns a patch record to add resource requirements to a container.
func GetAddResourceRequirementValuePatch(i int, kind string, resource core.ResourceName, quantity resource.Quantity) resource_admission.PatchRecord {
func GetAddResourceRequirementValuePatch(i int, kind string, resource core.ResourceName, quantity resource.Quantity, containerType model.ContainerType) resource_admission.PatchRecord {
return resource_admission.PatchRecord{
Op: "add",
Path: fmt.Sprintf("/spec/containers/%d/resources/%s/%s", i, kind, resource),
Path: fmt.Sprintf("%s/%d/resources/%s/%s", containerType.GetPatchPath(), i, kind, resource),
Value: quantity.String()}
}

// GetPatchInitializingEmptyResources returns a patch record to initialize an empty resources object for a container.
func GetPatchInitializingEmptyResources(i int) resource_admission.PatchRecord {
func GetPatchInitializingEmptyResources(i int, containerType model.ContainerType) resource_admission.PatchRecord {
return resource_admission.PatchRecord{
Op: "add",
Path: fmt.Sprintf("/spec/containers/%d/resources", i),
Path: fmt.Sprintf("%s/%d/resources", containerType.GetPatchPath(), i),
Value: core.ResourceRequirements{},
}
}

// GetPatchInitializingEmptyResourcesSubfield returns a patch record to initialize an empty subfield
// (e.g., "requests" or "limits") within a container's resources object.
func GetPatchInitializingEmptyResourcesSubfield(i int, kind string) resource_admission.PatchRecord {
func GetPatchInitializingEmptyResourcesSubfield(i int, kind string, containerType model.ContainerType) resource_admission.PatchRecord {
return resource_admission.PatchRecord{
Op: "add",
Path: fmt.Sprintf("/spec/containers/%d/resources/%s", i, kind),
Path: fmt.Sprintf("%s/%d/resources/%s", containerType.GetPatchPath(), i, kind),
Value: core.ResourceList{},
}
}
Loading
Loading