
Dietro le quinte
Più IA e ancora più trasparenza dei prezzi – idee e risultati dell'«Hackfest»
di Martin Jungfer
Le anteprime di deployment sono sempre un problema, soprattutto se non sono ordinate. Qui ti spiego cosa sono e come le abbiamo ordinate.
Chiunque abbia a che fare con i deployment sa che devono essere aggiornati di tanto in tanto, e chi aggiorna vuole anche poter testare (e chi non vuole, deve comunque farlo, ma questo è un altro tema). Per quanto riguarda i problemi del front-end, c'è l'ulteriore difficoltà che spesso devono essere testate diverse modifiche contemporaneamente, motivo per cui non possiamo semplicemente sovrascrivere il deployment di test esistente. A questo servono le anteprime di deployment. Si tratta di distribuzioni front-end che servono a testare una modifica specifica e che a volte devono essere eseguite in parallelo in numero maggiore. Affinché coloro che non sono specializzati in tecnologia possano osservarli senza dover frequentare un corso, dovrebbero essere facilmente accessibili anche dal browser.
Eseguiamo tutte queste implementazioni su Kubernetes, più precisamente su Azure Kubernetes Service (AKS), che ci porta alcuni vantaggi, ma non ci facilita sempre tutto. A proposito, utilizziamo anche GKE, alias Google Kubernetes Engine, ma non per questo caso d'uso. In particolare, AKS non riordina volontariamente, il che non è un problema con i deployment regolari (in cui le versioni più recenti sovrascrivono quelle più vecchie), ma lo è con le anteprime del front-end, che non sono autorizzate a sovrascriversi l'una con l'altra e sono quindi talvolta presenti in numero imprevedibile. Dopo l'uso devono essere riposti in modo ordinato. Ma da bambini non volevamo saperne nulla di ordine e, dato che gli informatici sono fondamentalmente dei bambini un po' cresciuti, non ce lo si può aspettare nemmeno da noi. Quindi, avevamo molti deployment in corso, e con essi anche tipi di risorse dedicate di Service e Ingress, e nessuno che riordina regolarmente. Possiamo almeno mantenere a zero i pod responsabili del consumo di CPU e RAM finché non sono necessari?
Horizontal Pod Autoscaler to the rescue! Se solo fosse così semplice... perché non scala a zero, o non scala un deployment con zero repliche, perché avrebbe bisogno di almeno un pod di cui può misurare il consumo di CPU e RAM. Quindi abbiamo dovuto mantenere tutte le anteprime di deployment in esecuzione con almeno un pod, nel caso in cui qualcuno volesse dare un'occhiata, e i nostri team di front-end effettuano il deployment in modo molto diligente. Questo costa risorse che non sono disponibili per altri deployment e che devono essere compensate da nodi più grandi o più numerosi, ma ovviamente non sono gratuiti. Abbiamo quindi dovuto trovare un'altra soluzione e, dopo alcune ricerche, l'abbiamo trovata in Knative.
Red Hat dice: kay-nay-tiv. Utilizziamo la sua componente di servizio. Per gli eventi, invece, ci affidiamo a KEDA, ma questa è un'altra storia e verrà raccontata un'altra volta; inoltre, al momento non utilizziamo funzioni come il «traffic splitting».
In sostanza, i servizi Knative sono molto simili alla solita triade deployment – service – ingress, ma si basano su definizioni di risorse personalizzate e sono in grado di scalare da zero non appena arrivano le richieste. Questo ci permette di creare e distribuire un'anteprima di deployment per tutte le richieste di pull senza dover riservare risorse di sistema, indipendentemente dal fatto che vengano visualizzate o meno. Un controller ingress dedicato, di proprietà di Knative, chiamato Kourier, aiuta anche a instradare le richieste verso l'istanza desiderata senza dover impostare manualmente nulla.
Questo è l'aspetto di un deployment regolare:
E questo di uno creato con Knative:
Naturalmente, i nostri team di funzionalità non devono occuparsi di Knative o di altri problemi legati all'infrastruttura più del necessario. Noi, il team Bender, siamo uno dei diversi team di ingegneria della piattaforma e, oltre a Kubernetes, siamo anche responsabili di rendere la distribuzione il più confortevole possibile per i team di funzionalità. Per questo motivo, qualche tempo fa abbiamo iniziato a creare un'intera collezione di chart Helm che astraggono da tutti i dettagli più importanti, in modo tale che ogni team debba avere solo un semplice file values.yaml con i dettagli più importanti nel proprio repository.
La fornitura di Knative è stata essenzialmente un affare a due facce:
Il controller ingress di Kourier, in collaborazione con DNS wildcard, ci ha quindi aiutati a rendere disponibile ogni anteprima di deployment con il suo ID di richiesta di pull e siamo partiti.
Non problemi, ma sfide. Kourier come controller aggiuntivo ingress ci è costato un po' di fatica in più. Fondamentalmente, ci affidiamo a Nginx. Avremmo preferito gestire questo aspetto allo stesso modo per Knative, dato che le regole di ingress devono spesso essere configurate in modo specifico per il controller. Poiché le nostre anteprime di deployment utilizzano tutte un sottodominio wildcard che non viene utilizzato per nient'altro, siamo stati in grado di indirizzare il traffico verso Kourier abbastanza facilmente senza che Nginx debba sentirsi responsabile.
Sfortunatamente, però, i deployment Knative hanno caratteristiche prestazionali un po' diverse da quelli normali in alcuni punti, quindi possiamo usarli per i test di carico solo in misura limitata. Sospettiamo che si tratti del controller Kourier, che effettua anche il buffering in alcuni punti in cui Nginx non lo fa, ma a questo punto non è altro che una lettura dei fondi di caffè. Fortunatamente, in un reparto IT i fondi di caffè non mancano mai.
Un'altra sfida è stata rappresentata dal fatto che Knative non supporta i mount hostPath. Tuttavia, Datadog, che utilizziamo come soluzione di monitoraggio, ne ha bisogno per poter montare la sua configurazione in ogni pod, quindi per il momento siamo privi di metriche e avvisi per quanto riguarda le anteprime di deployment. I deployment regolari non usano Knative, quindi non è un grosso problema, anche se sarebbe bello notare qualche problema già a questo punto. I dettagli sono ancora da chiarire, ma abbiamo già un'idea di base di come questo potrebbe essere risolto.
La sfida più grande è stato il riordino di cui sopra. Knative non fa nemmeno questo da solo.
Knative crea più servizi per ogni anteprima di deployment, in modo che le versioni più vecchie e quelle più recenti possano funzionare fianco a fianco. E poiché abbiamo motivazioni diverse quando si tratta di riordinare, a un certo punto ci sono state migliaia di risorse K8s che giacevano nel namespace del front-end. A Kubernetes questo non piace molto, perché in questo caso nessun servizio può trovare i suoi pod in questo spazio, finché non si richiama la Marie Kondō che è in noi e si fa ciò che deve essere fatto. Ce ne siamo accorti perché anche i normali deployment test di front-end, con cui Knative condivide lo stesso namespace, hanno smesso di funzionare. Come abbiamo scoperto, non è l'idea migliore. Ovviamente, il semplice mantenimento del numero di pod a zero non bastava.
Abbiamo quindi creato degli script shell (un po' in vecchio stile, ma comunque utili) che vengono eseguiti dalle pipeline in esecuzione regolare e che cancellano automaticamente tutte le anteprime di deployment più vecchie di 14 giorni e quelle le cui richieste di pull sono state completate. In questo modo, i nostri namespace sono sempre belli e puliti e Kubernetes non ha più problemi a fare il suo lavoro.
Una rapida occhiata al namespace (con il generoso supporto di kubectl e grep) mostra attualmente (1.6.2023, 12:52) la presenza di 43 anteprime di deployment. I pod di anteprima sono tre. quindi 40 deployment sono attualmente in esecuzione con zero pod, mentre prima di Knative ce n'era almeno uno a testa. La maggior parte dei nodi del nostro cluster di prova ha 16 core di CPU e 32 GiB di RAM: tutti quei pod di anteprima che non sono in esecuzione perché non ne abbiamo bisogno ci fanno risparmiare circa mezzo nodo. In pratica, si tratta di un nodo intero. Non sembra molto, ma per noi è un risparmio notevole e probabilmente è solo la punta dell'iceberg: tutti i pod che sarebbero appartenuti alle anteprime di deployment e che non vediamo più perché li riordiniamo regolarmente, il loro numero sarebbe probabilmente molto più alto.
Per illustrare questo aspetto, ecco due grafici che mostrano chiaramente i nostri risparmi in base al numero di pod nel tempo:
So che non ci sono molti dettagli, ma almeno ho etichettato le assi.
Naturalmente, non abbiamo ancora finito di implementare Knative. Alcune delle sfide menzionate sono pronte per essere affrontate, altre potrebbero essere più performanti e chissà che un domani non si trovi un'alternativa migliore a Knative.
Hai avuto a che fare con casi d'uso simili? Come hai risolto il problema? Usi Knative o qualcos'altro? Idee, commenti, domande, numeri del lotto di domani: non esitare a postare qualsiasi cosa nei commenti!