
Mi chiamo Dmitrij Sugrobov, sono sviluppatore presso «Leroy Merlin». In questo articolo parlerò del perché Helm sia necessario, come semplifica il lavoro con Kubernetes, cosa è cambiato nella terza versione e come usarlo per aggiornare le applicazioni in produzione senza downtime.
Questa è una sintesi ispirata a una presentazione conferenza by — se non volete leggere, guardate il video.

Perché usiamo Kubernetes in produzione
«Leroy Merlin» è leader nel mercato del retail DIY in Russia e in Europa. Nella nostra azienda lavorano più di cento sviluppatori, 33.000 dipendenti interni e un'enorme quantità di persone visitano i ipermarchi e il sito. Per farli tutti felici, abbiamo deciso di seguire gli approcci standard nel settore. Sviluppiamo nuove applicazioni utilizzando architetture a microservizi; per isolare gli ambienti e garantire una corretta distribuzione utilizziamo container; e per l'orchestrazione utilizziamo Kubernetes. Il costo dell'utilizzo degli orchestratori sta rapidamente diminuendo: nel mercato cresce il numero di ingegneri esperti nella tecnologia e ci sono fornitori che offrono Kubernetes come servizio.
Tutto ciò che fa Kubernetes può sicuramente essere fatto in altri modi, come impiegare script con Jenkins e docker-compose, ma perché complicarsi la vita quando esiste una soluzione pronta e affidabile? Per questo motivo abbiamo scelto Kubernetes e lo utilizziamo in produzione da un anno. Attualmente gestiamo ventiquattro cluster Kubernetes, il più vecchio dei quali ha più di un anno e conta circa duecento pod.
La maledizione del gran numero di file YAML in Kubernetes
Per avviare un microservizio in Kubernetes, creeremo almeno cinque file YAML: per Deployment, Service, Ingress, ConfigMap, Secrets e li invieremo al cluster. Per l'applicazione successiva scriveremo lo stesso pacchetto di file YAML, con un terzo ancora e così via. Moltiplicando il numero di documenti per il numero di ambienti, otteniamo già centinaia di file, senza contare gli ambienti dinamici.

Adam Reese, core maintainer di Helm, ha introdotto il concetto di «», che appare così:
- Copia YAML — copiare il file YAML.
- Incolla YAML — incollarlo.
- Correggi gli indent — sistemare gli spazi.
- Ripeti — ripetere di nuovo.
Questo approccio funziona, ma richiede di copiare molte volte i file YAML. È per modificare questo ciclo che è nato Helm.
Che cos'è Helm
Innanzitutto, Helm è un gestore di pacchetti, che aiuta a trovare e installare i programmi necessari. Per installare, ad esempio, MongoDB, non è necessario visitare il sito ufficiale e scaricare i binari, basta eseguire il comando helm install stable/mongodb.
In secondo luogo, Helm — un generatore di template, aiuta a parametrizzare i file. Torniamo alla situazione con i file YAML in Kubernetes. È più semplice scrivere lo stesso file YAML, aggiungere alcuni segnaposto, nei quali Helm inserirà i valori. Quindi, invece di un grande insieme di file YAML, ci sarà un insieme di template, nei quali al momento opportuno verranno inseriti i valori necessari.
In terzo luogo, Helm — è un maestro nell'implementazione. Con il suo aiuto è possibile installare, ripristinare e aggiornare le applicazioni. Vediamo come fare.

Come utilizzare Helm per il deployment delle proprie applicazioni
Installeremo il client Helm sul computer, seguendo la documentazione ufficiale . Quindi creeremo un insieme di file YAML. Invece di specificare valori concreti, lasceremo dei segnaposto, che in futuro Helm compilerà con le informazioni. Un insieme di tali file è chiamato chart Helm. Può essere inviato al client console di Helm in tre modi:
- specificando la cartella con i template;
- imballandoli in un archivio .tar e specificando il percorso.
- caricare il template nel repository remoto e aggiungere il link al repository nel client Helm.
È necessaro anche un file di valori — values.yaml. I dati da lì verranno inseriti nel template. Creiamo anche quello.

Nella seconda versione di Helm c'è un'applicazione server aggiuntiva — Tiller. Essa risiede all'esterno di Kubernetes e attende richieste dal client Helm, e al momento della chiamata inserisce i valori richiesti nel template e li invia a Kubernetes.

Helm 3 è più semplice: invece di elaborare i template sul server, le informazioni vengono ora elaborate interamente dal lato del client Helm e inviate direttamente all'API di Kubernetes. Questa semplificazione aumenta la sicurezza del cluster e facilita il processo di distribuzione.
Come funziona tutto ciò
Eseguiamo il comando helm install. Specifichiamo il nome della release dell'applicazione, diamo il percorso per values.yaml. Alla fine indichiamo il repository in cui si trova il chart e il nome del chart. Nell'esempio questi sono «lmru» e «bestchart» rispettivamente.
helm install --name bestapp --values values.yaml lmru/bestchart
L'esecuzione del comando è possibile solo una volta, durante l'esecuzione ripetuta verrà sostituito install bisogna usare aggiornamento. Per semplicità, invece di due comandi, è possibile eseguire il comando aggiornamento con l'opzione aggiuntiva --install. Alla prima esecuzione, Helm invierà un comando per installare la release e in seguito la aggiornerà.
helm upgrade --install bestapp --values values.yaml lmru/bestchart
Le insidie nella distribuzione delle nuove versioni dell'applicazione con Helm
In questo punto della narrazione, sto giocando con il pubblico a 'Chi vuol essere milionario' e scopriamo come far aggiornare Helm alla nuova versione dell'applicazione. .
Quando ho studiato come funziona Helm, sono rimasto sorpreso dal comportamento strano durante il tentativo di aggiornare le versioni delle applicazioni già in esecuzione. Ho aggiornato il codice dell'app, caricato una nuova immagine nel docker registry e inviato il comando per la distribuzione – e non è successo nulla. Di seguito ci sono alcuni modi non del tutto efficaci per aggiornare le applicazioni. Esaminando ciascuno di essi in dettaglio, inizia a comprendere il funzionamento interno dello strumento e le ragioni di questo comportamento non intuitivo.
Metodo 1. Non modificare le informazioni dall'ultimo avvio
Come dice Helm, 'I chart di Kubernetes possono essere grandi e complessi, quindi Helm cerca di non toccare nulla più del necessario'. Pertanto, se aggiorni l'immagine più recente dell'app nel docker registry e esegui il comando helm upgrade, quindi non succederà nulla. Helm penserà che nulla sia cambiato e non sarà necessario inviare a Kubernetes un comando per aggiornare l'applicazione.
Qui e oltre, il tag latest è mostrato esclusivamente come esempio. Specificando questo tag, Kubernetes scaricherà ogni volta l'immagine dal registry Docker, indipendentemente dal parametro imagePullPolicy. L'uso di latest in produzione è indesiderato e può causare effetti collaterali.
Metodo 2. Aggiornare il LABEL nell'immagine
Come scritto nello stesso , "Helm aggiornerà l'applicazione solo se è cambiata dalla ultima release." Un'opzione logica per questo sarebbe aggiornare l'etichetta LABEL nell'immagine Docker stessa. Tuttavia, Helm non esamina le immagini delle applicazioni e non ha idea di eventuali cambiamenti in esse. Pertanto, aggiornando le etichette nell'immagine, Helm non ne sarà a conoscenza e il comando di aggiornamento dell'applicazione in Kubernetes non verrà eseguito.
Metodo 3. Utilizzare la chiave —force

Rivolgendoci ai manuali e cercando la chiave necessaria. La chiave più adatta per significato è —force. Nonostante il nome, il comportamento è diverso da quanto previsto. Invece di forzare l'aggiornamento dell'applicazione, il suo vero scopo è ripristinare una release che si trova nello stato FAILED. Se non usi questa chiave, devi eseguire i comandi in modo sequenziale. helm delete && helm install --replace. Invece, si consiglia di utilizzare la chiave —force, che automatizza l'esecuzione sequenziale di questi comandi. Maggiori informazioni in questo . Per dire a Helm di aggiornare comunque la versione dell'applicazione, purtroppo questa chiave non è adatta.
Metodo 4. Modificare direttamente le etichette in Kubernetes.

Aggiornare l'etichetta direttamente nel cluster utilizzando il comando kubectl edit è una cattiva idea. Questa azione porterà a un'incoerenza tra l'applicazione in esecuzione e ciò che è stato originariamente inviato per il deployment. Il comportamento di Helm durante il deployment in questo caso è diverso rispetto alla sua versione: Helm 2 non farà nulla, mentre Helm 3 deployerà una nuova versione dell'applicazione. Per comprendere il motivo, è necessario capire come funziona Helm.
Come funziona Helm.
Per determinare se l'applicazione è cambiata dall'ultimo rilascio, Helm può avvalersi di:
- applicazione in esecuzione su Kubernetes;
- nuovo values.yaml e chart aggiornato;
- informazioni interne di Helm sui rilascio.
Per i più curiosi: dove Helm memorizza le informazioni interne sui rilascio?Eseguendo il comando helm history, otterremo tutte le informazioni sulle versioni installate tramite Helm.

Ci sono anche ulteriori dettagli sui template e valori inviati. Possiamo richiederli:

Nella seconda versione di Helm, queste informazioni si trovano nello stesso namespace in cui è in esecuzione Tiller (di default — kube-system), nel ConfigMap contrassegnato con l'etichetta «OWNER=TILLER»:

Con l'arrivo della terza versione di Helm, le informazioni sono state spostate nei segreti, sempre nello stesso namespace in cui è in esecuzione l'applicazione. Questo ha reso possibile eseguire più applicazioni contemporaneamente in diversi namespace con lo stesso nome di rilascio. Nella seconda versione, era un grosso problema quando i namespace erano isolati ma potevano influenzarsi a vicenda.

Il secondo Helm, quando cerca di capire se è necessario un aggiornamento, utilizza solo due fonti di informazione: quelle fornite attualmente e le informazioni interne sui rilascio, che si trovano nel ConfigMap.

Il terzo Helm utilizza una strategia di merge a tre vie: oltre alle informazioni esistenti, considera anche l'applicazione attualmente attiva in Kubernetes.

Per questo motivo, la vecchia versione di Helm non svolgerà alcuna operazione, poiché non tiene conto delle informazioni dell'applicazione nel cluster, mentre Helm 3 riceverà le modifiche e invierà una nuova applicazione per il deploy.
Modo 5. Utilizzare l'opzione —recreate-pods
Con l'opzione --recreate-pods è possibile raggiungere ciò che si intendeva inizialmente ottenere con l'opzione —force. I contenitori verranno riavviati e, in base alla politica imagePullPolicy: Always per il tag latest (come spiegato nella nota precedente), Kubernetes scaricherà e avvierà una nuova versione dell'immagine. Questo non avverrà nel modo migliore: ignorando lo StrategyType del deployment, spegnerà bruscamente tutte le vecchie istanze dell'applicazione e procederà a lanciare nuove. Durante il riavvio, il sistema non funzionerà e gli utenti subiranno disagi.
Nel Kubernetes stesso, un problema simile è esistito a lungo. E ora, dopo 4 anni dall'apertura , il problema è stato risolto e a partire dalla versione 1.15 di Kubernetes è disponibile la possibilità di rolling-restart dei pod.
Helm semplicemente chiude tutte le applicazioni e avvia nuovi contenitori affianco. In produzione non è permesso farlo, per non causare inattività all'applicazione. Questo dovrebbe essere fatto solo per esigenze di sviluppo, e può essere eseguito solo in ambienti di stage.
Come aggiornare la versione dell'applicazione utilizzando Helm?
Modificheremo i valori inviati a Helm. Di solito, si tratta di valori sostituiti al posto del tag dell'immagine. Nel caso di latest, comunemente usato per ambienti non produttivi, l'informazione modificabile è l'annotazione, che per Kubernetes stessa è inutile, mentre per Helm rappresenta un segnale per l'aggiornamento dell'applicazione. Opzioni per riempire il valore dell'annotazione:
- Valore casuale utilizzando la funzione standard —
{{ randAlphaNum 6 }}.
C'è un'osservazione: dopo ogni deployment utilizzando un chart con tale variabile, il valore dell'annotazione sarà unico, e Helm presumerà che ci siano delle modifiche. Di conseguenza, riavvieremo sempre l'applicazione, anche se non ne abbiamo cambiato la versione. Questo non è critico, poiché non ci sarà inattività, ma è comunque sgradevole. - Inserire la data e ora —
{{ .Release.Date }}.
L'opzione sembra un valore casuale con una variabile sempre unica. - Un modo più corretto è utilizzare checksum. Questo è lo SHA dell'immagine o lo SHA dell'ultimo commit nel git —
{{ .Values.sha }}.
Dovranno essere calcolati e inviati al client Helm sul lato chiamante, ad esempio in Jenkins. Se l'applicazione è cambiata, anche il checksum cambierà. Di conseguenza, Helm aggiornerà l'applicazione solo quando necessario.
Riassumiamo i nostri tentativi.
- Helm apporta le modifiche nel modo meno invasivo, quindi qualsiasi cambiamento a livello di immagine dell'applicazione nel Docker Registry non porterà a un aggiornamento: dopo l'esecuzione del comando, non accadrà nulla.
- Chiave
—forceè utilizzato per il ripristino di release problematiche e non è correlato a un aggiornamento forzato. - Chiave
--recreate-podsaggiornerà forzatamente le applicazioni, ma lo farà in modo distruttivo: spegnerà bruscamente tutti i container. Gli utenti subiranno disagi; in produzione non si dovrebbe fare in questo modo. - Non è necessario apportare modifiche direttamente al cluster Kubernetes usando il comando
kubectl edit: violeremmo la coerenza, e il comportamento varierà a seconda della versione di Helm. - Con l'uscita della nuova versione di Helm, sono emersi molti dettagli. Le Issues nel repository di Helm sono descritte in modo chiaro e possono aiutare a comprendere le sfumature.
- Aggiungere un'annotazione modificabile nel chart lo renderà più flessibile. Questo permetterà di distribuire l'applicazione in modo corretto, senza interruzione.
Un pensiero del tipo "pace nel mondo", che funziona in tutti gli ambiti della vita: leggi le istruzioni prima dell'uso, non dopo. Solo possedendo tutte le informazioni, sarà possibile costruire sistemi affidabili e rendere gli utenti felici.
Altri link pertinenti:
Questa relazione è stata presentata per la prima volta al da Mail.ru Cloud Solutions. Guarda altri interventi e iscriviti agli avvisi degli eventi su Telegram .
Fonte: habr.com
