Skip to content

Commit 74c6bf4

Browse files
committed
✨ add min/max and additional annotations
modified: README.md; modified: controllers/pod_controller.go; modified: controllers/pod_controller_functions.go; modified: controllers/pod_controller_types.go
1 parent 363d612 commit 74c6bf4

File tree

4 files changed

+87
-11
lines changed

4 files changed

+87
-11
lines changed

README.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ logLevel: info
3535

3636
Enable a annotation filter for pod scraping.
3737
Enabling this will ensure that the controller
38-
only sets requests of controllers of which PODS or NAMESPACE have the annotation.
39-
If false, will work on all pods in the cluster.
38+
only sets requests of controllers of which PODS or NAMESPACE
39+
have the annotation set to "true".
40+
If "false", will ignore annotations and work on all pods in the cluster.
4041

4142
# auto.request.operator/optimize=true
43+
# auto.request.operator/optimize=false
4244

4345
--sample-size int (default 1)
4446

@@ -69,10 +71,22 @@ logLevel: info
6971
Minimum memory in (Mi) that the controller can set a pod request to. 0 is infinite
7072
```
7173

72-
Disclaimer:
74+
### Annotations
75+
*If **annotation-filter** is **true**:*
76+
```
77+
auto.request.operator/optimize=true # Optimize Pod/Namespace
78+
auto.request.operator/optimize=false # Ignore Pod/Namespace
79+
```
80+
These are available POD/Namespace annotations *Regardless of **annotation-filter**:*
81+
```
82+
auto.request.operator/optimize=false # Ignore Pod/Namespace when optimizing entire cluster
83+
auto.request.operator/mode=average # Default Mode. Optimizes based on average. If ommited, mode is average
84+
auto.request.operator/mode=max # Sets the request to the MAXIMUM of all sample points
85+
auto.request.operator/mode=min # Sets the request to the MINIMUM of all sample points
86+
```
87+
### Disclaimer
7388

7489
`sample-size` is the amount of data-points the controller will store in cache before constructing an average for the pod. After a requests resizing, the cache will clean itself and a new average will be calculated based on the sample size. If `min-seconds` has not yet passed since the pod has last been supposed to be sample-reconciled, the controller will keep sampling the pod until `min-seconds` have been reached and only then zero the sample and restart from cache.
75-
7690
## Limitations
7791

7892
- Does not work with CRD controllers (such as Argo Rollouts)

controllers/pod_controller.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package controllers
1919
import (
2020
"context"
2121
"fmt"
22+
"math"
2223
"time"
2324

2425
corev1 "k8s.io/api/core/v1"
@@ -34,7 +35,8 @@ import (
3435
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;update;patch
3536

3637
const (
37-
operatorAnnotation = "auto.request.operator/optimize"
38+
operatorAnnotation = "auto.request.operator/optimize"
39+
operatorModeAnnotation = "auto.request.operator/mode"
3840
)
3941

4042
func cacheKeyFunc(obj interface{}) (string, error) {
@@ -68,8 +70,13 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R
6870
log.Error(err, "failed to get annotations")
6971
return ctrl.Result{}, err
7072
}
73+
ignoreAnnotation, err := r.NamespaceOrPodHaveIgnoreAnnotation(pod, ctx)
74+
if err != nil {
75+
log.Error(err, "failed to get annotations")
76+
return ctrl.Result{}, err
77+
}
7178

72-
if (!r.EnableAnnotation) || (r.EnableAnnotation && annotation) {
79+
if (!r.EnableAnnotation && !ignoreAnnotation) || (r.EnableAnnotation && annotation && !ignoreAnnotation) {
7380

7481
data, err := r.ClientSet.RESTClient().Get().AbsPath(fmt.Sprintf("apis/metrics.k8s.io/v1beta1/namespaces/%v/pods/%v", pod.Namespace, pod.Name)).DoRaw(ctx)
7582

@@ -105,6 +112,10 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R
105112
for _, latestC := range LatestPodRequest.ContainerRequests {
106113
if latestC.Name == sumC.Name {
107114
sumCAddr := &sumC
115+
sumCAddr.MaxCPU = int64(math.Max(float64(sumCAddr.MaxCPU), float64(latestC.CPU)))
116+
sumCAddr.MaxMemory = int64(math.Max(float64(sumCAddr.MaxMemory), float64(latestC.Memory)))
117+
sumCAddr.MinCPU = int64(math.Min(float64(sumCAddr.MinCPU), float64(latestC.CPU)))
118+
sumCAddr.MinMemory = int64(math.Min(float64(sumCAddr.MinMemory), float64(latestC.Memory)))
108119
sumCAddr.CPU += latestC.CPU
109120
sumCAddr.Memory += latestC.Memory
110121
}
@@ -140,15 +151,29 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R
140151
// if AverageUsageCPU < currentC.CPU {
141152
if r.ValidateCPU(currentC.CPU, AverageUsageCPU) {
142153
if pod.Spec.Containers[i].Resources.Requests != nil {
143-
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", AverageUsageCPU))
154+
switch r.GetPodMode(pod, ctx) {
155+
case "average":
156+
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", AverageUsageCPU))
157+
case "min":
158+
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", c.MinCPU))
159+
case "max":
160+
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", c.MaxCPU))
161+
}
144162
PodChange = true
145163
}
146164
}
147165
// }
148166
if r.ValidateMemory(currentC.Memory, AverageUsageMemory) {
149167
if AverageUsageMemory > 0 {
150168
if pod.Spec.Containers[i].Resources.Requests != nil {
151-
pod.Spec.Containers[i].Resources.Requests[v1.ResourceMemory] = resource.MustParse(fmt.Sprintf("%dMi", AverageUsageMemory))
169+
switch r.GetPodMode(pod, ctx) {
170+
case "average":
171+
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", AverageUsageMemory))
172+
case "min":
173+
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", c.MinMemory))
174+
case "max":
175+
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", c.MaxMemory))
176+
}
152177
PodChange = true
153178
}
154179
}

controllers/pod_controller_functions.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,39 @@ func (r *PodReconciler) NamespaceOrPodHaveAnnotation(pod corev1.Pod, ctx context
3030
return (podHasAnnotation || namespaceHasAnnotation), nil
3131
}
3232

33+
func (r *PodReconciler) NamespaceOrPodHaveIgnoreAnnotation(pod corev1.Pod, ctx context.Context) (bool, error) {
34+
podHasIgnoreAnnotation := pod.Annotations[operatorAnnotation] == "false"
35+
namespace, err := r.ClientSet.CoreV1().Namespaces().Get(ctx, pod.Namespace, metav1.GetOptions{})
36+
if err != nil {
37+
return false, err
38+
}
39+
namespaceHasIgnoreAnnotation := namespace.Annotations[operatorAnnotation] == "false"
40+
return (namespaceHasIgnoreAnnotation) && (podHasIgnoreAnnotation), nil
41+
}
42+
43+
func (r *PodReconciler) GetPodMode(pod corev1.Pod, ctx context.Context) string {
44+
podHasAverage := pod.Annotations[operatorModeAnnotation] == "average"
45+
podHasMin := pod.Annotations[operatorModeAnnotation] == "min"
46+
podHasMax := pod.Annotations[operatorModeAnnotation] == "max"
47+
namespace, err := r.ClientSet.CoreV1().Namespaces().Get(ctx, pod.Namespace, metav1.GetOptions{})
48+
if err != nil {
49+
return "average"
50+
}
51+
namespaceHasAverage := namespace.Annotations[operatorModeAnnotation] == "average"
52+
namespaceHasMin := namespace.Annotations[operatorModeAnnotation] == "min"
53+
namespaceHasMax := namespace.Annotations[operatorModeAnnotation] == "max"
54+
if podHasAverage || namespaceHasAverage {
55+
return "average"
56+
}
57+
if (podHasMax || namespaceHasMax) && !(podHasMin || namespaceHasMin) {
58+
return "max"
59+
}
60+
if (podHasMin || namespaceHasMin) && !(podHasMax || namespaceHasMax) {
61+
return "max"
62+
}
63+
return "average"
64+
}
65+
3366
func (r *PodReconciler) UpdateKubeObject(pod client.Object, ctx context.Context) (ctrl.Result, error) {
3467
if err := r.Update(ctx, pod); err != nil {
3568
if apierrors.IsConflict(err) {

controllers/pod_controller_types.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@ type PodRequests struct {
3737
}
3838

3939
type ContainerRequests struct {
40-
Name string
41-
CPU int64 // Nanocores
42-
Memory int64 // Mi
40+
Name string
41+
CPU int64 // Nanocores
42+
MaxCPU int64
43+
MinCPU int64
44+
Memory int64 // Mi
45+
MaxMemory int64
46+
MinMemory int64
4347
}
4448

4549
type NewContainerRequests struct {

0 commit comments

Comments
 (0)