1) Il rilascio che sembrava perfetto
Un rilascio sembrava perfetto sulla carta. I pod erano sani, i controlli di readiness passavano, nessun ciclo di crash. Poi il ritardo delle code iniziò a salire, la latenza p95 peggiorò e, quando capimmo cosa stava accadendo, una finestra analitica a valle era già stata mancata.
Fu allora che smisi di fidarmi del solo stato binario del deployment e iniziai a considerare il comportamento in esecuzione come la vera definizione di successo.
2) Il problema che stavamo davvero risolvendo
Eseguivamo servizi backend, worker per l’analisi distribuita e pipeline di dati su un’infrastruttura condivisa. Il cluster doveva sostenere contemporaneamente job ad alta capacità di elaborazione e servizi sensibili alla latenza.
- Obiettivo: finestre di aggiornamento prevedibili per i risultati analitici.
- Vincolo: interferenze tra carichi diversi, namespace e nodi.
- Requisito: ripristino rapido senza effetti a cascata.
3) Architettura iniziale
Avevamo una base solida: orchestrazione Kubernetes, servizi containerizzati, pipeline CI/CD e strumenti di monitoraggio. Ma le politiche di scheduling erano troppo generiche per la varietà dei nostri carichi.
Figura 1 — Modello iniziale dei carichi del cluster
I pool condivisi sembravano efficienti finché i picchi di utilizzo non misero in luce le interferenze tra carichi vicini.
4) Perché le soluzioni tradizionali fallirono
Provammo prima le soluzioni più ovvie: più nodi, nodi più grandi e autoscaling aggressivo. Aiutarono temporaneamente, poi introdussero nuove instabilità, come continui riposizionamenti e correzioni eccessive delle risorse.
- Più nodi non risolvevano i conflitti tra le politiche.
- Il sovradimensionamento riduceva gli avvisi, ma aumentava i costi e nascondeva la causa del problema.
- Uno scaling basato solo sulla CPU ignorava il comportamento delle code e della latenza.
5) Allineare esecuzione distribuita e piattaforma
Il livello di esecuzione distribuita, con percorsi analitici basati su Dask, richiedeva un allineamento esplicito con la piattaforma. Quando le politiche di orchestrazione ed esecuzione non concordano, il sistema sembra impegnato ma produce risultati modesti.
Passammo a un posizionamento consapevole dei carichi e alla profilazione delle risorse, riducendo significativamente le interferenze tra classi diverse.
6) Le lezioni più importanti su Kubernetes
- Requests e limits sono scelte architetturali. Valori scelti a intuito causavano costose anomalie nello scheduling.
- L’isolamento dei carichi è indispensabile. Pool di nodi, taint e toleration erano strumenti di sicurezza operativa.
- I criteri di accettazione dei rilasci devono includere le prestazioni, oltre allo stato del deployment.
- Il continuo ricambio dei pod è un indicatore di rischio quando è correlato al ritardo delle code e al peggioramento della latenza.
Figura 2 — Strategia di isolamento dei carichi
Separare le classi di carico ridusse le interferenze e rese più semplice localizzare gli incidenti.
7) Le sfide dei dati nella gestione di Kubernetes
I costi di trasferimento dei dati erano strettamente legati allo scheduling. Riposizionare job pesanti tra nodi senza considerare la località dei dati aumentava i trasferimenti e peggiorava i tempi di completamento.
Iniziammo a monitorare esplicitamente le fasi con molti trasferimenti e a usare questi segnali per pianificare capacità e politiche di posizionamento.
Figura 3 — Esempio di andamento operativo
Nei nostri incidenti, ritardo delle code e ricambio dei pod aumentavano insieme prima che diventassero visibili i problemi di aggiornamento dei dati.
8) Lezioni di osservabilità nella gestione della piattaforma
Passammo da dashboard incentrate sull’infrastruttura a dashboard incentrate sui risultati. Le metriche del cluster venivano sempre interpretate in relazione alla qualità e alla tempestività degli output.
- tempo di attesa dei pod collegato al rischio per il servizio,
- eviction collegate all’affidabilità della consegna,
- eventi di rilascio collegati alla latenza e al comportamento delle code.
9) Lezioni sull’affidabilità
L’affidabilità migliorò quando rendemmo espliciti rollback e contenimento dei guasti. Introducemmo tentativi limitati, strategie di failover specifiche per classe e responsabilità più chiare per gli incidenti che coinvolgevano più team.
Figura 4 — Ciclo di affidabilità e rollback
I criteri di successo dei rilasci passarono dalla readiness dei pod alla stabilità dei risultati del servizio.
10) Errori che oggi eviterei
- Presumere che lo scheduling predefinito sia stabile con classi di carico eterogenee.
- Considerare gli aggiornamenti del cluster solo eventi infrastrutturali, ignorando il rischio per i dati.
- Introdurre criteri di rilascio basati sulle prestazioni soltanto dopo incidenti dolorosi.
11) Consigli per chi sviluppa questi sistemi
Se la tua piattaforma cresce rapidamente, definisci presto classi di carico e confini delle politiche. Molti problemi nel codice si risolvono rapidamente; le ambiguità nelle politiche di un grande cluster sono molto più difficili da sciogliere sotto pressione.
- Profila il comportamento dei carichi prima di impostare requests e limits.
- Separa i percorsi critici dai carichi batch pesanti.
- Usa i segnali delle code e della latenza per decidere l’autoscaling.
- Costruisci i criteri di rilascio sui risultati dei servizi, oltre alle metriche dell’infrastruttura.
12) Conclusioni
Gestire Kubernetes su oltre 300 nodi mi ha insegnato che la stabilità della piattaforma dipende soprattutto dalla progettazione consapevole delle politiche. Buoni strumenti aiutano, ma risultati prevedibili nascono da obiettivi chiari per i carichi, segnali misurabili e cicli operativi rigorosi.
Se domani il tuo cluster raddoppiasse, le tue politiche di scheduling e rilascio reggerebbero ancora alla pressione di carichi diversi?