DevRel Engineer @ Akamas · CNCF TAG DevEx Co-Chair · CNCF Ambassador · Responsabile comunicazione per Kubernetes v1.35
01 — Kubernetes
La maggior parte degli sviluppatori ha sentito dire “date semplicemente più risorse ai vostri pod” come risposta standard ai tempi di avvio lenti. Qual è il costo reale di questo consiglio su larga scala — e perché ci è voluto così tanto tempo prima che Kubernetes offrisse qualcosa di meglio?
Aumentare la CPU o la memoria di un pod è la leva più facile che chiunque possa tirare, ed è esattamente questo il problema. Funziona sul momento, perché più risorse di solito tagliano qualche secondo dall’avvio a freddo, ma nessuno torna indietro in seguito per ridimensionarle una volta passata la pressione della scadenza. Moltiplicate questo per ogni servizio di una flotta grande e ottenete un cluster che gira a una frazione del suo reale utilizzo, perché le richieste sono state impostate per lo scenario peggiore di avvio anziché per il carico a regime. I team finiscono per pagare un margine che usano forse per trenta secondi all’avvio e poi non toccano mai più, e questo si riflette direttamente sulla bolletta cloud e sul numero di nodi necessari solo per far stare le richieste gonfiate di tutti.
Il motivo per cui Kubernetes non è riuscito a offrire una risposta più pulita per così tanto tempo dipende da come è stato costruito il modello delle risorse fin dal primo giorno. Le richieste e i limiti di risorse di un pod venivano trattati come sostanzialmente immutabili una volta avviato il pod, quindi se si volevano risorse diverse tra avvio e regime, l’unica opzione era terminare il pod e farne partire uno nuovo con valori nuovi — il che ovviamente vanifica lo scopo, se quello che si sta cercando di risolvere è proprio un avvio lento. Cambiare questo significava toccare lo scheduler, il kubelet, le API di base e ogni controller che presuppone che le risorse non si muovano una volta ammesso il pod, il che è una superficie enorme da gestire correttamente senza rompere i carichi di lavoro esistenti. Ecco perché il ridimensionamento delle risorse in-place ha richiesto anni di lavoro di progettazione attraverso il processo KEP, invece di essere una semplice patch. Solo di recente è diventato abbastanza stabile da poter ridimensionare un pod in esecuzione senza riavviarlo, il che finalmente dà alle persone un’alternativa reale rispetto al gonfiare i numeri e sperare per il meglio.
Costi cloud
In-Place Pod Resize sembra un dettaglio infrastrutturale, ma lei dice che potrebbe liberare migliaia di core sprecati in un cluster. Può tradurlo in termini che uno sviluppatore o un engineering manager sentirebbero sulla propria bolletta cloud?
Pensiamo a cosa succede oggi in assenza del ridimensionamento in-place. Un servizio ha bisogno di quattro CPU per i primi venti secondi mentre scalda le cache e carica la configurazione, poi si stabilizza e ne usa forse mezza per il resto della sua vita. Dato che non si può cambiare quella richiesta senza un riavvio, la si imposta a quattro CPU in modo permanente, perché nessuno vuole un pod che venga ucciso per mancanza di memoria (OOMKilled) o limitato durante l’avvio in produzione. Ora moltiplicate questo per il numero di repliche che gestite e per quanti servizi nella flotta assomigliano a questo, e vi ritrovate con un’enorme fetta della capacità del cluster riservata a qualcosa che è vero solo per pochi secondi sull’intera vita del pod.
Quella capacità riservata non è gratuita solo perché è inattiva. Lo scheduler deve comunque trovare spazio per quelle quattro CPU su qualche nodo, quindi si finisce per approvvigionare più nodi di quanti ne servano realmente al carico di lavoro, e quei nodi costano soldi indipendentemente dal fatto che le CPU siano occupate o lì ferme come margine di sicurezza. Con il ridimensionamento in-place, si può richiedere qualcosa più vicino a ciò di cui il pod ha effettivamente bisogno in media, lasciarlo salire temporaneamente durante l’avvio e poi farlo scendere automaticamente una volta stabilizzato, tutto senza riavvio. Per un engineering manager che guarda una bolletta cloud, questa è la differenza tra approvvigionare per il picco-per-sempre e approvvigionare per ciò che effettivamente si usa, il che su una flotta grande può significare tagliare il numero di nodi — e quindi la spesa — di una percentuale significativa senza toccare una sola riga di codice applicativo.
Developer experience
Lei lavora sia nel release team di Kubernetes sia sulla developer experience alla CNCF. Funzionalità come questa cambiano davvero il modo in cui gli sviluppatori pensano alla scrittura delle applicazioni, o il comportamento “giusto” viene comunque scaricato sui platform team perché se ne occupino loro?
Sinceramente, nella maggior parte delle organizzazioni questo ricade ancora sul platform team, e non penso che sia una cosa negativa in questo momento. Il ridimensionamento in-place è una primitiva, non una policy, e gli sviluppatori applicativi in generale non dovrebbero aver bisogno di sapere che Kubernetes finalmente permette di cambiare la CPU senza riavvio, più di quanto non abbiano bisogno di sapere come lo scheduler distribuisce i pod sui nodi. Ciò che cambia è che i platform team ora hanno una leva che prima non avevano, quindi invece di dire ai team applicativi “aumentate pure le vostre richieste e non preoccupatevi”, possono costruire un sistema interno che imposta una richiesta di base più bassa e la lascia flettersi automaticamente verso l’alto quando un servizio è effettivamente in fase di avvio o sotto carico. Il miglioramento della developer experience non sta nel fatto che ogni ingegnere capisca improvvisamente il tuning delle risorse, ma nel fatto che la piattaforma può smettere di chiedere loro di indovinare numeri che non erano mai stati messi in condizione di stimare accuratamente.
Il punto in cui invece inizia a cambiare il comportamento degli sviluppatori è ai margini, soprattutto per i team che costruiscono i propri operator o per chi lavora vicino alla workload API, perché ora c’è una vera ragione per pensare ai profili di risorse come a qualcosa di dinamico piuttosto che un numero statico che si imposta una volta e si dimentica. Ed è proprio questo il pattern a cui tengo di più dal punto di vista della DevEx. Una funzionalità come questa ripaga su larga scala solo se il tooling e i valori di default che la circondano sono abbastanza buoni da far sì che la maggior parte degli sviluppatori non debba mai pensarci, e i casi più complessi vengano gestiti da chi capisce davvero i compromessi in gioco. Quindi la risposta breve è che la primitiva è ciò che sblocca il comportamento più intelligente della piattaforma, ma è ancora il platform team a doverlo costruire ed esporlo in modo sensato, che si tratti di un controller in stile VPA, di un motore di policy o di uno strumento interno che semplicemente fa la cosa giusta di default.
Conferenze tech
Ha contribuito direttamente a Kubernetes v1.35 e ora è qui a spiegarlo sul palco. Quanto sono importanti conferenze come Codemotion nel colmare il divario tra “la funzionalità è uscita” e “gli sviluppatori sanno davvero che esiste e come usarla”?
Sinceramente, quel divario è più grande di quanto la maggior parte delle persone si renda conto, ed è uno dei motivi per cui tengo così tanto a questo tipo di talk. Una funzionalità può attraversare anni di progettazione KEP, implementazione, test, arrivare alla stabilità, avere le sue release notes scritte, e restare comunque quasi invisibile alla persona che potrebbe effettivamente trarne beneficio, perché le release notes sono una riga in un changelog che la maggior parte degli sviluppatori non legge mai, sepolta tra altre quaranta modifiche uscite nello stesso ciclo. Essere Comms Lead per la 1.35 me lo ha reso davvero evidente. Spendiamo uno sforzo enorme per assicurarci che una funzionalità sia tecnicamente corretta e ben testata, e relativamente poco sforzo per assicurarci che chi sta costruendo un servizio un martedì pomeriggio sappia davvero che quell’opzione esiste.
Conferenze come Codemotion colmano quel divario in un modo che la sola documentazione non può fare, perché si riesce a mostrare il costo reale in termini che le persone riconoscono dalla propria bolletta o dal proprio turno di reperibilità, invece che in un paragrafo di un riferimento API. Qualcuno può leggere “KEP-1287, ridimensionamento verticale in-place dei pod, promosso a stabile” e non provare nulla, ma se io traduco la stessa idea come “il warm-up della vostra JVM vi sta costando core inattivi che state pagando proprio adesso”, l’impatto è diverso e resta impresso. Funziona anche nella direzione opposta. Non mi limito a spiegare la funzionalità, ma ascolto dalla sala se effettivamente risolve il loro problema o se c’è qualche spigolo che nessuno aveva segnalato durante la revisione della KEP, e quel feedback torna direttamente nel progetto. Quindi direi che il vero valore di un talk in conferenza non è la distribuzione in senso di marketing, è la traduzione — prendere qualcosa che è uscito correttamente e assicurarsi che venga anche usato correttamente.
Cosa viene dopo
Lei si descrive come qualcuno che “cerca di rendere il cloud più veloce, più verde e decisamente meno fastidioso per tutti”. Se In-Place Pod Resize è un pezzo di questo puzzle — qual è il pezzo che manca ancora e che la frustra di più?
Ciò che manca ancora, sinceramente, è che questi pezzi vengono ancora trattati come problemi separati, quando in realtà sono lo stesso problema visto da angolazioni diverse. HPA e VPA sono un buon esempio. HPA decide di quante repliche avete bisogno, VPA decide quanto grande dovrebbe essere ciascuna, e i due in realtà non si coordinano, quindi si può finire con entrambi che scattano sullo stesso picco, scalando in orizzontale e in verticale contemporaneamente quando uno solo dei due sarebbe bastato. C’è un lavoro agli inizi che punta a riunire questi due aspetti in qualcosa di più vicino a uno scheduler realmente multidimensionale, un unico componente che ragiona su entrambi gli assi insieme invece di due controller che reagiscono indipendentemente allo stesso segnale, e penso che questo conterà molto di più di ciascuna delle due ottimizzazioni prese singolarmente, una volta che arriverà davvero.
Il secondo pezzo è che, anche con uno scheduler più intelligente, la piattaforma non ha ancora nessuna visibilità su ciò che il runtime dell’applicazione sta effettivamente facendo dentro il container. Vede CPU e memoria al confine del cgroup, ma non ha idea di cosa stia facendo il runtime applicativo al suo interno, quindi le decisioni sulle risorse vengono prese su numeri tecnicamente accurati ma che non spiegano perché l’applicazione si comporti in quel modo.
E questo si collega al terzo pezzo, il PSI, pressure stall information. A partire da Kubernetes 1.36 le metriche PSI stesse sono stabili ed esposte dal kubelet, quindi il problema dei dati è sostanzialmente risolto. Ciò che manca ancora è che gli scheduler e gli autoscaler che la maggior parte delle persone effettivamente usa, HPA e VPA integrati, non ragionano ancora nativamente su quel segnale. Oggi sono per lo più controller personalizzati a recuperare il PSI per conto proprio, piuttosto che essere l’input di default su cui scala l’intero ecosistema, e finché questo non cambierà, le decisioni sulle risorse continueranno a basarsi su un segnale solo parzialmente completo.

