From 7660896900baaf83979786b913c9f4794af19941 Mon Sep 17 00:00:00 2001 From: amitai-devops <87271817+amitai-devops@users.noreply.github.com> Date: Tue, 20 Dec 2022 10:09:07 +0200 Subject: [PATCH] :sparkles: add factor feature (#13) --- README.md | 17 ++++++++++++++--- charts/kube-reqsizer/Chart.yaml | 2 +- charts/kube-reqsizer/templates/deployment.yaml | 2 ++ charts/kube-reqsizer/values.yaml | 4 +++- controllers/pod_controller.go | 12 ++++++------ controllers/pod_controller_types.go | 2 ++ main.go | 8 ++++++++ 7 files changed, 36 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index b4a7d60..e11a1d6 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,10 @@ **kube-reqsizer** is a kubernetes controller that will measure the usage of pods over time and optimize (reduce/increase) their requests based on the average usage. -NOTE: This is an alternative to [Vertical-Pod-Autoscaler](https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler). The intended use of this project is to provide a simpler, more straightforward install and mechanism, without CRDs, **and that can work with [Horizontal-Pod-Autoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/).** +When all required conditions meet, the controller calculates the result requirements based on all the samples taken so far a pod. +It then goes "upstream" to the parent controller of that pod, for example *Deployment*, and updates the relevant containers for the pod inside the deployment as a reconciliation, as if its desired state is the new state with the new requirements. + +Note: This is an alternative to [Vertical-Pod-Autoscaler](https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler). The intended use of this project is to provide a simpler, more straightforward install and mechanism, without CRDs, **and that can work with [Horizontal-Pod-Autoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/).** ## Deploy - Helm ```bash @@ -17,6 +20,7 @@ helm install kube-reqsizer/kube-reqsizer **Core Values:** ```yaml +enabledAnnotation: true sampleSize: 1 minSeconds: 1 enableIncrease: true @@ -25,7 +29,8 @@ maxMemory: 0 minMemory: 0 maxCPU: 0 minCPU: 0 -enabledAnnotation: true +cpuFactor: 1 +memoryFactor: 1 logLevel: info ``` ## Prerequisites @@ -51,7 +56,7 @@ logLevel: info The sample size to create an average from when reconciling. ---min-seconds int (default 1) +--min-seconds float (default 1) Minimum seconds between pod restart. This ensures the controller will not restart a pod if the minimum time @@ -74,6 +79,12 @@ logLevel: info --min-memory int (default 0) Minimum memory in (Mi) that the controller can set a pod request to. 0 is infinite + +--cpu-factor float (default 1) + A factor to multiply CPU requests when reconciling. + +--memory-factor float (default 1) + A factor to multiply Memory requests when reconciling. ``` ### Annotations diff --git a/charts/kube-reqsizer/Chart.yaml b/charts/kube-reqsizer/Chart.yaml index 1edfb12..6dcd436 100644 --- a/charts/kube-reqsizer/Chart.yaml +++ b/charts/kube-reqsizer/Chart.yaml @@ -13,4 +13,4 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.6.4 +version: 0.6.5 diff --git a/charts/kube-reqsizer/templates/deployment.yaml b/charts/kube-reqsizer/templates/deployment.yaml index bea31f3..cd039c2 100644 --- a/charts/kube-reqsizer/templates/deployment.yaml +++ b/charts/kube-reqsizer/templates/deployment.yaml @@ -32,6 +32,8 @@ spec: - --max-memory={{.Values.maxMemory}} - --min-cpu={{.Values.minCPU}} - --min-memory={{.Values.minMemory}} + - --cpu-factor={{.Values.cpuFactor}} + - --memory-factor={{.Values.memoryFactor}} resources: {{- toYaml .Values.controllerManager.manager.resources | nindent 10 }} command: diff --git a/charts/kube-reqsizer/values.yaml b/charts/kube-reqsizer/values.yaml index b64c4e4..18d2b58 100644 --- a/charts/kube-reqsizer/values.yaml +++ b/charts/kube-reqsizer/values.yaml @@ -1,3 +1,4 @@ +enabledAnnotation: true sampleSize: 1 minSeconds: 1 enableIncrease: true @@ -6,7 +7,8 @@ maxMemory: 0 minMemory: 0 maxCPU: 0 minCPU: 0 -enabledAnnotation: true +cpuFactor: 1 +memoryFactor: 1 logLevel: info controllerManager: diff --git a/controllers/pod_controller.go b/controllers/pod_controller.go index f72e108..65e9a81 100644 --- a/controllers/pod_controller.go +++ b/controllers/pod_controller.go @@ -162,17 +162,17 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R switch r.GetPodMode(pod, ctx) { case "average": if r.ValidateCPU(currentC.CPU, AverageUsageCPU) { - pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", AverageUsageCPU)) + pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(AverageUsageCPU)*r.CPUFactor))) PodChange = true } case "min": if r.ValidateCPU(currentC.CPU, c.MinCPU) { - pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", c.MinCPU)) + pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(c.MinCPU)*r.CPUFactor))) PodChange = true } case "max": if r.ValidateCPU(currentC.CPU, c.MaxCPU) { - pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", c.MaxCPU)) + pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(c.MaxCPU)*r.CPUFactor))) PodChange = true } } @@ -182,17 +182,17 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R switch r.GetPodMode(pod, ctx) { case "average": if r.ValidateMemory(currentC.Memory, AverageUsageMemory) { - pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", AverageUsageMemory)) + pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(AverageUsageMemory)*r.MemoryFactor))) PodChange = true } case "min": if r.ValidateMemory(currentC.Memory, c.MinMemory) { - pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", c.MinMemory)) + pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(c.MinMemory)*r.MemoryFactor))) PodChange = true } case "max": if r.ValidateMemory(currentC.Memory, c.MaxMemory) { - pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", c.MaxMemory)) + pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(c.MaxMemory)*r.MemoryFactor))) PodChange = true } } diff --git a/controllers/pod_controller_types.go b/controllers/pod_controller_types.go index b62c692..c12019f 100644 --- a/controllers/pod_controller_types.go +++ b/controllers/pod_controller_types.go @@ -26,6 +26,8 @@ type PodReconciler struct { MaxCPU int64 MinMemory int64 MinCPU int64 + CPUFactor float64 + MemoryFactor float64 } type PodRequests struct { diff --git a/main.go b/main.go index 84f7443..1a6a4a9 100644 --- a/main.go +++ b/main.go @@ -63,10 +63,16 @@ func main() { var minMemory int64 var minCPU int64 + var cpuFactor float64 + var memoryFactor float64 + flag.BoolVar(&enableIncrease, "enable-increase", true, "Enables the controller to increase pod requests") flag.BoolVar(&enableReduce, "enable-reduce", true, "Enables the controller to reduce pod requests") flag.Int64Var(&maxMemory, "max-memory", 0, "Maximum memory in (Mi) that the controller can set a pod request to. 0 is infinite") flag.Int64Var(&maxCPU, "max-cpu", 0, "Maximum CPU in (m) that the controller can set a pod request to. 0 is infinite") + flag.Float64Var(&cpuFactor, "cpu-factor", 1, "A factor to multiply CPU requests when reconciling. 1 By default.") + flag.Float64Var(&memoryFactor, "memory-factor", 1, "A factor to multiply Memory requests when reconciling. 1 By default.") + flag.Int64Var(&minMemory, "min-memory", 0, "Minimum memory in (Mi) that the controller can set a pod request to. 0 is infinite") flag.Int64Var(&minCPU, "min-cpu", 0, "Minimum CPU in (m) that the controller can set a pod request to. 0 is infinite") @@ -132,6 +138,8 @@ func main() { MaxCPU: maxCPU, MinMemory: minMemory, MinCPU: minCPU, + CPUFactor: cpuFactor, + MemoryFactor: memoryFactor, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Pod") log.Error(err, err.Error())