Raddoppiare le risorse non è una strategia a lungo termine

2026/03/03 · Marco Orlandin · 7 min · TL;DR

Il cliente chiama perché la fattura AWS è troppo alta. Il server costa il doppio di prima, e nessuno sa spiegare bene perché.

Rappresentazione visiva di un server sovradimensionato rispetto al problema reale

La storia è semplice, e l'ho sentita più volte di quanto vorrei: un'istanza EC2 singola, diversi (molti) container in esecuzione, Traefik come reverse proxy davanti a tutto, più siti web ciascuno con la propria struttura e il proprio database. Un setup consolidato, che girava da anni (almeno 4) senza problemi particolari.

Un giorno qualcosa va storto. Il servizio diventa irraggiungibile, i siti non rispondono, i container si bloccano a catena. La soluzione più veloce? Raddoppiare CPU e RAM dell'istanza.

Come risposta a un'emergenza ha senso. È la cosa giusta da fare quando il telefono squilla e il cliente ha i siti giù: rimetti in piedi il servizio, poi ti fermi, respiri e vai a fondo. Il problema è quello che succede dopo.

Settimane dopo, l'istanza raddoppiata era ancora lì. Nessuno aveva indagato la reale causa del picco. Nessuno aveva verificato se le risorse aggiuntive servissero davvero. L'unica cosa certa era che la fattura mensile era raddoppiata.

Cosa ho trovato

Prima ancora di guardare il problema tecnico, il quadro generale era già abbastanza chiaro.

L'istanza era on-demand. Attiva da almeno quattro anni. Nessun Savings Plan, nessuna Reserved Instance, nessun tipo di impegno. Costo pieno, ogni ora, ogni giorno, per anni. Solo questo dettaglio rappresentava uno spreco significativo: un Savings Plan su un'istanza che gira 24/7 da quattro anni avrebbe potuto ridurre il costo compute di un buon 30-40%, senza cambiare nulla dell'architettura.

Poi c'era il monitoring: non esisteva. Nessun allarme CloudWatch configurato, nessuna metrica sotto osservazione, nessun dashboard. Il team si accorgeva dei problemi quando i siti andavano giù. Non prima, non durante: dopo. Quando qualcuno chiamava o quando qualcuno provava ad aprire un sito e non caricava.

Senza visibilità, ogni problema diventa un'emergenza. E ogni emergenza si risolve con la stessa mossa: buttarci più risorse sopra.

Ma il punto vero era un altro: cosa aveva causato il picco che aveva messo in ginocchio il servizio?

Non era la CPU. Non era la RAM.

Erano gli IOPS.

Il disco sbagliato

Il server era strutturato bene, per certi versi. Ogni container aveva il suo mountpoint dedicato per i dati, i container database montavano i propri volumi separati sul filesystem. L'architettura applicativa non era il problema, anzi: chi aveva impostato il sistema ci aveva pensato.

Il problema era il tipo di disco su cui girava tutto: un volume EBS di tipo gp2.

I volumi gp2 funzionano con un sistema a crediti. Hai un livello base di IOPS proporzionale alla dimensione del disco (3 IOPS per GB) e un burst fino a 3.000 IOPS che puoi usare quando serve, ma che si esaurisce. Finiti i crediti, torni al livello base. Se il disco è piccolo, quel livello base può essere molto basso.

Con quella quantità di container attivi, ciascuno con le proprie operazioni di lettura e scrittura, più i database che per natura fanno un uso intensivo del disco, il volume gp2 era sotto pressione costante. I crediti di burst si esaurivano e le performance crollavano. Quando il disco non riesce a tenere il passo, tutto quello che ci gira sopra rallenta: i database si bloccano, i container vanno in timeout, i siti smettono di rispondere.

Raddoppiare CPU e RAM non serviva a niente: il collo di bottiglia non era lì. Era come mettere un motore più potente in una macchina con le ruote sgonfie.

La soluzione

Migrazione da gp2 a gp3. La famiglia EBS offre anche i volumi io1 e io2, progettati per carichi che richiedono IOPS provisionati fino a 64.000 (io2 Block Express arriva a 256.000). Sono la scelta giusta per database ad alte prestazioni, sistemi transazionali con requisiti di latenza sotto il millisecondo, workload che hanno bisogno di IOPS garantiti ben oltre i 3.000. In questo caso, però, sarebbero stati sovradimensionati. Il server non aveva bisogno di performance estreme: aveva bisogno di performance costanti. La differenza è importante. Il problema del gp2 non era il picco massimo raggiungibile (il burst arrivava comunque a 3.000 IOPS), ma il fatto che quel picco non fosse sostenibile. Serviva una baseline affidabile, non un tetto più alto. In più, il costo di un io1 o io2 sarebbe stato sensibilmente superiore, nell'ordine di 3-5 volte rispetto a un gp3 a parità di capacità, senza un reale beneficio per questo tipo di carico.

I volumi gp3 sono la generazione successiva dei general purpose: offrono 3.000 IOPS di base garantiti, senza il meccanismo a crediti, e permettono di configurare IOPS e throughput in modo indipendente dalla dimensione del disco. Per questo workload erano il punto di equilibrio perfetto: performance prevedibili, costo contenuto, nessuna sorpresa.

Il bello è che il costo base di un gp3 è addirittura inferiore a quello di un gp2 a parità di dimensione. Stai pagando meno per avere performance migliori e più prevedibili.

Con il nuovo volume in posizione, il server ha smesso di soffocare. Le operazioni su disco non erano più il collo di bottiglia, i database rispondevano nei tempi normali, i container giravano senza timeout.

Un'altra opzione che ho valutato era quella di separare i dati su più volumi EBS distinti: un volume dedicato per i database, uno per i dati applicativi, uno per i log. In contesti con carichi molto diversi tra loro (per esempio un database write-intensive accanto a un'applicazione read-intensive), distribuire su più volumi permette di isolare le performance e configurare IOPS e throughput su misura per ciascun carico. In questo caso, però, non era necessario. Il collo di bottiglia era il tipo di volume, non il layout. I mountpoint per container erano già separati a livello di filesystem, e con i 3.000 IOPS base del gp3 la contesa sul disco è sparita. Aggiungere complessità infrastrutturale senza un beneficio misurabile sarebbe stato un errore diverso, ma pur sempre un errore.

A quel punto è stato possibile fare la cosa che contava davvero: riportare l'istanza alla dimensione originale. CPU e RAM dimezzate, perché il problema non era mai stato lì.

Risultato: circa il 50% in meno sul costo compute. Non perché abbiamo ottimizzato qualcosa di complicato, ma perché abbiamo individuato la reale causa del problema.

Il pattern

Questo caso ha molto in comune con un altro intervento sui costi cloud di cui ho già scritto. Lì il problema era lo storage S3 con versioning non governato: si pagava per dati che l'applicazione considerava cancellati. Qui è un disco di tipo sbagliato su un server che gira da anni senza che nessuno ci abbia più guardato dentro.

Contesti diversi, stesso schema: un costo che cresce, nessuno che sa spiegare perché, e una reazione istintiva (spendere di più) che non risolve niente.

Il denominatore comune è lo stesso: il cloud non è caro. È non governato.

Quando manca visibilità su quello che succede, le decisioni diventano reattive. Si raddoppia, si scala, si spende di più. Non perché serva, ma perché non si sa cosa fare di diverso.

Ho raccomandato al cliente di attivare un Savings Plan per l'istanza, perché quattro anni di on-demand sono soldi buttati. E di mettere in piedi un minimo di monitoring. Non serve un sistema complesso: bastano le metriche base di CloudWatch con allarmi sulle soglie giuste. In concreto, per un setup come questo, le metriche che contano sono poche. VolumeReadOps e VolumeWriteOps per tenere d'occhio le operazioni su disco e accorgersi se il carico I/O cresce nel tempo. Se il volume fosse ancora gp2, BurstBalance sarebbe la metrica più critica: ti dice quanti crediti di burst restano, e un calo costante è il segnale che stai per avere problemi. CPUUtilization è inclusa di default ed è il punto di partenza ovvio. Per la memoria serve un passo in più: installare il CloudWatch Agent sull'istanza, che espone MemoryUtilization (non è una metrica nativa di EC2). Con cinque o sei allarmi configurati su queste metriche, il team avrebbe visto il problema del disco settimane prima del blocco totale. Niente di sofisticato: solo il minimo per trasformare un'emergenza in un ticket pianificabile.

Prima di spendere di più, vale la pena capire cosa si sta pagando. E perché.

Se hai una fattura cloud che non sai spiegare, o un server che "funziona" solo perché qualcuno un giorno ha raddoppiato tutto... parliamone. Ti dico se il problema è davvero dove pensi tu.

In sintesi (TL;DR)

  • Il server era stato raddoppiato (CPU e RAM) dopo un'emergenza, ma il collo di bottiglia non era lì: erano gli IOPS di un disco gp2 a crediti esauriti.
  • Migrazione da gp2 a gp3: IOPS base garantiti, costo inferiore, performance prevedibili. Istanza riportata alla dimensione originale.
  • Quattro anni di istanza on-demand senza Savings Plan: spreco significativo su un workload 24/7.
  • Senza monitoring, ogni problema diventa emergenza e ogni emergenza si risolve buttandoci più risorse sopra.