KEDA: autoscaling event-driven per Kubernetes

KEDA: autoscaling event-driven per Kubernetes


Introduzione: perché KEDA è rilevante oggi

Negli ultimi anni Kubernetes ha standardizzato l’autoscaling tramite Horizontal Pod Autoscaler (HPA), basato principalmente su CPU e memoria. Questo approccio funziona bene per workload sincroni e CPU-bound, ma mostra limiti evidenti nelle architetture event-driven: consumer di code, stream processor, job asincroni, integrazioni con sistemi esterni.

In questi scenari il vero segnale di carico non è l’utilizzo di CPU, ma il backlog di lavoro: messaggi in coda, lag Kafka, eventi in attesa, metriche applicative. È qui che entra in gioco KEDA: un componente Kubernetes-native che abilita autoscaling guidato da eventi reali, incluso lo scaling to zero, impossibile con HPA puro.

Questo articolo è pensato per DevOps, Platform Engineer e SRE che vogliono capire a fondo KEDA e implementarlo correttamente in produzione, evitando errori comuni e comprendendo i trade-off architetturali.

Cos’è KEDA e cosa non è

KEDA (Kubernetes Event-Driven Autoscaling) è un operatore Kubernetes che estende HPA fornendo metriche esterne derivate da sistemi di eventi.

È importante chiarire subito alcuni punti:

  • KEDA non sostituisce HPA → lo estende.
  • KEDA non esegue workload → decide quando e quanto scalare.
  • KEDA non è un event source → osserva event source esistenti.

Il valore chiave di KEDA è la capacità di:

  • scalare da 0 a N pod,
  • basarsi su event source reali (RabbitMQ, Kafka, Prometheus, cron, ecc.),
  • integrarsi in modo nativo con l’API Kubernetes.

KEDA e workload GenAI: un’anticipazione sul perché è rilevante per i LLM su Kubernetes

Il valore di KEDA emerge in modo ancora più evidente quando si osservano i workload di nuova generazione, in particolare quelli legati alla Generative AI e ai Large Language Model (LLM).

Come evidenziato nella letteratura recente sul deployment di sistemi GenAI su Kubernetes, questi carichi presentano pattern fortemente irregolari: lunghi periodi di inattività possono essere seguiti da burst improvvisi di richieste di inferenza ad alto costo computazionale.
In questi scenari, metriche tradizionali come CPU o memoria non rappresentano il lavoro reale in modo tempestivo né affidabile.

Per i servizi LLM, il vero segnale di pressione non è l’utilizzo delle risorse a valle, ma la domanda in ingresso: richieste di inferenza, code di job asincroni, pipeline che orchestrano retrieval, embedding e generazione.
Scalare solo quando CPU o GPU sono già sature significa reagire troppo tardi, con impatti diretti su latenza e costi.

KEDA abilita un approccio diverso: permette di scalare i componenti di un’architettura GenAI in base agli eventi che generano lavoro, non in base al consumo delle risorse che lo eseguono. Questo rende possibile:

  • anticipare i picchi di carico,
  • ridurre l’over-provisioning di risorse costose (come le GPU),
  • scalare a zero i servizi di inferenza quando non sono utilizzati.

In questo senso, KEDA non è solo un autoscaler per microservizi event-driven, ma un abilitatore architetturale per l’esecuzione efficiente di LLM su Kubernetes, soprattutto quando combinato con HPA e autoscaling del cluster.

Questo stesso principio — scalare sulla domanda reale anziché sull’utilizzo delle risorse — è alla base dell’architettura di KEDA e del modo in cui si integra con HPA.

Architettura di KEDA (come funziona davvero)

https://keda.sh/docs/2.18/concepts/

L’architettura di KEDA è intenzionalmente semplice e allineata ai pattern Kubernetes.

Componenti principali

  1. Scaler
    • Ogni scaler è responsabile di una sorgente di eventi specifica.
    • Esempi: RabbitMQ, Kafka, Azure Service Bus, Prometheus, Cron.
    • Recupera una metrica semantica (es. lunghezza coda, lag).
  2. Controller
    • Osserva le Custom Resource (ScaledObject, ScaledJob).
    • Decide quando attivare/disattivare lo scaling.
  3. Metrics Adapter
    • Espone le metriche come External Metrics API.
    • Permette all’HPA di consumarle come se fossero native.
  4. Admission Webhook
    • Valida le configurazioni (CRD).
    • Previene errori strutturali a runtime.

Integrazione con HPA

Il flusso reale è il seguente:

  1. Definisci uno ScaledObject.
  2. KEDA crea automaticamente un HPA.
  3. KEDA gestisce:
    • scaling 0 → 1
    • scaling 1 → 0
  4. HPA gestisce:
    • scaling 1 → N

Questo design evita di reinventare l’autoscaling e mantiene compatibilità totale con l’ecosistema Kubernetes.

ScaledObject e ScaledJob: scegliere lo strumento giusto

ScaledObject

Usalo quando:

  • il workload è long-running,
  • scala un Deployment o StatefulSet,
  • vuoi consumatori sempre pronti quando arrivano eventi.

Caratteristiche:

  • supporta scaling to zero,
  • replica count dinamico,
  • perfetto per microservizi e consumer.

ScaledJob

Usalo quando:

  • ogni evento è un’unità di lavoro indipendente,
  • vuoi Job Kubernetes reali,
  • il pod deve terminare dopo l’elaborazione.

Caratteristiche:

  • 1 evento → 1 Job,
  • ideale per batch, ETL, task asincroni,
  • pulizia automatica dei job completati.

Trade-off chiave

ScaledJob offre isolamento perfetto ma aumenta l’overhead di scheduling. ScaledObject è più efficiente per flussi continui.

Installazione di KEDA

Metodo consigliato: Helm

helm repo add kedacore https://kedacore.github.io/charts

helm repo update

kubectl create namespace keda

helm install keda kedacore/keda --namespace keda

Perché Helm in produzione

  • versioning controllato,
  • upgrade e rollback semplici,
  • gestione pulita delle CRD.

Alternativa: manifest YAML

Utile per test rapidi o ambienti estremamente controllati:

kubectl apply -f https://github.com/kedacore/keda/releases/download/v2.18.2/keda-2.18.2.yaml

Anatomy di uno ScaledObject (capirlo riga per riga)

Parametri fondamentali

CAMPO PERCHÉ È IMPORTANTE
scaleTargetRef Collega KEDA al workload
pollingInterval Frequenza di lettura eventi
cooldownPeriod Stabilità dello scaling
minReplicaCount Consente lo scale a zero
maxReplicaCount Protezione da runaway scaling
idleReplicaCount Numero di repliche “idle” (se vuoi non scendere a zero)

Sezione advanced

Qui si governa il comportamento fine dello scaling.

  • restoreToOriginalReplicaCount
    • true: ritorna al valore del Deployment
    • false: usa i limiti dello ScaledObject
  • horizontalPodAutoscalerConfig.behavior
    • evita oscillazioni aggressive,
    • consente scale-up immediato e scale-down controllato.

In produzione è fortemente consigliato configurare behavior. 

Caso reale: autoscaling RabbitMQ consumer

Obiettivo

  • Consumer Java su Kubernetes
  • RabbitMQ come event source
  • Scaling automatico 0 → N → 0

Logica di scaling

KEDA calcola:

desiredReplicas = ceil(queueLength / targetValue)

Esempio:

  • 100 messaggi
  • target = 5
  • → 20 pod

Questo approccio:

  • massimizza parallelismo,
  • evita sovra-scaling,
  • mantiene prevedibilità operativa.

Perché il cooldown è essenziale

Senza cooldownPeriod:

  • continui scale-up/scale-down,
  • instabilità,
  • costi inutili.

Autenticazione in KEDA: un aspetto strutturale, non un dettaglio

Quando si introduce KEDA in un cluster Kubernetes, l’autoscaling smette di essere un problema puramente “interno” al cluster.
Gli scaler di KEDA, per definizione, devono parlare con sistemi esterni: broker di messaggi, database, API cloud, metriche esposte fuori dal cluster.

Questo cambia radicalmente il modello di sicurezza.

Con HPA classico:

  • le metriche sono locali (CPU, memoria),
  • non esistono credenziali applicative coinvolte.

Con KEDA:

  • ogni scaler deve autenticarsi verso un sistema esterno,
  • quelle credenziali diventano parte integrante del piano di controllo dello scaling.

Trattare l’autenticazione come un dettaglio di configurazione è uno degli errori più comuni — e più pericolosi — quando si porta KEDA in produzione.

Il principio chiave: separare scaling, workload e secrets

KEDA introduce volutamente un livello di indirezione tra:

  • il workload da scalare (Deployment / Job),
  • la logica di scaling (ScaledObject / ScaledJob),
  • le credenziali necessarie per leggere le metriche.

Questo principio è fondamentale per tre motivi:

  1. Sicurezza
    • Nessuna credenziale hardcoded negli YAML applicativi.
  2. Manutenibilità
    • Cambiare password o token non richiede modifiche ai workload.
  3. Osservabilità e audit
    • È chiaro chi accede a cosa e perché.

È qui che entrano in gioco Secret, TriggerAuthentication e authenticationRef.

Flusso reale dell’autenticazione in KEDA

A livello concettuale, il flusso è questo:

  1. KEDA legge lo ScaledObject
  2. Trova un authenticationRef
  3. Risolve il riferimento a un TriggerAuthentication
  4. Il TriggerAuthentication:
    • legge uno o più Secret Kubernetes (oppure un secret esterno)
  5. Le credenziali vengono usate solo dallo scaler
  6. Il Deployment da scalare non conosce nulla di questo processo

Questo è un punto cruciale: il pod scalato non ha bisogno di conoscere le credenziali usate per scalare.

Pattern consigliato: Secret + TriggerAuthentication + ScaledObject

Questo è il pattern di riferimento per ambienti reali.

1. Secret Kubernetes

Il Secret contiene solo dati sensibili, nulla di logico:

  • host
  • username
  • password
  • connection string
  • token
apiVersion: v1
kind: Secret
metadata:
  name: rabbitmq-secret
type: Opaque
data:
  host: <base64>
  username: <base64>
  password: <base64>

Il Secret vive nel namespace dell’applicazione, segue RBAC e può essere ruotato senza toccare KEDA.

2. TriggerAuthentication

Il TriggerAuthentication è l’oggetto che mappa i Secret verso i parametri attesi dallo scaler.

apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: rabbitmq-trigger-auth
spec:
  secretTargetRef:
  - parameter: host
    name: rabbitmq-secret
    key: host
  - parameter: username
    name: rabbitmq-secret
    key: username
  - parameter: password
    name: rabbitmq-secret
    key: password

Qui avviene la parte più importante:

  • parameter non è arbitrario,
  • deve corrispondere esattamente a ciò che lo scaler RabbitMQ si aspetta.

Questo rende l’autenticazione esplicita, tipizzata e verificabile.

3. ScaledObject con authenticationRef

Lo ScaledObject si limita a dire:

“Per questo trigger, usa quell’identità”.

triggers:
- type: rabbitmq
  metadata:
    queueName: queue001
    mode: QueueLength
    value: "5"
  authenticationRef:
    name: rabbitmq-trigger-auth

Il risultato è una separazione netta:

  • ScaledObject → logica di scaling
  • TriggerAuthentication → identità
  • Secret → dati sensibili

 

Perché evitare l’autenticazione inline (se non per demo)

KEDA permette anche configurazioni più rapide, come secretKeyRef diretto nel trigger.
È utile per test e PoC, ma presenta limiti chiari:

  • accoppia scaling e credenziali,
  • rende difficile la rotazione,
  • peggiora la leggibilità del flusso.

In ambienti condivisi o regolamentati, questo approccio non scala a livello organizzativo.

 

ClusterTriggerAuthentication: quando lo scaling è multi-namespace

In contesti enterprise è comune avere:

  • più namespace,
  • più team,
  • un unico broker o servizio esterno condiviso.

Il TriggerAuthentication standard è namespace-scoped.
Per evitare duplicazioni, KEDA introduce ClusterTriggerAuthentication.

Differenza chiave:

OGGETTO SCOPE
TriggerAuthentication Namespace
ClusterTriggerAuthentication Cluster-wide

Il vantaggio:

  • un’unica definizione di identità,
  • riutilizzabile da più ScaledObject.

Il Secret, invece, rimane nel namespace dell’applicazione, mantenendo l’isolamento dei dati sensibili.

 

Secret esterni: HashiCorp Vault e oltre

Per ambienti ad alta compliance (PCI, SOC2, regulated workload), KEDA può leggere segreti direttamente da sistemi esterni, come HashiCorp Vault.

In questo scenario:

  • KEDA non legge Secret Kubernetes,
  • interroga Vault usando un metodo di autenticazione (token, Kubernetes auth, ecc.),
  • risolve dinamicamente i parametri dello scaler.

Il trade-off è chiaro:

  • più sicurezza
  • più complessità operativa

È una scelta architetturale, non una necessità universale.

 

Scaler disponibili e quando usarli

Uno dei punti di forza di KEDA è l’astrazione del concetto di evento.
Non è rilevante da dove arrivi il segnale di carico, ma quanto quel segnale rappresenti in modo affidabile il lavoro reale da svolgere.

Per questo motivo KEDA supporta un numero elevato di scaler: ciascuno è pensato per tradurre una specifica tipologia di evento o metrica in una decisione di scaling coerente.

Queue-based scaler

(RabbitMQ, AWS SQS, Azure Queue)
Sono ideali quando il backlog di messaggi rappresenta direttamente il carico del sistema.
Il numero di messaggi in coda è una misura chiara e immediata del lavoro da elaborare, rendendo questo tipo di scaler perfetto per consumer asincroni e pipeline di integrazione.

Stream-based scaler

(Kafka, EventHub, Pub/Sub)
Pensati per sistemi ad alta velocità e throughput elevato, dove il consumer lag è il vero indicatore di pressione.
In questi scenari CPU e memoria sono metriche secondarie: è il ritardo accumulato nello stream a determinare la necessità di scalare.

Metric-based scaler

(Prometheus, Datadog)
Utili quando il carico è espresso da una metrica applicativa aggregata, come il numero di richieste, il tempo di risposta o un contatore custom.
Questo approccio è particolarmente efficace quando non esiste una coda esplicita, ma il lavoro può essere rappresentato da una metrica osservabile.

Time-based scaler

(Cron)
Perfetti per carichi prevedibili o finestre operative controllate.
Consentono di scalare in base al tempo, ad esempio per aumentare la capacità in orari di punta noti o ridurla durante periodi di inattività programmata.

Database scaler

(Redis, PostgreSQL, MySQL)
Indicati quando lo stato applicativo risiede in strutture dati o tabelle.
Il numero di record in uno stato specifico (ad esempio “pending” o “queued”) diventa il segnale che guida lo scaling, mantenendo il sistema allineato al lavoro effettivo.

In KEDA non si scala sulla CPU, ma sulla verità operativa del sistema: se un segnale è misurabile e rappresenta lavoro reale, può guidare lo scaling.

Esempio di autoscaling basato su metriche applicative: Prometheus espone metriche esterne consumate da KEDA e dall’HPA fonte https://medium.com/building-inventa/scaling-pods-based-on-prometheus-metrics-using-keda-64686565ed11

Errori comuni da evitare

1. Usare KEDA come sostituto completo dell’HPA

KEDA non è un replacement dell’HPA, ma un event source che ne estende le capacità.
In molti scenari, soprattutto quelli CPU-bound o memory-bound, l’HPA resta la soluzione più stabile e prevedibile.

Best practice: usare KEDA per tradurre eventi esterni (queue, stream, topic) in metriche Kubernetes e lasciare all’HPA la responsabilità finale dello scaling. Questo approccio ibrido riduce comportamenti erratici e migliora la stabilità.

2. Impostare un polling interval troppo aggressivo

Un pollingInterval troppo basso può:

  • aumentare il carico su API esterne (es. Kafka, Azure Service Bus, Prometheus),
  • introdurre latenza e instabilità nello scaling,
  • generare costi inutili o throttling.
Best practice: partire da valori conservativi (30–60s) e ridurli solo dopo aver osservato metriche reali di latenza e throughput. Lo scaling event-driven non deve essere iper-reattivo, ma sufficientemente tempestivo.

3. Non definire un maxReplicaCount

Lasciare lo scaling senza un limite superiore è uno degli errori più pericolosi in produzione:

  • può causare over-scaling incontrollato,
  • saturare nodi e cluster,
  • generare costi imprevisti.
Best practice: impostare sempre maxReplicaCount in base (alla capacità del cluster, ai limiti dell'event source, al carico massimo sostenibile dall'appliazione).

4. Hardcodare secret e credenziali negli YAML

Inserire credenziali direttamente nei manifest Kubernetes:

  • viola le best practice di sicurezza,
  • espone segreti nei repository,
  • rende difficile la rotazione delle chiavi.
Best practice: usare TriggerAuthentication con Kubernetes Secrets oppure provider esterni (Vault, Azure Key Vault, AWS Secrets Manager)

5. Ignorare il comportamento dello scale-down

Molti team si concentrano solo sullo scale-up, ma lo scale-down è spesso la parte più delicata:

  • riduzioni troppo aggressive possono causare cold start frequenti,
  • applicazioni stateful o con cleanup asincrono possono perdere messaggi o task in corso.
Best practice: configurare correttamente cooldownPeriod, testare il comportamento con carichi intermittenti, verificare che l’applicazione gestisca shutdown e retry in modo idempotente.

Conclusione: quando KEDA fa davvero la differenza

KEDA è uno strumento potente ma mirato. Non va usato ovunque, ma dove il carico è guidato dagli eventi offre vantaggi enormi:

Takeaway pratici

  • Usa KEDA quando CPU ≠ lavoro
  • Combinalo sempre con HPA (non escluderlo)
  • Progetta lo scale-down prima dello scale-up
  • Tratta i secret come cittadini di prima classe
  • Inizia semplice, raffina con advanced

Se Kubernetes è il sistema operativo del cloud, KEDA è ciò che lo rende reattivo al mondo reale.