diff --git a/deployment/k8s/charts/build-bot/templates/deployment.yaml b/deployment/k8s/charts/build-bot/templates/deployment.yaml index 743e3e81..76c3b8d5 100644 --- a/deployment/k8s/charts/build-bot/templates/deployment.yaml +++ b/deployment/k8s/charts/build-bot/templates/deployment.yaml @@ -6,7 +6,9 @@ metadata: labels: app: build-bot spec: + {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount | default 1 }} + {{- end }} selector: matchLabels: app: build-bot diff --git a/deployment/k8s/charts/build-bot/templates/hpa.yaml b/deployment/k8s/charts/build-bot/templates/hpa.yaml new file mode 100644 index 00000000..8367674a --- /dev/null +++ b/deployment/k8s/charts/build-bot/templates/hpa.yaml @@ -0,0 +1 @@ +{{ include "buttercup.hpa" . }} diff --git a/deployment/k8s/charts/build-bot/values.yaml b/deployment/k8s/charts/build-bot/values.yaml index eab264e6..905d7812 100644 --- a/deployment/k8s/charts/build-bot/values.yaml +++ b/deployment/k8s/charts/build-bot/values.yaml @@ -11,3 +11,20 @@ resources: replicaCount: 4 timer: 5000 logLevel: "DEBUG" + +# Autoscaling configuration +autoscaling: + enabled: false + minReplicas: 2 + maxReplicas: 12 + targetCPUUtilizationPercentage: 75 + behavior: + scaleDown: + # 300s: build tasks are relatively short-lived and can tolerate faster scale-down + stabilizationWindowSeconds: 300 + scaleUp: + stabilizationWindowSeconds: 60 + policies: + - type: Pods + value: 3 + periodSeconds: 60 diff --git a/deployment/k8s/charts/coverage-bot/templates/deployment.yaml b/deployment/k8s/charts/coverage-bot/templates/deployment.yaml index 04d29dea..67bbebaf 100644 --- a/deployment/k8s/charts/coverage-bot/templates/deployment.yaml +++ b/deployment/k8s/charts/coverage-bot/templates/deployment.yaml @@ -6,7 +6,9 @@ metadata: labels: app: coverage-bot spec: + {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount | default 1 }} + {{- end }} selector: matchLabels: app: coverage-bot diff --git a/deployment/k8s/charts/coverage-bot/templates/hpa.yaml b/deployment/k8s/charts/coverage-bot/templates/hpa.yaml new file mode 100644 index 00000000..8367674a --- /dev/null +++ b/deployment/k8s/charts/coverage-bot/templates/hpa.yaml @@ -0,0 +1 @@ +{{ include "buttercup.hpa" . }} diff --git a/deployment/k8s/charts/coverage-bot/values.yaml b/deployment/k8s/charts/coverage-bot/values.yaml index 75273be2..80dcea27 100644 --- a/deployment/k8s/charts/coverage-bot/values.yaml +++ b/deployment/k8s/charts/coverage-bot/values.yaml @@ -8,3 +8,21 @@ resources: requests: cpu: 250m memory: 256Mi + +# Autoscaling configuration +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 6 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 75 + behavior: + scaleDown: + # 600s: long-running coverage analysis should not be interrupted by premature scale-down + stabilizationWindowSeconds: 600 + scaleUp: + stabilizationWindowSeconds: 120 + policies: + - type: Pods + value: 1 + periodSeconds: 60 diff --git a/deployment/k8s/charts/fuzzer-bot/templates/deployment.yaml b/deployment/k8s/charts/fuzzer-bot/templates/deployment.yaml index 775675e2..7b00fc61 100644 --- a/deployment/k8s/charts/fuzzer-bot/templates/deployment.yaml +++ b/deployment/k8s/charts/fuzzer-bot/templates/deployment.yaml @@ -6,7 +6,9 @@ metadata: labels: app: fuzzer-bot spec: + {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount | default 1 }} + {{- end }} selector: matchLabels: app: fuzzer-bot diff --git a/deployment/k8s/charts/fuzzer-bot/templates/hpa.yaml b/deployment/k8s/charts/fuzzer-bot/templates/hpa.yaml new file mode 100644 index 00000000..8367674a --- /dev/null +++ b/deployment/k8s/charts/fuzzer-bot/templates/hpa.yaml @@ -0,0 +1 @@ +{{ include "buttercup.hpa" . }} diff --git a/deployment/k8s/charts/fuzzer-bot/values.yaml b/deployment/k8s/charts/fuzzer-bot/values.yaml index 2124c7da..6a21447b 100644 --- a/deployment/k8s/charts/fuzzer-bot/values.yaml +++ b/deployment/k8s/charts/fuzzer-bot/values.yaml @@ -25,3 +25,25 @@ healthCheck: timeoutSeconds: 10 # Allow 2 failures before restarting failureThreshold: 2 + +# Autoscaling configuration +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 75 + targetMemoryUtilizationPercentage: 80 + behavior: + scaleDown: + # 300s: shorter-lived fuzzing tasks can tolerate faster scale-down + stabilizationWindowSeconds: 300 + policies: + - type: Percent + value: 25 + periodSeconds: 60 + scaleUp: + stabilizationWindowSeconds: 60 + policies: + - type: Pods + value: 2 + periodSeconds: 60 diff --git a/deployment/k8s/charts/patcher/templates/deployment.yaml b/deployment/k8s/charts/patcher/templates/deployment.yaml index 1e4f4c0e..42715091 100644 --- a/deployment/k8s/charts/patcher/templates/deployment.yaml +++ b/deployment/k8s/charts/patcher/templates/deployment.yaml @@ -6,7 +6,9 @@ metadata: labels: app: patcher spec: + {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount | default 1 }} + {{- end }} selector: matchLabels: app: patcher diff --git a/deployment/k8s/charts/patcher/templates/hpa.yaml b/deployment/k8s/charts/patcher/templates/hpa.yaml new file mode 100644 index 00000000..8367674a --- /dev/null +++ b/deployment/k8s/charts/patcher/templates/hpa.yaml @@ -0,0 +1 @@ +{{ include "buttercup.hpa" . }} diff --git a/deployment/k8s/charts/patcher/values.yaml b/deployment/k8s/charts/patcher/values.yaml index 47859a97..db206bf7 100644 --- a/deployment/k8s/charts/patcher/values.yaml +++ b/deployment/k8s/charts/patcher/values.yaml @@ -9,3 +9,16 @@ resources: requests: cpu: 200m memory: 256Mi + +# Autoscaling configuration +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 70 + behavior: + scaleDown: + # 600s: LLM-powered patching tasks are long-running and should not be interrupted + stabilizationWindowSeconds: 600 + scaleUp: + stabilizationWindowSeconds: 120 diff --git a/deployment/k8s/charts/pov-reproducer/templates/deployment.yaml b/deployment/k8s/charts/pov-reproducer/templates/deployment.yaml index b86e38e0..478d6d01 100644 --- a/deployment/k8s/charts/pov-reproducer/templates/deployment.yaml +++ b/deployment/k8s/charts/pov-reproducer/templates/deployment.yaml @@ -3,7 +3,9 @@ kind: Deployment metadata: name: {{ .Release.Name }}-pov-reproducer spec: + {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount | default 1 }} + {{- end }} selector: matchLabels: app: pov-reproducer diff --git a/deployment/k8s/charts/pov-reproducer/templates/hpa.yaml b/deployment/k8s/charts/pov-reproducer/templates/hpa.yaml new file mode 100644 index 00000000..8367674a --- /dev/null +++ b/deployment/k8s/charts/pov-reproducer/templates/hpa.yaml @@ -0,0 +1 @@ +{{ include "buttercup.hpa" . }} diff --git a/deployment/k8s/charts/pov-reproducer/values.yaml b/deployment/k8s/charts/pov-reproducer/values.yaml index 55b696f9..186d4e3c 100644 --- a/deployment/k8s/charts/pov-reproducer/values.yaml +++ b/deployment/k8s/charts/pov-reproducer/values.yaml @@ -14,3 +14,21 @@ resources: povReproducer: sleepTime: 5.0 maxRetries: 100000 + +# Autoscaling configuration +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 8 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 75 + behavior: + scaleDown: + # 300s: POV reproduction tasks are relatively short-lived + stabilizationWindowSeconds: 300 + scaleUp: + stabilizationWindowSeconds: 90 + policies: + - type: Pods + value: 2 + periodSeconds: 60 diff --git a/deployment/k8s/charts/seed-gen/templates/deployment.yaml b/deployment/k8s/charts/seed-gen/templates/deployment.yaml index 8e80a533..86ff5326 100644 --- a/deployment/k8s/charts/seed-gen/templates/deployment.yaml +++ b/deployment/k8s/charts/seed-gen/templates/deployment.yaml @@ -6,7 +6,9 @@ metadata: labels: app: seed-gen spec: + {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount | default 1 }} + {{- end }} selector: matchLabels: app: seed-gen diff --git a/deployment/k8s/charts/seed-gen/templates/hpa.yaml b/deployment/k8s/charts/seed-gen/templates/hpa.yaml new file mode 100644 index 00000000..8367674a --- /dev/null +++ b/deployment/k8s/charts/seed-gen/templates/hpa.yaml @@ -0,0 +1 @@ +{{ include "buttercup.hpa" . }} diff --git a/deployment/k8s/charts/seed-gen/values.yaml b/deployment/k8s/charts/seed-gen/values.yaml index cb0e95fa..13bd6192 100644 --- a/deployment/k8s/charts/seed-gen/values.yaml +++ b/deployment/k8s/charts/seed-gen/values.yaml @@ -20,3 +20,16 @@ healthCheck: timeoutSeconds: 10 # Allow 2 failures before restarting failureThreshold: 2 + +# Autoscaling configuration +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 4 + targetCPUUtilizationPercentage: 75 + behavior: + scaleDown: + # 300s: seed generation tasks are relatively short-lived + stabilizationWindowSeconds: 300 + scaleUp: + stabilizationWindowSeconds: 120 diff --git a/deployment/k8s/charts/task-downloader/templates/deployment.yaml b/deployment/k8s/charts/task-downloader/templates/deployment.yaml index 10ef9e6e..5cfcbdd7 100644 --- a/deployment/k8s/charts/task-downloader/templates/deployment.yaml +++ b/deployment/k8s/charts/task-downloader/templates/deployment.yaml @@ -3,7 +3,9 @@ kind: Deployment metadata: name: {{ .Release.Name }}-task-downloader spec: + {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount | default 1 }} + {{- end }} selector: matchLabels: app: task-downloader diff --git a/deployment/k8s/charts/task-downloader/templates/hpa.yaml b/deployment/k8s/charts/task-downloader/templates/hpa.yaml new file mode 100644 index 00000000..8367674a --- /dev/null +++ b/deployment/k8s/charts/task-downloader/templates/hpa.yaml @@ -0,0 +1 @@ +{{ include "buttercup.hpa" . }} diff --git a/deployment/k8s/charts/task-downloader/values.yaml b/deployment/k8s/charts/task-downloader/values.yaml index 8690258f..97eab549 100644 --- a/deployment/k8s/charts/task-downloader/values.yaml +++ b/deployment/k8s/charts/task-downloader/values.yaml @@ -8,3 +8,16 @@ resources: requests: cpu: 100m memory: 256Mi + +# Autoscaling configuration +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 80 + behavior: + scaleDown: + # 300s: download tasks are short-lived and can tolerate faster scale-down + stabilizationWindowSeconds: 300 + scaleUp: + stabilizationWindowSeconds: 180 diff --git a/deployment/k8s/charts/tracer-bot/templates/deployment.yaml b/deployment/k8s/charts/tracer-bot/templates/deployment.yaml index 534b1e34..739cbc49 100644 --- a/deployment/k8s/charts/tracer-bot/templates/deployment.yaml +++ b/deployment/k8s/charts/tracer-bot/templates/deployment.yaml @@ -6,7 +6,9 @@ metadata: labels: app: tracer-bot spec: + {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount | default 1 }} + {{- end }} selector: matchLabels: app: tracer-bot diff --git a/deployment/k8s/charts/tracer-bot/templates/hpa.yaml b/deployment/k8s/charts/tracer-bot/templates/hpa.yaml new file mode 100644 index 00000000..8367674a --- /dev/null +++ b/deployment/k8s/charts/tracer-bot/templates/hpa.yaml @@ -0,0 +1 @@ +{{ include "buttercup.hpa" . }} diff --git a/deployment/k8s/charts/tracer-bot/values.yaml b/deployment/k8s/charts/tracer-bot/values.yaml index c3b817b2..256e4802 100644 --- a/deployment/k8s/charts/tracer-bot/values.yaml +++ b/deployment/k8s/charts/tracer-bot/values.yaml @@ -24,3 +24,17 @@ healthCheck: timeoutSeconds: 10 # Allow 2 failures before restarting failureThreshold: 2 + +# Autoscaling configuration +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 6 + targetCPUUtilizationPercentage: 75 + targetMemoryUtilizationPercentage: 80 + behavior: + scaleDown: + # 600s: long-running trace analysis should not be interrupted by premature scale-down + stabilizationWindowSeconds: 600 + scaleUp: + stabilizationWindowSeconds: 120 diff --git a/deployment/k8s/templates/_helpers.tpl b/deployment/k8s/templates/_helpers.tpl index 5ee3a1e7..71200380 100644 --- a/deployment/k8s/templates/_helpers.tpl +++ b/deployment/k8s/templates/_helpers.tpl @@ -217,3 +217,45 @@ Usage: {{- include "buttercup.apiKeySecretVolume" (dict "secretName" "litellm-ap secret: secretName: {{ .secretName }} {{- end -}} + +{{/* +Define a shared HorizontalPodAutoscaler template for all services. +Uses .Chart.Name to dynamically resolve the service name from each subchart. +Usage (in subchart's hpa.yaml): {{ include "buttercup.hpa" . }} +*/}} +{{- define "buttercup.hpa" -}} +{{- if and .Values.enabled .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }} + labels: + app: {{ .Chart.Name }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ .Release.Name }}-{{ .Chart.Name }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} + {{- with .Values.autoscaling.behavior }} + behavior: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end -}} diff --git a/deployment/k8s/values.yaml b/deployment/k8s/values.yaml index 4995b478..30847d0a 100644 --- a/deployment/k8s/values.yaml +++ b/deployment/k8s/values.yaml @@ -104,6 +104,18 @@ volumes: size: "1Gi" accessMode: ReadWriteOnce +# Autoscaling Configuration +# Each service can enable HPA by setting autoscaling.enabled=true in its values +# Example: +# fuzzer-bot: +# autoscaling: +# enabled: true +# minReplicas: 1 +# maxReplicas: 10 +# Prerequisites: +# - metrics-server must be installed in the cluster +# - For Minikube: minikube addons enable metrics-server +# - For AKS: metrics-server is pre-installed # Dind daemon chart configuration dind-daemon: