Spaces:
Sleeping
Sleeping
infra(k8s): add HorizontalPodAutoscaler for api (CPU-based) and worker (queue-depth-based)
Browse files- kubernetes/hpa.yaml +145 -0
kubernetes/hpa.yaml
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# HorizontalPodAutoscaler for the AI Code Review Agent
|
| 2 |
+
#
|
| 3 |
+
# Two HPAs:
|
| 4 |
+
#
|
| 5 |
+
# api-hpa — scales the FastAPI pod on CPU utilisation.
|
| 6 |
+
# Rationale: API pods are stateless and CPU-bound during
|
| 7 |
+
# request parsing and response serialisation. CPU at 60%
|
| 8 |
+
# is a reliable proxy for saturation without over-provisioning.
|
| 9 |
+
#
|
| 10 |
+
# worker-hpa — scales the Celery worker pod on a custom metric:
|
| 11 |
+
# the depth of the Celery task queue exposed via Prometheus.
|
| 12 |
+
# Rationale: CPU is a lagging indicator for queue workers —
|
| 13 |
+
# a worker sitting idle waiting on an LLM response shows low
|
| 14 |
+
# CPU but the queue may be backing up. Queue depth is a
|
| 15 |
+
# leading indicator that triggers scale-out before jobs pile up.
|
| 16 |
+
#
|
| 17 |
+
# Prerequisites:
|
| 18 |
+
# - metrics-server installed in the cluster (for CPU-based HPA)
|
| 19 |
+
# - Prometheus Adapter installed and configured to expose
|
| 20 |
+
# celery_queue_length as a custom metric (for worker HPA)
|
| 21 |
+
# - kubectl apply -f kubernetes/hpa.yaml
|
| 22 |
+
|
| 23 |
+
apiVersion: autoscaling/v2
|
| 24 |
+
kind: HorizontalPodAutoscaler
|
| 25 |
+
metadata:
|
| 26 |
+
name: api-hpa
|
| 27 |
+
namespace: default
|
| 28 |
+
labels:
|
| 29 |
+
app: ai-code-review-agent
|
| 30 |
+
component: api
|
| 31 |
+
spec:
|
| 32 |
+
scaleTargetRef:
|
| 33 |
+
apiVersion: apps/v1
|
| 34 |
+
kind: Deployment
|
| 35 |
+
name: api # must match metadata.name in deployment.yaml
|
| 36 |
+
|
| 37 |
+
minReplicas: 2 # always run at least 2 for availability
|
| 38 |
+
maxReplicas: 10 # cap prevents runaway LLM cost under attack
|
| 39 |
+
|
| 40 |
+
metrics:
|
| 41 |
+
- type: Resource
|
| 42 |
+
resource:
|
| 43 |
+
name: cpu
|
| 44 |
+
target:
|
| 45 |
+
type: Utilization
|
| 46 |
+
averageUtilization: 60 # scale out when avg CPU across pods exceeds 60%
|
| 47 |
+
|
| 48 |
+
- type: Resource
|
| 49 |
+
resource:
|
| 50 |
+
name: memory
|
| 51 |
+
target:
|
| 52 |
+
type: Utilization
|
| 53 |
+
averageUtilization: 75 # secondary guard — large repos spike memory
|
| 54 |
+
|
| 55 |
+
behavior:
|
| 56 |
+
scaleUp:
|
| 57 |
+
stabilizationWindowSeconds: 30 # react quickly to traffic spikes
|
| 58 |
+
policies:
|
| 59 |
+
- type: Pods
|
| 60 |
+
value: 2 # add at most 2 pods per scale event
|
| 61 |
+
periodSeconds: 30
|
| 62 |
+
scaleDown:
|
| 63 |
+
stabilizationWindowSeconds: 300 # wait 5 min before scaling down
|
| 64 |
+
policies: # prevents flapping after burst traffic
|
| 65 |
+
- type: Pods
|
| 66 |
+
value: 1 # remove at most 1 pod per scale event
|
| 67 |
+
periodSeconds: 60
|
| 68 |
+
|
| 69 |
+
---
|
| 70 |
+
|
| 71 |
+
apiVersion: autoscaling/v2
|
| 72 |
+
kind: HorizontalPodAutoscaler
|
| 73 |
+
metadata:
|
| 74 |
+
name: worker-hpa
|
| 75 |
+
namespace: default
|
| 76 |
+
labels:
|
| 77 |
+
app: ai-code-review-agent
|
| 78 |
+
component: worker
|
| 79 |
+
spec:
|
| 80 |
+
scaleTargetRef:
|
| 81 |
+
apiVersion: apps/v1
|
| 82 |
+
kind: Deployment
|
| 83 |
+
name: worker # must match metadata.name in deployment.yaml
|
| 84 |
+
|
| 85 |
+
minReplicas: 1 # one worker is enough at idle
|
| 86 |
+
maxReplicas: 8 # each worker holds 2 Celery slots (--concurrency=2)
|
| 87 |
+
# so 8 pods = 16 concurrent analysis tasks max
|
| 88 |
+
|
| 89 |
+
metrics:
|
| 90 |
+
- type: Pods
|
| 91 |
+
pods:
|
| 92 |
+
metric:
|
| 93 |
+
name: celery_queue_length # exposed by Prometheus Adapter
|
| 94 |
+
# configure in prometheus-adapter ConfigMap:
|
| 95 |
+
# - seriesQuery: 'celery_queue_length{queue=~"high|low"}'
|
| 96 |
+
# name: { as: "celery_queue_length" }
|
| 97 |
+
# metricsQuery: 'sum(celery_queue_length{<<.LabelMatchers>>})'
|
| 98 |
+
target:
|
| 99 |
+
type: AverageValue
|
| 100 |
+
averageValue: "5" # scale out when avg queue depth per pod exceeds 5 jobs
|
| 101 |
+
|
| 102 |
+
- type: Resource
|
| 103 |
+
resource:
|
| 104 |
+
name: cpu
|
| 105 |
+
target:
|
| 106 |
+
type: Utilization
|
| 107 |
+
averageUtilization: 70 # fallback CPU metric if Prometheus Adapter unavailable
|
| 108 |
+
|
| 109 |
+
behavior:
|
| 110 |
+
scaleUp:
|
| 111 |
+
stabilizationWindowSeconds: 15 # workers need to react faster than API pods
|
| 112 |
+
policies:
|
| 113 |
+
- type: Pods
|
| 114 |
+
value: 2
|
| 115 |
+
periodSeconds: 15 # burst: add 2 workers every 15s if queue keeps growing
|
| 116 |
+
scaleDown:
|
| 117 |
+
stabilizationWindowSeconds: 600 # wait 10 min — draining a long analysis task
|
| 118 |
+
policies: # takes time; premature scale-down kills in-flight jobs
|
| 119 |
+
- type: Pods
|
| 120 |
+
value: 1
|
| 121 |
+
periodSeconds: 120
|
| 122 |
+
|
| 123 |
+
---
|
| 124 |
+
|
| 125 |
+
# Prometheus Adapter ConfigMap reference (apply separately if not already present)
|
| 126 |
+
# This tells the adapter how to translate the Prometheus metric into a
|
| 127 |
+
# Kubernetes custom metric that the worker HPA can consume.
|
| 128 |
+
#
|
| 129 |
+
# apiVersion: v1
|
| 130 |
+
# kind: ConfigMap
|
| 131 |
+
# metadata:
|
| 132 |
+
# name: prometheus-adapter-config
|
| 133 |
+
# namespace: monitoring
|
| 134 |
+
# data:
|
| 135 |
+
# config.yaml: |
|
| 136 |
+
# rules:
|
| 137 |
+
# - seriesQuery: 'celery_queue_length{queue=~"high|low"}'
|
| 138 |
+
# resources:
|
| 139 |
+
# overrides:
|
| 140 |
+
# namespace: { resource: namespace }
|
| 141 |
+
# pod: { resource: pod }
|
| 142 |
+
# name:
|
| 143 |
+
# matches: "celery_queue_length"
|
| 144 |
+
# as: "celery_queue_length"
|
| 145 |
+
# metricsQuery: 'sum(celery_queue_length{<<.LabelMatchers>>})'
|