Skip to content

Commit 22fa4ed

Browse files
20 - Fix min seconds. Remove Code Dupe. Optimization. (#21)
1 parent fea9728 commit 22fa4ed

File tree

7 files changed

+101
-98
lines changed

7 files changed

+101
-98
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ minCPU: 0
4545
cpuFactor: 1
4646
memoryFactor: 1
4747
logLevel: info
48-
concurrentWorkers: 20
48+
concurrentWorkers: 10
4949
```
5050
## Prerequisites
5151
- The metrics server must be deployed in your cluster. Read more about [Metrics Server](https://github.com/kubernetes-sigs/metrics-server). This controller uses the **metrics.k8s.io** extension API group (apis/metrics.k8s.io/v1beta1)
@@ -100,7 +100,7 @@ concurrentWorkers: 20
100100
--memory-factor float (default 1)
101101
A factor to multiply Memory requests when reconciling.
102102

103-
--concurrent-workers (default 20)
103+
--concurrent-workers (default 10)
104104
How many pods to sample in parallel. This may affect the controller's stability.
105105
```
106106

charts/kube-reqsizer/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ type: application
1313
# This is the chart version. This version number should be incremented each time you make changes
1414
# to the chart and its templates, including the app version.
1515
# Versions are expected to follow Semantic Versioning (https://semver.org/)
16-
version: 0.8.1
16+
version: 0.8.2

charts/kube-reqsizer/values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ minCPU: 0
1010
cpuFactor: 1
1111
memoryFactor: 1
1212
logLevel: info
13-
concurrentWorkers: 20
13+
concurrentWorkers: 10
1414

1515

1616
controllerManager:

controllers/pod_controller.go

Lines changed: 37 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ import (
2626
v1 "k8s.io/api/core/v1"
2727
apierrors "k8s.io/apimachinery/pkg/api/errors"
2828
"k8s.io/apimachinery/pkg/api/resource"
29-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3029
"k8s.io/client-go/tools/cache"
3130

3231
ctrl "sigs.k8s.io/controller-runtime"
32+
"sigs.k8s.io/controller-runtime/pkg/client"
3333
)
3434

3535
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;update;patch
@@ -77,7 +77,6 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R
7777
}
7878

7979
if ((!r.EnableAnnotation) || (r.EnableAnnotation && annotation)) && !ignoreAnnotation {
80-
8180
data, err := r.ClientSet.RESTClient().Get().AbsPath(fmt.Sprintf("apis/metrics.k8s.io/v1beta1/namespaces/%v/pods/%v", pod.Namespace, pod.Name)).DoRaw(ctx)
8281

8382
if err != nil {
@@ -93,20 +92,12 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R
9392
LatestPodRequest, err := fetchFromCache(cacheStore, pod.Name)
9493
if err != nil {
9594
SumPodRequest.Sample = 0
96-
SumPodRequest.TimeSinceFirstSample = 0
97-
SumPodRequest.Timestamp = time.Now()
9895
log.Info(fmt.Sprint("Adding cache sample ", SumPodRequest.Sample))
9996
addToCache(cacheStore, SumPodRequest)
10097
log.Info(fmt.Sprint("Items in Cache: ", len(cacheStore.List())))
10198
} else {
102-
99+
log.Info(fmt.Sprint("Items in Cache: ", len(cacheStore.List())))
103100
SumPodRequest.Sample = LatestPodRequest.Sample + 1
104-
if LatestPodRequest.Sample == 1 {
105-
SumPodRequest.Timestamp = time.Now()
106-
LatestPodRequest.Timestamp = SumPodRequest.Timestamp
107-
}
108-
log.Info(fmt.Sprint(time.Now(), LatestPodRequest.Timestamp))
109-
SumPodRequest.TimeSinceFirstSample = time.Since(LatestPodRequest.Timestamp).Seconds()
110101

111102
log.Info(fmt.Sprint("Updating cache sample ", SumPodRequest.Sample))
112103

@@ -143,7 +134,7 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R
143134
}
144135
}
145136
log.Info(fmt.Sprint(SumPodRequest))
146-
if (SumPodRequest.Sample >= r.SampleSize) && (SumPodRequest.TimeSinceFirstSample >= r.MinSecondsBetweenPodRestart) {
137+
if (SumPodRequest.Sample >= r.SampleSize) && r.MinimumUptimeOfPodInParent(pod, ctx) {
147138
log.Info("Sample Size and Minimum Time have been reached")
148139
PodChange := false
149140
Requests := []NewContainerRequests{}
@@ -155,49 +146,52 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R
155146
if currentC.Name == c.Name {
156147
for i, v := range pod.Spec.Containers {
157148
if v.Name == c.Name {
158-
log.Info(c.Name)
159-
log.Info(fmt.Sprint("Comparing CPU: ", fmt.Sprintf("%dm", AverageUsageCPU), " <> ", fmt.Sprintf("%dm", currentC.CPU)))
160-
log.Info(fmt.Sprint("Comparing Memory: ", fmt.Sprintf("%dMi", AverageUsageMemory), " <> ", fmt.Sprintf("%dMi", currentC.Memory)))
149+
if AverageUsageCPU < r.MinCPU && (r.MinCPU > 0) {
150+
AverageUsageCPU = r.MinCPU
151+
}
152+
if AverageUsageCPU > r.MaxCPU && (r.MaxCPU > 0) {
153+
AverageUsageCPU = r.MaxCPU
154+
}
155+
if AverageUsageMemory < r.MinMemory && (r.MinMemory > 0) {
156+
AverageUsageMemory = r.MinMemory
157+
}
158+
if AverageUsageMemory > r.MaxMemory && (r.MaxMemory > 0) {
159+
AverageUsageMemory = r.MaxMemory
160+
}
161+
log.Info(fmt.Sprint(c.Name, " Comparing CPU: ", fmt.Sprintf("%dm", AverageUsageCPU), " <> ", fmt.Sprintf("%dm", currentC.CPU)))
162+
log.Info(fmt.Sprint(c.Name, " Comparing Memory: ", fmt.Sprintf("%dMi", AverageUsageMemory), " <> ", fmt.Sprintf("%dMi", currentC.Memory)))
161163
if pod.Spec.Containers[i].Resources.Requests != nil {
162164
switch r.GetPodMode(pod, ctx) {
163165
case "average":
164166
if r.ValidateCPU(currentC.CPU, AverageUsageCPU) {
165167
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(AverageUsageCPU)*r.CPUFactor)))
166168
PodChange = true
167169
}
170+
if r.ValidateMemory(currentC.Memory, AverageUsageMemory) {
171+
pod.Spec.Containers[i].Resources.Requests[v1.ResourceMemory] = resource.MustParse(fmt.Sprintf("%dMi", int(float64(AverageUsageMemory)*r.MemoryFactor)))
172+
PodChange = true
173+
}
168174
case "min":
169175
if r.ValidateCPU(currentC.CPU, c.MinCPU) {
170176
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(c.MinCPU)*r.CPUFactor)))
171177
PodChange = true
172178
}
179+
if r.ValidateMemory(currentC.Memory, c.MinMemory) {
180+
pod.Spec.Containers[i].Resources.Requests[v1.ResourceMemory] = resource.MustParse(fmt.Sprintf("%dMi", int(float64(c.MinMemory)*r.MemoryFactor)))
181+
PodChange = true
182+
}
173183
case "max":
174184
if r.ValidateCPU(currentC.CPU, c.MaxCPU) {
175185
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(c.MaxCPU)*r.CPUFactor)))
176186
PodChange = true
177187
}
178-
}
179-
}
180-
if AverageUsageMemory > 0 {
181-
if pod.Spec.Containers[i].Resources.Requests != nil {
182-
switch r.GetPodMode(pod, ctx) {
183-
case "average":
184-
if r.ValidateMemory(currentC.Memory, AverageUsageMemory) {
185-
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(AverageUsageMemory)*r.MemoryFactor)))
186-
PodChange = true
187-
}
188-
case "min":
189-
if r.ValidateMemory(currentC.Memory, c.MinMemory) {
190-
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(c.MinMemory)*r.MemoryFactor)))
191-
PodChange = true
192-
}
193-
case "max":
194-
if r.ValidateMemory(currentC.Memory, c.MaxMemory) {
195-
pod.Spec.Containers[i].Resources.Requests[v1.ResourceCPU] = resource.MustParse(fmt.Sprintf("%dm", int(float64(c.MaxMemory)*r.MemoryFactor)))
196-
PodChange = true
197-
}
188+
if r.ValidateMemory(currentC.Memory, c.MaxMemory) {
189+
pod.Spec.Containers[i].Resources.Requests[v1.ResourceMemory] = resource.MustParse(fmt.Sprintf("%dMi", int(float64(c.MaxMemory)*r.MemoryFactor)))
190+
PodChange = true
198191
}
199192
}
200193
}
194+
201195
Requests = append(Requests, NewContainerRequests{Name: c.Name, Requests: pod.Spec.Containers[i].Resources})
202196
}
203197
}
@@ -206,68 +200,25 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R
206200
}
207201
if PodChange {
208202
pod.Annotations["reqsizer.jatalocks.github.io/changed"] = "true"
203+
209204
log.Info("Pod Requests Will Change")
210205

211206
if len(pod.OwnerReferences) == 0 {
212207
log.Info("Pod has no owner")
213208
return r.UpdateKubeObject(&pod, ctx)
214209
}
215210

216-
var ownerName string
217-
switch pod.OwnerReferences[0].Kind {
218-
case "ReplicaSet":
219-
replica, err := r.ClientSet.AppsV1().ReplicaSets(pod.Namespace).Get(ctx, pod.OwnerReferences[0].Name, metav1.GetOptions{})
220-
if err != nil {
221-
log.Error(err, err.Error())
222-
return ctrl.Result{}, err
223-
}
224-
225-
ownerName = replica.OwnerReferences[0].Name
226-
if replica.OwnerReferences[0].Kind == "Deployment" {
227-
log.Info("Is Owned by Deployment")
228-
deployment, err := r.ClientSet.AppsV1().Deployments(pod.Namespace).Get(ctx, ownerName, metav1.GetOptions{})
229-
if err != nil {
230-
log.Error(err, err.Error())
231-
return ctrl.Result{}, err
232-
}
233-
UpdatePodController(&deployment.Spec.Template.Spec, Requests, ctx)
234-
deployment.Annotations["reqsizer.jatalocks.github.io/changed"] = "true"
211+
err, podSpec, deployment, _ := r.GetPodParentKind(pod, ctx)
212+
if err != nil {
213+
return ctrl.Result{}, nil
214+
}
235215

236-
return r.UpdateKubeObject(deployment, ctx)
237-
} else {
238-
log.Info("Is Owned by Unknown CRD")
239-
return ctrl.Result{}, nil
240-
}
241-
case "DaemonSet":
242-
log.Info("Is Owned by DaemonSet")
243-
ownerName = pod.OwnerReferences[0].Name
244-
245-
deployment, err := r.ClientSet.AppsV1().DaemonSets(pod.Namespace).Get(ctx, ownerName, metav1.GetOptions{})
246-
if err != nil {
247-
log.Error(err, err.Error())
248-
return ctrl.Result{}, err
249-
}
250-
UpdatePodController(&deployment.Spec.Template.Spec, Requests, ctx)
251-
deployment.Annotations["reqsizer.jatalocks.github.io/changed"] = "true"
252-
return r.UpdateKubeObject(deployment, ctx)
253-
case "StatefulSet":
254-
log.Info("Is Owned by StatefulSet")
255-
ownerName = pod.OwnerReferences[0].Name
256-
257-
deployment, err := r.ClientSet.AppsV1().StatefulSets(pod.Namespace).Get(ctx, ownerName, metav1.GetOptions{})
258-
if err != nil {
259-
log.Error(err, err.Error())
260-
return ctrl.Result{}, err
261-
}
216+
UpdatePodController(podSpec, Requests, ctx)
262217

263-
UpdatePodController(&deployment.Spec.Template.Spec, Requests, ctx)
264-
deployment.Annotations["reqsizer.jatalocks.github.io/changed"] = "true"
265-
return r.UpdateKubeObject(deployment, ctx)
266-
default:
267-
fmt.Printf("Could not find resource manager for type %s\n", pod.OwnerReferences[0].Kind)
268-
}
218+
return r.UpdateKubeObject(deployment.(client.Object), ctx)
269219

270220
}
221+
271222
err := deleteFromCache(cacheStore, SumPodRequest)
272223
if err != nil {
273224
log.Error(err, err.Error())

controllers/pod_controller_functions.go

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/labstack/gommon/log"
1414
corev1 "k8s.io/api/core/v1"
15+
v1 "k8s.io/api/core/v1"
1516
apierrors "k8s.io/apimachinery/pkg/api/errors"
1617
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1718
"k8s.io/client-go/tools/cache"
@@ -168,7 +169,7 @@ func GetPodRequests(pod corev1.Pod) PodRequests {
168169
}
169170
containerData = append(containerData, ContainerRequests{Name: c.Name, CPU: int64(nanoCores), Memory: int64(miMemory)})
170171
}
171-
return PodRequests{pod.Name, containerData, 0, 0, time.Now()}
172+
return PodRequests{pod.Name, containerData, 0}
172173
}
173174

174175
func addToCache(cacheStore cache.Store, object PodRequests) error {
@@ -208,5 +209,56 @@ func GeneratePodRequestsObjectFromRestData(restData []byte) PodRequests {
208209
kiMemory, _ := strconv.Atoi(strings.ReplaceAll(c.Usage.Memory, "Ki", ""))
209210
containerData = append(containerData, ContainerRequests{Name: c.Name, CPU: int64(nanoCores / 1000000), Memory: int64(kiMemory / 1000)})
210211
}
211-
return PodRequests{data.Metadata.Name, containerData, 0, 0, time.Now()}
212+
return PodRequests{data.Metadata.Name, containerData, 0}
213+
}
214+
215+
func (r *PodReconciler) MinimumUptimeOfPodInParent(pod corev1.Pod, ctx context.Context) bool {
216+
217+
if len(pod.OwnerReferences) == 0 {
218+
return time.Since(pod.CreationTimestamp.Time).Seconds() >= r.MinSecondsBetweenPodRestart
219+
}
220+
err, _, _, deploymentName := r.GetPodParentKind(pod, ctx)
221+
if err != nil {
222+
return false
223+
}
224+
options := metav1.ListOptions{
225+
LabelSelector: "app=" + deploymentName,
226+
}
227+
podList, _ := r.ClientSet.CoreV1().Pods(pod.Namespace).List(ctx, options)
228+
// List() returns a pointer to slice, derefernce it, before iterating
229+
for _, podInfo := range (*podList).Items {
230+
if time.Since(podInfo.CreationTimestamp.Time).Seconds() < r.MinSecondsBetweenPodRestart {
231+
return false
232+
}
233+
}
234+
return true
235+
236+
}
237+
238+
func (r *PodReconciler) GetPodParentKind(pod corev1.Pod, ctx context.Context) (error, *v1.PodSpec, interface{}, string) {
239+
switch pod.OwnerReferences[0].Kind {
240+
case "ReplicaSet":
241+
replica, err := r.ClientSet.AppsV1().ReplicaSets(pod.Namespace).Get(ctx, pod.OwnerReferences[0].Name, metav1.GetOptions{})
242+
if err != nil {
243+
log.Error(err, err.Error())
244+
return err, nil, nil, ""
245+
}
246+
deployment, err := r.ClientSet.AppsV1().Deployments(pod.Namespace).Get(ctx, replica.OwnerReferences[0].Name, metav1.GetOptions{})
247+
deployment.Annotations["reqsizer.jatalocks.github.io/changed"] = "true"
248+
if replica.OwnerReferences[0].Kind == "Deployment" {
249+
return err, &deployment.Spec.Template.Spec, deployment, deployment.Name
250+
} else {
251+
return errors.New("Is Owned by Unknown CRD"), nil, nil, ""
252+
}
253+
case "DaemonSet":
254+
deployment, err := r.ClientSet.AppsV1().DaemonSets(pod.Namespace).Get(ctx, pod.OwnerReferences[0].Kind, metav1.GetOptions{})
255+
deployment.Annotations["reqsizer.jatalocks.github.io/changed"] = "true"
256+
return err, &deployment.Spec.Template.Spec, deployment, deployment.Name
257+
case "StatefulSet":
258+
deployment, err := r.ClientSet.AppsV1().StatefulSets(pod.Namespace).Get(ctx, pod.OwnerReferences[0].Kind, metav1.GetOptions{})
259+
deployment.Annotations["reqsizer.jatalocks.github.io/changed"] = "true"
260+
return err, &deployment.Spec.Template.Spec, deployment, deployment.Name
261+
default:
262+
return errors.New("Is Owned by Unknown CRD"), nil, nil, ""
263+
}
212264
}

controllers/pod_controller_types.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ type PodReconciler struct {
3131
}
3232

3333
type PodRequests struct {
34-
Name string
35-
ContainerRequests []ContainerRequests
36-
Sample int
37-
TimeSinceFirstSample float64
38-
Timestamp time.Time
34+
Name string
35+
ContainerRequests []ContainerRequests
36+
Sample int
37+
// TimeSinceFirstSample float64
38+
// Timestamp time.Time
3939
}
4040

4141
type ContainerRequests struct {

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func main() {
7474
flag.Float64Var(&cpuFactor, "cpu-factor", 1, "A factor to multiply CPU requests when reconciling. 1 By default.")
7575
flag.Float64Var(&memoryFactor, "memory-factor", 1, "A factor to multiply Memory requests when reconciling. 1 By default.")
7676

77-
flag.UintVar(&concurrentWorkers, "concurrent-workers", 20, "How many pods to sample in parallel. This may affect the controller's stability.")
77+
flag.UintVar(&concurrentWorkers, "concurrent-workers", 10, "How many pods to sample in parallel. This may affect the controller's stability.")
7878

7979
flag.Int64Var(&minMemory, "min-memory", 0, "Minimum memory in (Mi) that the controller can set a pod request to. 0 is infinite")
8080
flag.Int64Var(&minCPU, "min-cpu", 0, "Minimum CPU in (m) that the controller can set a pod request to. 0 is infinite")

0 commit comments

Comments
 (0)