Inleiding
Autoscaling in Kubernetes wordt in veel teams nog steeds gelijkgesteld aan “HPA op CPU%”. Dat werkt prima voor een subset van stateless web workloads, maar het breekt (of schaal-in/out wordt instabiel) zodra CPU en geheugen geen goede proxy meer zijn voor werkdruk. KEDA is precies in dat gat ontstaan: workloads schalen op basis van events en externe signalen (queue depth, stream lag, backlog, custom metrics), inclusief de mogelijkheid om naar nul replicas te schalen wanneer er géén werk is. KEDA vervangt de HPA niet; het bouwt er bovenop en gebruikt de Kubernetes scaling- en metrics-API’s als interface tussen event sources en de bestaande autoscaling control loop.
Waarom autoscaling verder gaat dan CPU en geheugen
Wat de HPA daadwerkelijk doet
De Kubernetes Horizontal Pod Autoscaler (HPA) is een API-object plus controller die periodiek de gewenste replica-count van een scale target (bijv. Deployment of StatefulSet) bijstelt zodat gemeten metrics rond een target uitkomen. Die “periodiek” is expliciet: Kubernetes implementeert HPA als een control loop die op een sync-period draait (default 15 seconden) en dus altijd reactief is op observaties, niet continu “event-driven” in de strikte zin.
De HPA haalt metrics op via verschillende routes, afhankelijk van het type metric: resource metrics (CPU/memory) via de resource metrics API; andere metrics via de custom metrics API of external metrics API. In de praktijk betekent dit dat je voor CPU/memory vaak de Metrics Server nodig hebt, en voor custom/external metrics één of meer metrics adapters die die API’s implementeren.
De scaling-beslissing zelf is (conceptueel) relatief eenvoudig: de controller rekent met een ratio tussen current metric value en desired metric value en schaalt naar ceil(currentReplicas * currentMetricValue / desiredMetricValue), met tolerances en stabilisatie-mechanismen om “flapping” te dempen.
Waarom CPU en geheugen vaak slechte proxies zijn voor werkdruk
CPU- en geheugenmeting zijn resource consumption signals, niet per se work signals. Dat verschil wordt zichtbaar zodra de bottleneck elders zit dan compute:
Bij I/O-gebonden services kan de werkdruk stijgen (meer requests, meer queueing) terwijl CPU relatief laag blijft; bij upstream/back-end latency of rate limits kan throughput dalen zonder dat CPU stijgt. Voor dit soort workloads is het vaak zinvoller om te schalen op een metric die direct de “work backlog” representeert (bijv. queue length, inflight requests, consumer lag) dan op CPU%.
Zelfs als CPU wél correleert met load, heeft HPA-resource-scaling een modelafhankelijkheid: CPU-utilization wordt berekend als percentage van de resource request. Als containers geen requests hebben gezet, kan de autoscaler voor die metric niets doen. Dat maakt “HPA op CPU%” niet alleen een proxy-probleem, maar ook een configuratie- en discipline-probleem (requests/limits).
De stap naar event- en metric-gedreven scaling
Kubernetes ondersteunt expliciet meerdere metric-soorten, waaronder object metrics en external metrics. External metrics zijn bedoeld voor metrics die niet aan een Kubernetes object gekoppeld zijn (bijv. queue depth in een externe broker) en worden via external.metrics.k8s.io aangeboden.
In theorie kun je met “HPA + een metrics adapter” al ver komen. In de praktijk zitten de fricties meestal in drie delen:
- het consistent en veilig ontsluiten van event-source metrics naar de Kubernetes metrics API’s
- het modelleren van “activatie” (0→1) versus normale scaling (1→N)
- het beheren van een grote catalog aan event sources (queues, streams, CI/CD queues, databases, custom APIs) zonder telkens maatwerk adapters te beheren
KEDA positioneert zich precies op dat kruispunt: niet als nieuwe scheduler, maar als control-plane uitbreiding die event sources vertaalt naar HPA-consumeerbare metrics en daarnaast de 0↔1 “activatiefase” afhandelt.
Wat KEDA is en waarom het bestaat
Definitie en kernidee
KEDA (Kubernetes Event-driven Autoscaling) is een Kubernetes-native autoscaling component die workloads kan schalen op basis van “real-world events” zoals queue depth of incoming request rate. Het is ontworpen om naast standaard Kubernetes componenten te draaien, in het bijzonder naast de HPA, en breidt de soort signals uit waarop je kunt schalen zonder dat je voor elke event source een eigen integratie hoeft te bouwen.
In het KEDA-model definieer je intentie via Custom Resources (o.a. ScaledObject, ScaledJob) en definieer je één of meerdere “triggers” (scalers) die metrics uit een event source halen. KEDA vertaalt die naar metrics voor autoscaling.
Projectstatus en ontwerpfilosofie
KEDA is een project onder de Cloud Native Computing Foundation en is inmiddels “Graduated” binnen de CNCF maturity levels (acceptance 2020, incubating 2021, graduated 2023). Dat is relevant omdat autoscaling control-plane software direct invloed heeft op stabiliteit en kosten, en dus baat heeft bij volwassen governance en brede adoptie.
De kernfilosofie: KEDA probeert bestaande Kubernetes primitives (HPA, metrics API’s, /scale subresource) te hergebruiken in plaats van een parallel autoscaling-systeem te introduceren. KEDA “werkt met de grain” van Kubernetes: CRDs voor intentie, controllers voor reconcile loops, en de API aggregation layer voor metrics.
Historisch is KEDA gestart als samenwerking tussen Microsoft en Red Hat; dat zegt vooral iets over de oorsprong, maar het ontwerp is expliciet vendor-neutraal (veel verschillende event sources en auth-methoden).
KEDA als autoscaling control-plane, niet als scheduler
Belangrijk onderscheid: KEDA beslist niet waar een Pod draait en plant geen Pods; het beïnvloedt primair hoeveel replicas een workload moet hebben, via dezelfde schaalinterfaces als de HPA. Scheduling blijft volledig bij de Kubernetes scheduler; node-capaciteit blijft een aparte laag (cluster/node autoscaling).
In de “deployment scaling” modus is de HPA nog steeds degene die 1→N scaling uitvoert. KEDA stuurt vooral (a) activatie van 0→1 en (b) het leveren van external metrics aan de HPA.
Architectuur en werking van KEDA
Componenten en controlelussen
Conceptueel bestaat KEDA uit een set control-plane componenten:
- KEDA Operator: bewaakt KEDA CRDs en beheert de lifecycle van scaling-configuratie, inclusief het activeren/deactiveren van workloads afhankelijk van triggers.
- Metrics Server / Metrics Adapter: biedt external metrics aan Kubernetes (vooral voor HPA-consumptie) zodat HPA kan schalen op niet-resource metrics.
- Scalers (triggers): integraties die metrics uit event sources halen; scalers bepalen tevens of een workload “actief” is (voor 0↔1).
- Admission webhooks: valideren (en in sommige gevallen muteren) KEDA-resources om misconfiguraties vroeg te detecteren.
Operationeel draait dit als meerdere reconcile loops: de operator reconcile’t ScaledObjects/ScaledJobs; de metrics adapter bedient requests vanuit de Kubernetes API aggregation layer; HPA reconcile’t zijn eigen desired replicas op basis van metrics.
ScaledObject: event-driven scaling voor replica-based workloads
Een ScaledObject linkt een scale target (meestal Deployment of StatefulSet) aan één of meerdere triggers. In de spec definieer je o.a.:
scaleTargetRefnaar het object met/scalesubresourceminReplicaCount(default 0) enmaxReplicaCountpollingInterval(default 30s) waarmee KEDA de event source checkt, vooral relevant wanneer replicas=0cooldownPeriod(default 300s) waarmee KEDA wacht na laatste “active” trigger voordat het terug naar 0 schaalt
Wanneer je een ScaledObject definieert, is het model dat KEDA de event source monitort en metrics “aanlevert” aan Kubernetes/HPA zodat HPA de scaling van 1→N kan uitvoeren met de standaard HPA-controller.
Een minimale pseudo-config (vereenvoudigd, niet bedoeld als install-handleiding) ziet er conceptueel zo uit:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
spec:
scaleTargetRef:
name: worker-deployment
minReplicaCount: 0
maxReplicaCount: 50
pollingInterval: 30
cooldownPeriod: 300
triggers:
- type: <event-source>
metadata:
threshold: "<target>"
activationThreshold: "<activation>"
De semantiek van threshold versus activationThreshold is belangrijk en komt terug bij “event-driven” scaling: KEDA maakt onderscheid tussen de activatiegrens (0↔1) en de reguliere HPA target (1↔N).
ScaledJob: event-driven scaling voor job-based processing
Voor batch- of worker-achtige code die beter past bij Jobs dan bij een Deployment biedt KEDA ScaledJob. Het expliciete ontwerpdoel: per event (of per eenheid werk) een Job starten, die één event oppakt, verwerkt, en klaar is. KEDA’s docs benadrukken dit als alternatief om long-running executions te verwerken: in plaats van één deployment die meerdere events verwerkt, schedule je per event een aparte Job die naar completion gaat.
Dat model verandert de failure- en lifecycle-eigenschappen:
- Bij Deployments kunnen Pods bij scale-down worden beëindigd terwijl ze nog werk doen (tenzij je zelf drain/graceful shutdown goed regelt).
- Bij Jobs is de intentie “run-to-completion”; pods verdwijnen pas nadat het werk klaar is (of faalt), waardoor “scale-down” geen directe terminatie is maar vooral “geen nieuwe Jobs starten”.
KEDA’s ScaledJob-spec heeft bovendien job-specifieke opties, zoals history limits voor succesvolle en gefaalde jobs.
Een vereenvoudigde pseudo-config:
apiVersion: keda.sh/v1alpha1
kind: ScaledJob
spec:
maxReplicaCount: 100
pollingInterval: 30
triggers:
- type: <event-source>
metadata:
value: "<work-per-job>"
jobTargetRef:
template:
spec:
containers:
- name: worker
image: example/worker
restartPolicy: Never
Trigger-mechanisme en authenticatie
KEDA “scalers” doen twee dingen: ze bepalen of een workload actief moet worden (activatie/deactivatie) en ze leveren metrics waarmee scaling kan gebeuren.
Authenticatie naar event sources wordt vaak losgetrokken via TriggerAuthentication en ClusterTriggerAuthentication, zodat secrets/identity niet in elk ScaledObject herhaald hoeven te worden en governance eenvoudiger wordt.
KEDA-documentatie beschrijft daarnaast operationele knobs die direct relevant zijn voor betrouwbaarheid bij externe event sources, zoals standaard HTTP timeouts (default 3 seconden) voor scalers die HTTP requests doen, en het kunnen instellen van proxies/timeouts via environment variables.
Metrics flow: HPA, metrics API’s en de API aggregation layer
Er zitten twee overlappende “polling” ritmes in dit systeem:
- de HPA-controller vraagt metrics op met zijn eigen sync-period (default 15s)
- KEDA pollt triggers per ScaledObject met
pollingInterval(default 30s), en dat geldt extra expliciet voor 0→1 scaling.
Die mismatch creëert een praktisch probleem: als de HPA elke 15s metrics opraagt, wil je niet noodzakelijk elke 15s een dure API call naar een externe queue/monitoringsysteem doen. Daarom ondersteunt KEDA caching van metrics (useCachedMetrics) zodat de metrics adapter HPA-requests kan beantwoorden met gecachte waarden die op pollingInterval zijn opgehaald.
Een tweede, meer “cluster-wide” architectuurpunt: de external metrics API (external.metrics.k8s.io) wordt via Kubernetes API aggregation aangeboden. In de praktijk betekent dat dat er een cluster-wide APIService geregistreerd wordt. KEDA’s eigen documentatie benoemt daarom expliciet dat je maar één actieve metrics server kunt hebben die external.metrics.k8s.io bedient, en dat KEDA’s metrics server die rol moet hebben als je KEDA gebruikt.
Dat is geen theoretisch detail: als de APIService niet “Available” is (bijvoorbeeld netwerkpolicies, service mesh side-effects, proxy settings), kun je errors krijgen zoals FailedDiscoveryCheck, en verschillende control-plane paden (HPA scaling, API discovery, soms zelfs namespace deletion) kunnen last krijgen van het ontbreken van die aggregated API.
Event-driven autoscaling uitgelegd
Wat is een “event” in deze context?
“Event-driven autoscaling” in KEDA betekent niet dat Kubernetes ineens een push-gebaseerde scheduler wordt. Het betekent dat de schaalbeslissing gevoed wordt door event-adjacent signals: queue length, pending jobs, consumer lag, backlog, inflight requests, of metrics die door een event-systeem worden geproduceerd. KEDA noemt dit expliciet “real-world events” zoals queue messages of incoming requests.
Een bruikbare engineering-definitie in dit kader:
Een event is een discrete aanwijzing dat er werk op de wachtrij staat of dat er processing capacity nodig is, zónder dat je eerst CPU-/memory-pressure hoeft te observeren.
In concrete systemen zijn de meeste scaling signals echter geen discrete events maar samenvattende metrics (gauge/counter) over events: queue depth (gauge), lag (gauge), throughput (rate), of pending/inflight count. KEDA vertaalt die naar external metrics voor HPA-consumptie.
Push versus pull signals
In standaard KEDA-scaling is de dominante modus pull: KEDA pollt event sources op een interval (pollingInterval) en levert metrics aan HPA; HPA pollt op zijn interval. Dat is “event-driven” in de zin van event-sources als input, maar technisch nog steeds interval-based control loops.
KEDA kent daarnaast het concept van external push scalers: een scaler kan via een gRPC mechanisme de activatiestatus pushen naar KEDA (StreamIsActive). In de ScaledObject-spec staat expliciet dat dit een uitzondering is op de normale activationTarget logica en dat external push scalers activation status direct kunnen pushen, ongeacht metric value/activation target.
Die push-variant is relevant voor workloads waar polling-latency te duur is (bijvoorbeeld wanneer je sneller dan je polling interval van 0 naar 1 wilt). In de praktijk blijf je echter meestal met polling te maken hebben: ook push-scalers zijn onderdeel van systemen die state bijhouden en moeten integreren met de HPA control loop (die nog steeds periodiek beslist).
Queue length, lag, backlog en throughput: signalen en semantiek
Veel KEDA-scenarios draaien om queue- en stream-systemen. De belangrijkste metrics die daar terugkomen:
Queue length / backlog Aantal items dat nog verwerkt moet worden. Dit is vaak de meest directe “work signal”: het representeert onuitgevoerd werk. KEDA’s eigen docs gebruiken dit patroon expliciet: bij geen pending messages kan een deployment naar 0; bij message arrival activeert KEDA de deployment, en bij meer messages kan KEDA metrics aan HPA leveren om naar meer replicas te schalen.
Consumer lag Bij streams (log-based systemen) is “lag” een maat voor achterstand: consumer position loopt achter op producer position. Semantisch is dat backlog, maar met nuance: lag kan “stale” zijn door rebalances en offsets, en lag zegt niets over processing time per event.
Throughput Work-per-time of events-per-second. Dit is vaak stabieler dan queue length op korte vensters, maar kan minder geschikt zijn als je doel is backlog weg te werken binnen een tijdbudget.
Een nuttige lens om queue length/backlog relateerbaar te maken aan performance is Little’s Law: in steady state geldt (L = \lambda W), waarbij (L) het gemiddeld aantal items in het systeem is, ( \lambda ) de arrival rate en (W) de gemiddelde tijd in het systeem. De queueing-theorie literatuur benadrukt dat dit onder zeer algemene voorwaarden geldt en het verband legt tussen “hoeveel werk ligt er” en “hoe snel verwerk je het” via doorlooptijd.
Waarom dit voor autoscaling relevant is: als je een SLO hebt op “maximale wachttijd” of “backlog moet binnen X minuten wegwerken”, kun je (met voldoende meetbaarheid) een target op backlog afleiden in plaats van op CPU. Dit is geen magie: service time variance, burstiness en batching maken het lastig, maar het is conceptueel wél een betere aansluiting op het domeinprobleem dan CPU%.
Activatie versus scaling: twee regimes
KEDA formaliseert een split die in veel autoscaling setups impliciet is:
- Activatiefase (0↔1): KEDA operator beslist of de workload van/to 0 moet. Dit baseert KEDA op “IsActive” en scalers kunnen daarvoor aparte activation thresholds hebben.
- Scalingfase (1↔N): zodra er 1 replica is, laat KEDA de reguliere scalingbeslissing aan de HPA-controller op basis van metrics die KEDA expose’t.
Deze split verklaart ook waarom je soms “rare” situaties ziet als activationThreshold en threshold niet consistent gekozen zijn. In de OpenShift/KEDA-derivaten wordt expliciet beschreven dat activationThreshold “meer prioriteit” kan krijgen: als je scaling threshold laag is maar activation threshold hoog, kan KEDA een workload naar 0 houden terwijl HPA op basis van scaling threshold eigenlijk omhoog zou willen.
Stabiliteit: flapping, hysteresis en stabilisatievensters
Zodra je op externe metrics gaat schalen (zeker als die noisy of delayed zijn), loop je hard tegen control-loop stabiliteit aan. Kubernetes documenteert expliciet “thrashing/flapping” als fenomeen en biedt stabilisatie via een “stabilization window” om replica-fluctuaties te dempen wanneer metrics fluctueren.
KEDA voegt daar eigen timing knobs aan toe voor 0→1 en 1→0 (pollingInterval, cooldownPeriod, activationThreshold), maar het “klassieke” dempen van 1↔N gebeurt nog steeds primair in HPA-behavior (stabilization windows, scaling policies).
Workload-typen waar KEDA vaak goed past
Queue-based workers en work-queues
De “canonical” KEDA workload is een worker deployment die werk uit een queue trekt. Dit is precies het scenario waarin CPU een indirect signaal is, en queue depth/backlog een direct signaal. KEDA beschrijft dit model zelf: bij geen messages kan de deployment naar 0; bij arrival activeert het; bij groeiende queue kan KEDA HPA voeden voor scale-out.
Waarom dit goed werkt:
- De event source is doorgaans de enige bron van waarheid voor “hoeveel werk is er?”.
- Replica’s zijn vaak horizontaal schaalbaar: meer consumers betekent meer parallelisme, zolang het downstream systeem het kan dragen.
- Het patroon combineert goed met “scale to zero” omdat idle workers vaak pure kosten zijn.
Een belangrijke nuance: queue-based scaling veronderstelt dat “meer consumers” zinvol is; bij locked-message semantics, ordering constraints, of shared downstream bottlenecks kan extra parallelisme vooral extra contention opleveren. In dat geval heb je ofwel backpressure/flow control nodig of je moet op een ander signaal schalen (bijv. downstream saturation) in plaats van pure queue depth.
Background processing en asynchrone taken
Asynchrone “background” taken (mail rendering, image/video processing, indexing, ETL-achtige pipelines) hebben vaak bursty load: perioden van niets, afgewisseld met batches. Daar is scale-to-zero aantrekkelijk, maar alleen als je cold-start latency acceptabel is en als de taakverwerking idempotent kan omgaan met retries en duplicates. Scale-to-zero wordt in KEDA-documentatie expliciet gepositioneerd als mechanisme om bij geen pending work naar nul te gaan.
Event streams en consumer groups
Voor stream-based processing (log-based messaging) is de interessante metric meestal consumer lag of pending entries count. KEDA’s scalers catalog bevat bijvoorbeeld stream-gerelateerde triggers die expliciet “pending entries” als scale metric kunnen gebruiken.
Waar KEDA hier waarde toevoegt: het biedt een standaard pad om stream-lag metrics naar external.metrics.k8s.io te projecteren, inclusief activatie van 0→1. In tegenstelling tot handmatig HPA + custom adapter, heb je meestal minder glue code in de cluster.
HTTP-services met externe load-signalen
Voor klassieke request/response web services is “scale on HTTP requests” conceptueel directer dan “scale on CPU”, maar Kubernetes heeft geen native “requests per second” metric; je moet die via observability of proxies meten en aanbieden als custom/external metric.
KEDA biedt twee paden:
- schalen op metrics uit een monitoring-systeem (bijv. Prometheus queries via de Prometheus scaler)
- het HTTP add-on model: een interceptor/proxy accepteert requests, houdt ze vast als de backend op 0 staat, en levert “pending requests” metrics aan KEDA via een external scaler; de operator maakt de benodigde resources aan. In de KEDA HTTP add-on aankondiging staat dit flow-model expliciet: interceptor houdt requests vast bij scaled-to-zero, external scaler vertaalt queue metrics naar KEDA, en een operator maakt
HTTPScaledObjectresources.
Dit is nuttig, maar het heeft een fundamentele trade-off: je verschuift de “boundary” waar requests binnenkomen naar een component die moet kunnen bufferen en correct moet omgaan met timeouts, retries en backpressure. Dat is architectonisch prima zolang je het expliciet ontwerpt; het is niet “gratis”.
Scheduled en bursty workloads
KEDA ondersteunt time-window scaling via de Cron scaler: je definieert een tijdsrange waarin je workload naar een gewenste replica-count gaat. KEDA benoemt daarbij expliciet wat Cron scaler niet doet: het is niet bedoeld om “recurring schedule” events als load te gebruiken; het zet replicas binnen een window, maar blijft geen event scheduler.
Dit is praktisch voor voorspelbare piekuren (kantooruren, batchvensters), of voor “warm capacity” (bijv. preventief 1 replica tijdens een venster om cold starts te vermijden).
Waar KEDA minder geschikt is en hoe het zich verhoudt tot traditionele HPA
Wanneer KEDA geen goede keuze is
KEDA is geen universele oplossing. Vier categorieën vallen in de praktijk vaak tegen, tenzij je specifieke mitigaties hebt:
Stateful workloads KEDA kan StatefulSets schalen (ook volgens de docs), maar “kunnen” is niet hetzelfde als “willen”. Stateful systemen hebben vaak constraints: shard ownership, data locality, warm caches, replication, compaction. Een extern signaal zoals queue depth zegt dan weinig over de kosten van scale-out (rebalancing, data warmup) en kan resulteren in thrashing of onvoorspelbare latency.
Latency-kritische workloads Scale-to-zero plus “on demand” activatie betekent cold starts. KEDA formaliseert 0↔1 activatie, maar het kan niet de inherent koude starttijd van je container/image/netwerk/pull/warmup wegtoveren. Als je SLO’s eisen dat de eerste request altijd binnen tientallen milliseconden wordt bediend, dan is “0→1 bij demand” vaak architectonisch ongeschikt, tenzij je buffering/proxying (HTTP add-on) inzet en accepteren dat requests even in een queue staan.
CPU-bound batch jobs zonder duidelijke backpressure Als je workload pure compute is (bijv. batch rendering) en de input geen duidelijke queue/backlog metric heeft, dan is CPU (of een internal metric zoals “tasks in progress”) soms juist wél het beste signaal. KEDA kan hier nog steeds werken (bijv. via metrics-api of Prometheus scaler), maar de toegevoegde waarde t.o.v. HPA met custom metrics kan kleiner zijn, terwijl je wél extra componenten introduceert.
Workloads met slecht gedefinieerde schaal-signalen Als “meer replicas” geen voorspelbaar effect heeft (bijv. door downstream rate limits, shared DB bottleneck, globale locks), dan is elk autoscaling-systeem fragiel. Je moet dan eerst de architectuur schaalbaar maken (backpressure, partitioning, load shedding) en pas daarna autoscaling doen. Backpressure als concept is juist bedoeld om systemen te behoeden voor collapse door bounded queues en door de consument de flow te laten bepalen.
KEDA versus traditionele HPA
Een vendor-neutrale manier om KEDA vs HPA te zien:
De HPA is het native mechanisme dat replica counts aanpast op basis van metrics. Het ondersteunt resource metrics en ook custom/external metrics via aggregated APIs.
KEDA is een ecosysteemlaag bovenop die HPA-capability:
- KEDA levert (via zijn metrics adapter) external metrics aan de HPA.
- KEDA voegt een expliciete activation phase toe (0↔1) voor scale-to-zero, terwijl HPA in veel vormen praktisch op ≥1 blijft of afhankelijk is van metrics availability. KEDA specificeert
minReplicaCountmet default 0 en beschrijft activation phase expliciet. - KEDA levert een catalog aan scalers (integraties) en een uniform CRD-model in plaats van “HPA + losse adapter + custom metric mapping per backend”.
Maar er is ook een duidelijke trade-off: KEDA introduceert een extra control-plane layer én het maakt je cluster afhankelijk van de availability van een aggregated API (external.metrics.k8s.io) die cluster-wide impact kan hebben. KEDA’s troubleshooting docs tonen concrete failure modes rond FailedDiscoveryCheck en netwerk/proxy/service mesh problemen.
KEDA, VPA en node autoscaling
Vertical Pod Autoscaler (VPA) heeft een ander doel: het past resource requests/limits aan op basis van gebruik en doet dat via components zoals een admission controller (mutating webhook) die bij pod creatie aanbevelingen toepast. Het verandert niet primair replica counts maar “size per pod”.
Dit creëert een interessant spanningsveld:
- HPA/KEDA schalen horizontaal: meer pods.
- VPA schaalt verticaal: grotere/kleinere pods.
Combinaties zijn mogelijk, maar je moet nadenken over interacties: als VPA requests omhoog zet, kan HPA/KEDA later minder pods nodig hebben (of juist meer capacity per pod), waardoor node autoscaling anders reageert.
Node autoscaling (bijv. cluster autoscaler) is weer een andere laag: die provisiont nodes voor unschedulable pods en consolideert nodes als pods verdwijnen. Kubernetes documenteert node autoscaling als mechanisme dat reageert op pods die niet gescheduled kunnen worden en nodes consolideert wanneer ze niet meer nodig zijn.
Het gevolg voor KEDA: “pod scaling” (KEDA/HPA) en “node scaling” moeten bij elkaar passen. Als KEDA agressief naar nul schaalt, kan node autoscaling nodes verwijderen en cold-start costs vergroten. Als node autoscaling conservatief is (min node pool), dan levert scale-to-zero op pods misschien minder kostenwinst op dan verwacht.
Metrics, betrouwbaarheid, kosten en ontwerpprincipes
Metrics, thresholds en timing
KEDA introduceert relatief veel “timing knobs”, en die zijn bijna altijd bepalender voor productiegedrag dan “welke scaler je gebruikt”.
pollingInterval Default 30s. Dit bepaalt hoe vaak KEDA triggers checkt, en (kritisch) hoe vaak KEDA probeert te activeren als replicas=0. Een pollingInterval van 60s betekent dus: worst-case een minuut extra idle latency voordat je überhaupt van 0→1 gaat.
HPA sync period Default 15s cluster-wide. Zelfs als KEDA pollt op 30s, kan HPA vaker metrics willen opvragen. Dat is waarom caching bestaat.
cooldownPeriod Default 300s, en het geldt alleen voor scaling naar 0. KEDA’s spec benoemt expliciet dat cooldownPeriod pas begint na een trigger en alleen relevant is voor terugschalen naar 0 (1↔N blijft HPA). Dit is je “hysteresis” knob voor de idle boundary: hoe lang blijf je warm na de laatste activiteit?
activationThreshold Niet elke scaler heeft deze parameter, maar waar hij bestaat is hij essentieel: je definieert wanneer de scaler “actief” is (0→1). KEDA introduceerde dit expliciet zodat niet elk klein beetje activiteit (bijv. één queue message) direct tot activatie leidt.
stabilization windows en scaling policies Voor 1↔N blijft HPA-leading. Kubernetes beschrijft stabilization windows als mechanisme om flapping te beperken door aanbevelingen over een window te evalueren. Daarmee voorkom je “scale down en direct weer scale up” bij noisy metrics.
Een nuttige mentale model: KEDA is vooral je “edge detector” en metric projector; HPA is je “PID-achtige” control loop voor replica counts met stabilisatie.
Prometheus-based triggers en externe metrics
In veel platformen is monitoring-telemetrie de snelste manier om “work signals” te krijgen. KEDA heeft een Prometheus scaler waarbij je scaling baseert op een Prometheus query en ook activationThreshold kunt gebruiken.
Belangrijke nuance: schalen op basis van monitoring-data betekent schalen op basis van een pipeline met eigen delays (scrape interval, aggregation, query resolution). Dat kan prima zijn als je loadspikes in minuten komen, maar is riskant bij sub-seconde bursts. Als je te agressief op “requests per second” gaat schalen met metrics die per minuut gescraped worden, krijg je of under-scaling of oscillatie. (Dit is context-afhankelijk; de discussie is niet “Prometheus is slecht”, maar “meet- en sample-intervals zijn onderdeel van je control loop”.)
KEDA expose’t zelf ook metrics die je kunt scrapen: operator, webhooks en metrics adapter hebben Prometheus endpoints en er zijn pre-made Grafana dashboards om de metrics server en scale target behavior te visualiseren. Dat is belangrijk voor debugging en SRE-operaties.
Failure modes bij slechte metrics en externe dependencies
Event-driven autoscaling is zo betrouwbaar als het zwakste stuk in de meetketen. In KEDA-context zijn er typische failure modes:
Control-plane kan external metrics API niet bereiken
Als de aggregated APIService external.metrics.k8s.io niet beschikbaar is, ziet de HPA controller errors en kan scaling uitblijven. KEDA documenteert concrete symptomen (FailedDiscoveryCheck) en wijst naar netwerk/proxy/service mesh oorzaken.
Je kunt maar één “active” external metrics server hebben
Omdat external.metrics.k8s.io cluster-wide via APIService geregistreerd is, ontstaat er een single-provider constraint. KEDA benoemt dit expliciet: één actieve metric server die external.metrics.k8s.io serve’t moet de KEDA metric server zijn als je KEDA gebruikt. Dit kan conflicteren met andere oplossingen die dezelfde API willen aanbieden.
Metrics failure en HPA gedrag Kubernetes HPA documenteert dat als meerdere metrics gebruikt worden, de controller per metric desired replicas berekent en vervolgens de grootste kiest. Als metrics niet omgezet kunnen worden (bijv. fetch errors) en een scale down gesuggereerd wordt door metrics die wel werken, dan wordt scaling overgeslagen; scale up kan nog wel als een metric een hoger desired replica count geeft. Dit is een belangrijk “fail-safe” detail: bij metric errors voorkom je vaak onbedoelde scale downs, maar scale up kan nog plaatsvinden.
Timeouts en rate limits op event sources KEDA’s scalers doen vaak HTTP/gRPC calls naar externe systemen. De docs benoemen standaard HTTP timeouts (3 seconden) en de mogelijkheid om timeouts te tunen. In praktijk betekent dit: rate limiting of tijdelijke brownouts in je event source kunnen je scaling loop ontregelen.
False positives, false negatives en de kosten van “fout schalen”
Autoscaling-fouten vallen grofweg in twee klassen:
False positives (over-scaling) Je signaal zegt “meer werk”, maar de extra pods leveren geen extra throughput (downstream bottleneck) of zijn gebaseerd op noisy spikes. Gevolg: hogere kosten, mogelijk hogere load op backends, en soms lagere stabiliteit door thrash. HPA stabilisatievensters dempen dit gedeeltelijk, maar lossen het niet op als je metric semantisch verkeerd is.
False negatives (under-scaling) Je signaal mist werk (bijv. sampling delay, verkeerde query, te hoge activationThreshold), waardoor je te laat opschaalt. Gevolg: backlog groeit, latency stijgt, retries stapelen op, en je kunt een “retry storm” krijgen die het probleem versterkt. De activatie/scaling split in KEDA maakt dit expliciet: je kunt prima een scaleTarget “goed” tunen voor 1→N, maar als activation te conservatief is, blijf je te lang op 0.
Een pragmatische SRE-regel: als je workload niet goed degradeert bij under-provisioning (geen backpressure, wel timeouts/retries), dan moet je eerst flow-control ontwerpen voordat je autoscaling agressief inzet. Backpressure in de Reactive Streams definitie is expliciet bedoeld om te voorkomen dat “receivers” oneindig moeten bufferen; die gedachte is ook toepasbaar op queue/worker systemen.
Kosten, efficiëntie en cloud-impact
KEDA heeft twee kosten-implicaties die je niet moet onderschatten:
Scale-to-zero kan echte kostenwinst opleveren Zeker bij intermittent workloads: ’s nachts/weekenden, periodieke jobs, of event-driven pipelines met lage duty cycle. Cloud providers documenteren dit ook expliciet in KEDA-context: workloads naar nul pods schalen bespaart resources tijdens inactiviteit en is bedoeld voor intermittent workloads.
Maar scale-to-zero introduceert latency en node churn Als je van 0→1 gaat, is er altijd opstarttijd (image pull, init, warmup, connection establishment). En als je node autoscaling hebt, kan “0 pods” leiden tot “0 nodes” (afhankelijk van pool settings), waardoor je cold start nog groter wordt. Kubernetes’ node autoscaling beschrijft expliciet dat node autoscaling typisch reageert op unschedulable pods en nodes consolideert zodra ze niet meer nodig zijn. Dat betekent dat agressieve scale-to-zero in combinatie met agressieve node scale-down kan resulteren in veel churn.
Een belangrijk ontwerppunt is dus: kosten vs latency is geen “parameter tuning” vraag maar een product/SLO vraag. Als “first event latency” belangrijk is, laat je vaak bewust een minimale warm capacity staan (minReplicaCount > 0 of cron window) en accepteer je minder kostenwinst.
Ontwerpprincipes en best practices
KEDA-configuratie is uiteindelijk control theory + distributed systems. Een aantal principes komt steeds terug.
Kies schaal-signalen die “work” representeren, niet symptomen Queue backlog, lag en inflight work zijn meestal beter dan CPU als het doel is “werk op tijd wegwerken”. Maar kies niet blind: als downstream de bottleneck is, schaal je liever op downstream saturation (bijv. DB connection pool utilization) of je introduceert backpressure en stelt max concurrency in.
Behandel retries/duplicates als normaal gedrag Event-driven systemen leveren vaak “at-least-once” gedrag op (retries, redelivery). Dan is idempotency geen nice-to-have maar een correctness eis. Het Idempotent Consumer pattern beschrijft precies dit: de uitkomst van het herhaald verwerken van hetzelfde bericht moet hetzelfde zijn als één keer verwerken.
Waarom dit direct met autoscaling te maken heeft: scale-out vergroot concurrency; concurrency vergroot de kans dat races, retries en duplicates manifest worden. Zonder idempotency wordt autoscaling een incident-generator.
Ontwerp backpressure en bounded queues expliciet Als je producer sneller is dan je consumers, wil je dat het systeem zich reguleert zonder meltdown. Reactive Streams definieert backpressure precies als mechanisme om te voorkomen dat receivers arbitrair veel moeten bufferen—een concept dat je op message processing kunt projecteren: bounded queues, consumer pull rate, of server-side throttling.
Tuning: start conservatief, meet, en pas aan in regimes KEDA heeft activatie- en scalingregimes. Tuning moet je daarom ook in twee regimes doen:
- optimaliseer 0→1 voor “acceptable first-work latency” (pollingInterval, activationThreshold)
- optimaliseer 1→N voor “stabiliteit en throughput” (HPA behavior, targets, stabilization windows)
Plan voor metric failures: fail-safe defaults Maak bewust keuzes over wat er gebeurt als metrics niet beschikbaar zijn. Kubernetes HPA’s gedrag bij metric errors (scale-down skip bij errors) helpt, maar je moet nog eerst je metrics adapter en external metrics API availability bewaken. KEDA expose’t metrics om de health van operator/metrics adapter/webhooks te monitoren.
Let op cluster-wide impact van external.metrics.k8s.io
Omdat je maar één active provider voor external metrics kunt hebben, moet je als platform team governance hebben: wie beheert die APIService, welke component levert hem, en hoe voorkom je conflicts. KEDA benoemt dit expliciet in cluster-operate docs.
Plaats van KEDA binnen modern platform engineering
KEDA past het best in platformen waar:
- event-driven architecturen “first-class” zijn (queues, streams, workers)
- teams behoefte hebben aan een standaard interface om autoscaling op domeinmetrics te doen
- je platform team bereid is om een autoscaling building block te runnen en te opereren (observability, upgrades, incident response)
KEDA is daarmee geen “silver bullet”, maar een control-plane bouwblok. De grootste winst zit niet in “magisch betere scaling”, maar in het feit dat je scaling intentie (triggers, activatie, thresholds) als Kubernetes-native declarative config kunt beheren, en dat je een consistent pad krijgt om event sources naar HPA metrics te projecteren.
Samenvattende conclusies
KEDA is architectonisch logisch wanneer je signaal voor “werkdruk” buiten je pods ligt (queue depth, lag, backlog, pending jobs) en wanneer je scale-to-zero of “activatie op events” écht waardevol is. Het is vooral sterk voor queue-based workers, event streams, bursty background processing en scenarios waar je expliciet 0↔1 en 1↔N verschillend wilt tunen.
KEDA is minder geschikt wanneer je workload stateful is met dure rebalancing, wanneer je latency-kritisch bent en cold starts niet kunt maskeren, of wanneer je geen goed gedefinieerde scaling signalen hebt (of je throughput wordt bepaald door downstream bottlenecks). In die situaties is “eenvoudiger is beter” vaak waar: HPA op goede application metrics, of zelfs vaste capaciteit met expliciete backpressure kan betrouwbaarder zijn.
Wat KEDA wél oplost: het operationaliseert event-driven autoscaling als een Kubernetes-native control-plane uitbreiding, met CRDs, scalers en een metrics adapter die HPA kan gebruiken, plus expliciete activatie voor scale-to-zero. Wat KEDA níét oplost: fundamentele schaalbaarheid van je workload, correctness onder retries/duplicates, en de SLO-trade-off tussen kosten en latency. Die blijven ontwerp- en productkeuzes, geen YAML-parameters.