Architettura di dati e sistemi, dalle fondamenta Lezione 67 / 80

Ottimizzazione del costo del compute: Spot, autoscaling, right-sizing

Tre leve muovono la maggior parte della bolletta di compute: Spot instance per i workload interrompibili, autoscaling che risponde al carico senza thrashing e right-sizing delle VM, che sono per lo più sovradimensionate. La reserved capacity copre la baseline prevedibile.

La lezione 65 ha inquadrato il costo come un iceberg, con il compute come la parte visibile che tutti guardano. La lezione 66 è andata in profondità sullo storage. Questa lezione copre il lato compute: le tre leve operative che, per la maggior parte dei team, sono responsabili della maggior parte dei risparmi sul compute. Spot e preemptible instance per i workload che tollerano l’interruzione. Autoscaling che risponde al carico reale senza thrashing e senza mancare i picchi. Right-sizing delle VM che sono state over-provisioned dall’ultimo panico di capacity tre anni fa.

La lezione tocca anche la reserved capacity (Reserved Instance e Savings Plan su AWS, Committed Use Discount su GCP, Reservation su Azure), che è una leva contrattuale piuttosto che architetturale ma si compone con il lavoro architetturale. E si chiude sui pattern di autoscaling specifici per Kubernetes (Cluster Autoscaler, Karpenter) che la maggior parte dei team moderni incontrerà prima o poi.

Leva uno: Spot e preemptible instance

I cloud provider operano a scala, e in qualunque momento hanno capacità di riserva che non è stata allocata ai customer on-demand. Vendono quella capacità di riserva con uno sconto consistente, con la fregatura che il provider può riprendersela con preavviso breve quando la domanda on-demand sale.

AWS le chiama Spot Instance, con risparmi dal 60 al 90 per cento sulle tariffe on-demand a seconda di tipo di istanza e region. Avviso di reclaim di due minuti. GCP ha le Spot VM (un tempo Preemptible VM) con sconti dal 60 al 91 per cento, con un avviso di 30 secondi. Azure ha le Spot Virtual Machine con pricing simile e un avviso di 30 secondi. L’economia tra i tre è abbastanza vicina che la scelta è di solito guidata da quello su cui gira il resto del workload, piuttosto che dal pricing Spot in isolamento.

I risparmi annunciati (dal 60 al 90 per cento) sono reali, sostenuti e sostanziali. Un team che fa girare un workload ETL batch che entra su Spot paga una frazione della bolletta di un team che fa girare lo stesso workload su on-demand. La domanda di inquadramento non è “dovremmo usare Spot” ma “quali dei nostri workload tollerano l’interruzione”.

Workload che si adattano bene a Spot.

  • Job batch idempotenti e retryable. Un ETL notturno che può essere rieseguito dopo un’interruzione è un fit da manuale.
  • Worker Spark (executor). Il driver di solito gira su on-demand o su un’istanza stabile, ma gli executor possono essere Spot. Il meccanismo di task-retry di Spark gestisce con grazia la perdita di executor.
  • Training ML distribuito con checkpointing. Un job di training che fa checkpoint ogni pochi minuti perde al massimo qualche minuto di progressi quando viene reclaimed.
  • Web worker stateless dietro un load balancer con un pool di istanze in salute. Perdi il 20 per cento di capacity per un attimo, l’autoscaler tira su i rimpiazzi.
  • Runner CI/CD. Una build che viene interrotta fa retry su un altro runner.

Workload che non si adattano a Spot.

  • Servizi single-instance stateful. La perdita dell’istanza significa perdita dello stato.
  • Nodi leader nei sistemi distribuiti (controller Kafka, driver Spark, ZooKeeper, etcd). Una perdita di leader-election è recuperabile ma costosa, e il reclaim Spot è troppo frequente per assorbirla in modo pulito.
  • Job interattivi a lunga durata senza checkpointing. Il reclaim a metà strada significa ricominciare da capo.
  • Servizi sensibili alla latenza con SLO stringenti sulla tail-latency. L’evento di reclaim stesso causa uno spike di latenza durante il failover.

Il pattern maturo è una flotta mista: una baseline on-demand o Reserved per i workload che hanno bisogno di stabilità, con Spot per il resto. Gli Auto Scaling Group su AWS supportano direttamente questo tramite le “mixed instances policies”, che permettono al team di specificare una capacity di base on-demand con il resto riempito dai pool Spot. Karpenter (trattato sotto) va oltre e lascia al cluster autoscaler la scelta di Spot o on-demand su base per-pod, basata sulle tolerance dichiarate.

Leva due: Autoscaling

La maggior parte dei workload non ha carico costante. Il traffico web fa picco nel pomeriggio e cala di notte. I job batch girano a ondate. Le query analitiche si raggruppano attorno agli orari di lavoro. Una flotta statica dimensionata per il picco di carico è in idle la maggior parte del tempo, pagando per capacity che non usa.

L’autoscaling regola la dimensione della flotta in base al carico osservato. Fatto bene, il team paga per quello che usa e niente di più. Fatto male, l’autoscaling produce una bolletta peggiore di una flotta statica, perché fa thrashing tra operazioni di scale-up e scale-down, non riesce a rispondere abbastanza in fretta agli spike di traffico (causando request perse e violazioni di SLO), o scala in anticipo su segnali rumorosi.

La meccanica è semplice in concetto. Definisci una metrica che l’autoscaler osserva (CPU utilisation, request rate, queue depth). Definisci le soglie alle quali aggiunge o rimuove capacity. Il controller di autoscaling controlla la metrica a intervalli regolari e regola la dimensione della flotta.

Le insidie sono più sottili di quanto la meccanica lasci intendere.

Oscillazione. Scale up al 70 per cento di CPU, scale down al 30 per cento. Aggiungi un’istanza al 70, la media scende al 40, il controller scala giù, la media risale al 70, il controller scala su di nuovo. La flotta fa ping-pong tra le dimensioni, pagando ripetutamente per overhead di start-up e shutdown delle istanze. La soluzione è un gap di isteresi (scale up al 70, scale down al 40) e un periodo di cooldown (dopo qualunque azione di scaling, aspetta 10 minuti prima di considerarne un’altra).

Scale-up lento. Arriva uno spike di traffico, l’autoscaler decide di aggiungere capacity, AWS provisiona un’istanza in un paio di minuti, l’istanza fa boot e gira il suo init script per un altro minuto, il load balancer la marca healthy dopo qualche altro secondo, e solo allora inizia a servire traffico. Tempo totale dallo spike alla capacity, circa cinque minuti. Se lo spike è più corto di cinque minuti, l’autoscaler sta chiudendo la stalla dopo la fuga del cavallo. La soluzione è headroom in eccesso (target al 60 per cento invece dell’80 per cento), warm pool (istanze pre-inizializzate pronte a servire) e predictive scaling che guarda i pattern storici e scala in anticipo.

Scale-down prima del calo del carico. Il fallimento opposto: il carico sta calando, l’autoscaler scala giù basandosi sul segnale immediato, poi il carico ritorna e il team si trova a corto di capacity. La soluzione è scaling asimmetrico: scala su in modo aggressivo, scala giù lentamente. Una configurazione tipica scala su entro 60 secondi dal superamento della soglia superiore e aspetta 10-15 minuti di sostenuta utilisation bassa prima di scalare giù.

Latenza di cold-start. Anche quando la capacity è disponibile, un container o una VM appena avviati sono lenti sulle loro prime richieste perché il JIT compiler, le cache e i connection pool non sono caldi. Il team che ignora il cold-start vede la tail latency schizzare ogni volta che l’autoscaler aggiunge capacity. La soluzione è a livello applicativo (warm-up endpoint, connection pool pre-warmed) più a livello di piattaforma (ramping graduale del traffico per le nuove istanze).

Thrashing su metriche rumorose. La CPU utilisation è rumorosa su istanze piccole e workload bursty. Le decisioni di scaling fatte sulla media a 10 secondi producono più thrashing di quelle fatte sulla media a 5 minuti. La soluzione è lo smoothing della metrica (usa medie a 5 minuti, pesa leggermente di più le osservazioni recenti) e considera lo scaling su segnali più stabili (queue depth, request rate) per i workload dove la CPU è troppo rumorosa.

La forma di una configurazione di autoscaling ben tarata: target di utilisation attorno al 60-70 per cento, regole di scaling asimmetriche, isteresi tra le soglie up e down, un periodo di cooldown e predictive scaling sovrapposto in cima per i workload con forti pattern giornalieri. Fatto bene, l’autoscaling può tagliare la bolletta della flotta del 40-60 per cento rispetto a un provisioning statico dimensionato sul picco.

Leva tre: Right-sizing

La maggior parte delle VM è sovradimensionata. Il tipo di istanza è stato scelto durante il deployment iniziale basandosi su congetture sul carico, padded per sicurezza, e mai più rivisto. Adesso l’applicazione usa il 15 per cento della CPU e il 30 per cento della memoria in media, e il team paga per il 100 per cento dell’istanza.

L’esercizio è semplice e dovrebbe essere una disciplina trimestrale.

flowchart LR
    Measure["Measure utilisation<br/>CPU, memory, disk, network<br/>over a representative week"] --> Recommend["Pick instance type<br/>with 30-40 percent headroom<br/>over observed peak"]
    Recommend --> Test["Test in staging<br/>under production-like load"]
    Test --> Deploy["Roll out gradually<br/>monitor for regressions"]
    Deploy --> Repeat["Revisit quarterly"]
    Repeat --> Measure

La finestra di misurazione deve catturare il ciclo naturale del workload. Un servizio web con pattern settimanali ha bisogno di una settimana di dati. Un job batch con cicli mensili ha bisogno di un mese. Il picco osservato durante la finestra, più un margine di sicurezza del 30-40 per cento, definisce il tipo di istanza più piccolo che ci sta.

Il 30-40 per cento di headroom non è arbitrario. Tiene conto dell’incertezza di misurazione, della crescita futura e degli spike imprevedibili che la finestra di misurazione non ha catturato. Un team che fa right-sizing al 5 per cento di headroom passerà il trimestre successivo a spegnere incendi sulle istanze che toccano il soffitto in un giorno di traffico. Un team che fa right-sizing al 70 per cento di headroom paga per capacity che non userà.

Sia AWS che GCP forniscono recommender di right-sizing che automatizzano misurazione e raccomandazione. AWS Compute Optimizer (https://aws.amazon.com/compute-optimizer/, consultato 2026-05-01) analizza le metriche CloudWatch per EC2, EBS, Lambda, ECS e RDS, e produce raccomandazioni per tipi di istanza più piccoli, più grandi o di famiglia diversa. GCP Recommender (https://cloud.google.com/recommender, consultato 2026-05-01) fa l’equivalente per Compute Engine, con copertura simile sul resto della flotta GCP. Azure ha Azure Advisor con una categoria di cost-optimisation. Nessuno dei tre è un sostituto del giudizio dell’ingegnere, ma rimuovono la maggior parte del lavoro di raccolta dati e danno al team un punto di partenza per la discussione.

Il right-sizing è il raro intervento di costo senza costo architetturale. L’applicazione non cambia. L’istanza viene rimpiazzata con una più piccola della stessa famiglia, il workload gira con lo stesso codice, il comportamento visibile all’utente è identico, e la bolletta cala. La ragione per cui i team non fanno right-sizing più spesso non è che sia difficile; è che raramente c’è una forcing function. Una review trimestrale dei costi con il right-sizing come punto fisso in agenda è la pratica che lo rende routine.

Reserved capacity e committed use discount

Reserved Instance (AWS) e Savings Plan (AWS), Committed Use Discount (GCP) e Reservation (Azure) sono gli strumenti contrattuali che scambiano un impegno pluriennale per uno sconto del 30-60 per cento sulle tariffe on-demand. La meccanica differisce tra i provider; l’inquadramento è coerente.

L’argomento di base: un workload che girerà comunque in continuo per i prossimi uno-tre anni può essere pagato in anticipo con uno sconto consistente. Il rischio è che il workload cambi (venga ritirato, migri a un tipo di istanza diverso, si sposti in una region diversa) prima che l’impegno maturi, lasciando il team a pagare per capacity che non può usare.

La disciplina è misurare prima la baseline prevedibile. Il minimo a finestra mobile a 30 giorni dell’utilizzo di compute, in instance-hour, è un proxy ragionevole per “la capacity che il team usa in ogni caso”. Riserva fino a quella baseline. Lascia la porzione variabile sopra di essa su on-demand o Spot. L’errore classico è iper-riservare (bloccarsi sull’80 per cento dell’utilizzo corrente, poi scoprire che l’utilizzo corrente includeva un workload deprecato che viene ritirato, lasciando la reservation orfana) o sotto-riservare (bloccarsi al 20 per cento e pagare prezzo pieno sulla porzione prevedibile sopra).

I Savings Plan di AWS, introdotti nel 2019, hanno reso il calcolo più facile rispetto al vecchio modello Reserved Instance: un Savings Plan si impegna su una spesa in dollari-per-ora invece che su un tipo di istanza specifico, e lo sconto si applica in modo flessibile tra famiglie e dimensioni di istanze. Al 2026, i Savings Plan sono di solito il punto di partenza giusto per un team nuovo alla reserved capacity su AWS.

Il pattern combinato: Reserved o committed capacity per la baseline prevedibile, on-demand per i picchi prevedibili sopra la baseline, Spot per i workload interrompibili. Una tipica data platform matura ha tutti e tre i layer che girano insieme, con le proporzioni tarate sul mix di workload.

Autoscaling specifico per Kubernetes

Per i team che girano su Kubernetes, la storia dell’autoscaling ha due layer. L’autoscaling a livello pod (Horizontal Pod Autoscaler, Vertical Pod Autoscaler) decide quante repliche di pod far girare e quanta CPU e memoria ogni pod richiede. L’autoscaling a livello cluster decide quante VM sottostanti far girare per ospitare quei pod.

Il Cluster Autoscaler è stato l’autoscaler a livello cluster di default fin dai primi anni di Kubernetes. Osserva i pod non schedulabili e aggiunge nodi dai node pool configurati, e osserva i nodi sotto-utilizzati e li rimuove. Funziona bene, ma è vincolato dal modello dei node pool: ogni pool è un tipo di istanza fisso, e l’autoscaler può solo scegliere quanti di quel tipo far girare.

Karpenter, introdotto da AWS nel 2021 e ora un progetto CNCF, è l’alternativa moderna. Invece di node pool fissi, Karpenter guarda i pod pendenti, calcola il tipo di istanza best-fit per ogni batch e lo provisiona direttamente. Il risultato è un packing più denso, uno scale-up più veloce (Karpenter può lanciare nodi in circa un minuto rispetto a diversi minuti per il Cluster Autoscaler) e costi più bassi perché la scelta dell’istanza è workload-aware. Karpenter gestisce anche l’integrazione Spot in modo nativo, con tolerance dichiarate che permettono ai pod di optare per i pool Spot.

Per un team su EKS nel 2026, Karpenter è la scelta consigliata. Il Cluster Autoscaler funziona ancora ed è ancora appropriato per i team che lo stanno già facendo girare in modo stabile. I cluster greenfield dovrebbero default su Karpenter. La documentazione di Karpenter (https://karpenter.sh/, consultato 2026-05-01) copre il setup e i pattern.

Mettendo tutto insieme

Il quadro complessivo dei costi per un data team maturo che gira su AWS nel 2026:

  • Baseline prevedibile: Reserved Instance o Savings Plan per il 30-60 per cento di sconto sull’on-demand.
  • Picco variabile: on-demand, dimensionato attraverso autoscaling che punta al 60-70 per cento di utilisation.
  • Workload interrompibili: Spot, integrato attraverso mixed-instances policies o Karpenter.
  • Right-sizing: review trimestrale con le raccomandazioni di Compute Optimizer come punto di partenza.

Un team che fa girare bene tutte e quattro le leve cattura tipicamente il 50-70 per cento di risparmi rispetto a una bolletta di compute non ottimizzata. Un team che non ne fa girare nessuna sta lasciando sul tavolo la maggior parte dei risparmi disponibili. La maggior parte dei team sta da qualche parte nel mezzo, con una o due leve in gioco e le altre nel backlog.

Il filo che lega le prime tre lezioni del Modulo 9: il costo non è un progetto una tantum. È una disciplina. La lezione 65 ha inquadrato l’iceberg. La lezione 66 ha coperto le leve di storage. Questa lezione ha coperto il compute. Le prossime lezioni del modulo si spostano sull’ottimizzazione a livello di query, sui pattern di costo architetturali (multi-region contro single-region, costi del traffico tra microservizi, trade-off serverless contro provisioned) e sulle pratiche culturali che mantengono il costo come una preoccupazione condivisa accanto ad affidabilità e performance.

Citazioni e letture di approfondimento

  • AWS, EC2 Spot Instances, https://aws.amazon.com/ec2/spot/ (consultato 2026-05-01). Modello di pricing, meccanica delle interruzioni e pattern di integrazione con gli Auto Scaling Group.
  • GCP, Spot VMs, https://cloud.google.com/compute/docs/instances/spot (consultato 2026-05-01). Pricing e meccanica della preemption su GCP.
  • AWS Compute Optimizer, https://aws.amazon.com/compute-optimizer/ (consultato 2026-05-01). Raccomandazioni di right-sizing su EC2, EBS, Lambda, ECS e RDS.
  • GCP Recommender, https://cloud.google.com/recommender (consultato 2026-05-01). Il servizio di raccomandazione equivalente su GCP.
  • AWS, Savings Plans, https://aws.amazon.com/savingsplans/ (consultato 2026-05-01). Il meccanismo di sconto flessibile basato su impegno.
  • Karpenter, https://karpenter.sh/ (consultato 2026-05-01). Il moderno autoscaler di nodi Kubernetes, con documentazione su installazione, configurazione di NodePool e integrazione Spot.
  • Progetto Kubernetes Autoscaler, https://github.com/kubernetes/autoscaler (consultato 2026-05-01). Le implementazioni di riferimento di Cluster Autoscaler e Vertical Pod Autoscaler.
  • FinOps Foundation, working group “Rate optimisation”, https://www.finops.org/ (consultato 2026-05-01). Risorse della community su Spot, reserved capacity e right-sizing come parte della pratica FinOps più ampia.
Cerca