
Mă numesc Dmitrii Sugrobov, sunt dezvoltator la „Leroy Merlin”. În acest articol voi explica de ce este necesar Helm, cum simplifică munca cu Kubernetes, ce s-a schimbat în a treia versiune și cum să actualizăm aplicațiile în producție fără a avea timpi de nefuncționare.
Acesta este un rezumat bazat pe o prezentare de la conferință prin — dacă nu doriți să citiți, vizionați videoclipul.

De ce folosim Kubernetes în producție
„Leroy Merlin” este lider pe piața retailului DIY în Rusia și Europa. În compania noastră sunt mai mult de o sută de dezvoltatori, 33.000 de angajați interni și un număr mare de persoane care vizitează hipermarketurile și site-ul. Pentru a-i face pe toți fericiți, am decis să ne aliniem standardelor din industrie. Dezvoltăm aplicații noi folosind arhitectura microserviciilor; pentru izolarea mediilor și livrarea corectă folosim containere; iar pentru orchestrare folosim Kubernetes. Prețul utilizării orchestratorilor scade rapid: pe piață crește numărul de ingineri care stăpânesc tehnologia, apar furnizori care oferă Kubernetes ca serviciu.
Tot ceea ce face Kubernetes, desigur, poate fi realizat și în alte moduri, de exemplu, prin aplicarea unor scripturi cu Jenkins și docker-compose, dar de ce să complicăm viața dacă există o soluție gata și fiabilă? Prin urmare, am ales Kubernetes și deja îl folosim în producție de un an. Acum avem douăzeci și patru de clustere Kubernetes, cel mai vechi având peste un an, cu aproximativ două sute de poduri.
Blestemul unui număr mare de fișiere YAML în Kubernetes
Pentru a lansa un microserviciu în Kubernetes, vom crea cel puțin cinci fișiere YAML: pentru Deployment, Service, Ingress, ConfigMap, Secrets — și le vom trimite în cluster. Pentru următoarea aplicație, vom scrie același pachet de fișiere YAML, pentru a treia oară încă unul și așa mai departe. Să înmulțim numărul de documente cu numărul de medii, și deja vom obține sute de fișiere, fără a lua în considerare mediile dinamice.

Adam Reese, menținător principal Helm, a introdus conceptul „”, care arată astfel:
- Copie YAML — copiați fișierul YAML.
- Lipire YAML — inserați-l.
- Corectare Indentări — reparați indentările.
- Repetare — repetați din nou.
Varianta funcționează, dar este necesară copierea frecventă a fișierelor YAML. Pentru a schimba acest ciclu, a fost inventat Helm.
Ce este Helm
În primul rând, Helm este un manager de pachete, ajută la găsirea și instalarea programelor necesare. Pentru instalarea, de exemplu, a MongoDB nu trebuie să accesați site-ul oficial și să descărcați binarele, este suficient să executați comanda helm install stable/mongodb.
În al doilea rând, Helm — un template engine, ajută la parametrizarea fișierelor. Să ne întoarcem la situația cu fișierele YAML din Kubernetes. Este mai simplu să scriem același fișier YAML, să adăugăm în el câțiva placeholders, în care Helm va înlocui valorile. Asta înseamnă că în loc de un set mare de fișiere YAML vom avea un set de template-uri în care, la momentul potrivit, se vor introduce valorile corespunzătoare.
În al treilea rând, Helm — un maestru în deploy. Cu ajutorul lui, puteți instala, reveni la versiunile anterioare și actualiza aplicațiile. Să vedem cum se face acest lucru.

Cum să folosești Helm pentru a-ți desfășura propriile aplicații
Vom instala clientul Helm pe computer, urmând instrucțiunile oficiale . Apoi, vom crea un set de fișiere YAML. În loc de a indica valori concrete, vom lăsa placeholders, pe care Helm le va completa ulterior cu informații. Setul acestor fișiere se numește Helm chart. Poate fi trimis către clientul consolei Helm în trei moduri:
- specificând folderul cu template-uri;
- împachetând într-un arhiv .tar și indicând-l;
- încercând să adăugăm template-ul într-un repository remote și adăugând linkul către repository în clientul Helm.
De asemenea, este nevoie de un fișier cu valorile — values.yaml. Datele din acesta vor fi inserate în template. Să-l creăm și pe acesta.

În a doua versiune a Helm, există o aplicație server suplimentară — Tiller. Aceasta rulează extern Kubernetes-ului și așteaptă cererile de la clientul Helm, iar la solicitare înlocuiește valorile necesare în template și le trimite în Kubernetes.

Helm 3 este mai simplu: în loc de a procesa template-urile pe server, informația este acum procesată complet de către clientul Helm și trimisă direct la API-ul Kubernetes. Această simplificare îmbunătățește securitatea cluster-ului și face schema de deploy mai ușoară.
Cum funcționează totul
Executăm comanda helm install. Vom indica numele release-ului aplicației, vom da calea către values.yaml. La sfârșit, vom specifica repository-ul unde se află chartul și numele acestuia. În exemplul nostru, acestea sunt «lmru» și «bestchart».
helm install --name bestapp --values values.yaml lmru/bestchart
Executarea comenzii este posibilă doar o singură dată, la o nouă execuție în loc de install trebuie utilizat upgrade. Pentru simplificare, în loc de două comenzi, se poate executa comanda upgrade cu o cheie suplimentară --install. La prima execuție, Helm va trimite comanda pentru instalarea release-ului, iar ulterior îl va actualiza.
helm upgrade --install bestapp --values values.yaml lmru/bestchart
Capcane în desfășurarea noilor versiuni ale aplicației cu Helm
În acest moment al povestirii, mă joc cu sala în «Cine vrea să fie milionar», și aflăm cum să facem ca Helm să actualizeze versiunea aplicației. .
Când studiam funcționarea lui Helm, m-a surprins comportamentul ciudat la încercarea de a actualiza versiunile aplicațiilor în execuție. Codul aplicației a fost actualizat, am încărcat o nouă imagine în docker registry, am trimis comanda pentru desfășurare – și nimic nu s-a întâmplat. Mai jos sunt câteva metode nu tocmai reușite de actualizare a aplicațiilor. Studiind fiecare dintre ele mai în detaliu, începi să înțelegi structura internă a instrumentului și motivele acestui comportament neașteptat.
Metoda 1. Nu schimba informația de la ultima execuție
Cum spune Helm, „Chiartele Kubernetes pot fi mari și complexe, așa că Helm încearcă să nu schimbe nimic fără motiv”. Prin urmare, dacă actualizezi versiunea latest a imaginii aplicației în docker registry și execuți comanda helm upgrade, nimic nu se va întâmpla. Helm va crede că nimic nu s-a schimbat și nu va fi nevoie să trimită o comandă pentru actualizarea aplicației în Kubernetes.
Aici și mai departe, eticheta latest este arătată doar ca exemplu. Când folosești această etichetă, Kubernetes va descărca de fiecare dată imaginea din docker registry, indiferent de parametrul imagePullPolicy. Utilizarea latest în producție este nedorită și cauzează efecte secundare.
Metoda 2. Actualizează LABEL în imagine
Așa cum este scris în același , „Helm va actualiza aplicația doar dacă aceasta s-a schimbat de la ultima publicație”. O variantă logică pentru aceasta ar părea actualizarea etichetei LABEL în imaginea docker. Cu toate acestea, Helm nu verifică imaginile aplicațiilor și nu are nicio idee despre eventualele modificări ale acestora. Prin urmare, la actualizarea etichetelor din imagine, Helm nu va afla despre ele, iar comanda de actualizare a aplicației în Kubernetes nu va fi trimisă.
Metoda 3. Folosește cheia --force

Să ne întoarcem la manuale și să căutăm cheia dorită. Cea mai potrivită în context este cheia --force. Cu toate că numele sugerează altceva, comportamentul diferă de așteptări. În loc de a forța actualizarea aplicației, adevărata sa destinație este restaurarea unui release aflat în status FAILED. Dacă nu se folosește această opțiune, trebuie să executați comenzi în ordine. helm delete && helm install --replace. În schimb, se recomandă utilizarea opțiunii --force, care automatizează execuția în ordine a acestor comenzi. Mai multe informații în acest . Pentru a spune Helm să actualizeze versiunea aplicației, din păcate, această opțiune nu se potrivește.
Metoda 4. Schimbați etichetele direct în Kubernetes.

Actualizarea etichetei direct în cluster cu comanda kubectl edit — o idee proastă. Această acțiune va duce la inconsistențe între informațiile aplicației în execuție și ceea ce a fost inițial trimis la desfășurare. Comportamentul Helm în această situație diferă de versiunea sa: Helm 2 nu va face nimic, iar Helm 3 va desfășura o nouă versiune a aplicației. Pentru a înțelege cauza, trebuie să știm cum funcționează Helm.
Cum funcționează Helm?
Pentru a determina dacă aplicația s-a schimbat de la ultima desfășurare, Helm poate utiliza:
- aplicația în execuție în Kubernetes;
- un nou values.yaml și chartul actual;
- informațiile interne Helm despre release-uri.
Pentru cei mai curioși: unde păstrează Helm informațiile interne despre release-uri?Executând comanda helm history, vom obține toate informațiile despre versiunile instalate cu Helm.

Există, de asemenea, informații detaliate despre șabloanele și valorile trimise. Putem solicita aceste informații:

În a doua versiune Helm, aceste informații erau păstrate în același namespace în care era rulat Tiller (implicit – kube-system), în ConfigMap marcat cu eticheta „OWNER=TILLER”:

Odată cu apariția celei de-a treia versiuni Helm, informațiile s-au mutat în secrete, în același namespace în care este rulat aplicația. Datorită acestui fapt, a devenit posibil să se desfășoare simultan mai multe aplicații în namespace-uri diferite cu același nume de release. În a doua versiune, aceasta era o mare durere de cap, când namespace-urile sunt izolate, dar pot influența una pe cealaltă.

Al doilea Helm, atunci când încearcă să înțeleagă dacă este necesară o actualizare, folosește doar două surse de informație: ceea ce i s-a furnizat acum și informațiile interne despre release-uri, care sunt păstrate în ConfigMap.

Al treilea Helm folosește strategia de îmbinare în trei moduri: pe lângă informațiile existente, ia în considerare și aplicația care rulează în prezent în Kubernetes.

Din această cauză, versiunea veche a Helm nu va face nimic, deoarece nu consideră informațiile aplicației din cluster, iar Helm 3 va obține modificările și va trimite o nouă aplicație pentru desfășurare.
Metoda 5. Folosiți cheia —recreate-pods
Prin intermediul cheii --recreate-pods se poate ajunge la ceea ce s-a intenționat inițial să se obțină folosind cheia --force. Containerele se vor reporni și, conform politicii imagePullPolicy: Always pentru eticheta latest (despre care am vorbit în nota de mai sus), Kubernetes va descărca și va lansa o nouă versiune a imaginii. Acest lucru se va face într-un mod nu tocmai optim: fără a lua în considerare StrategyType al desfășurării, va opri brusc toate instanțele vechi ale aplicației și va începe să lanseze altele noi. În timpul repornirii, sistemul nu va funcționa, iar utilizatorii vor suferi.
În Kubernetes a existat o problemă similară care a persistat o perioadă îndelungată. Și iată, după 4 ani de la deschiderea , problema a fost corectată, iar începând cu versiunea 1.15 a Kubernetes, apare posibilitatea de rolling-restart a podurilor.
Helm pur și simplu oprește toate aplicațiile și lansează în paralel noi containere. În producție, așa nu se poate proceda, pentru a nu provoca o întrerupere a aplicației. Aceasta se face doar pentru nevoile dezvoltării, putând fi realizată doar în medii de tip staging.
Cum se actualizează versiunea aplicației cu ajutorul Helm?
Vom schimba valorile trimise către Helm. De obicei, acestea sunt valorile înlocuite pentru eticheta imaginii. În cazul celui mai recent, utilizat frecvent pentru medii neproductive, informația modificabilă este o adnotare, care este inutilă pentru Kubernetes, dar va acționa ca un semnal pentru Helm de necesitate a actualizării aplicației. Variantele de completare a valorii adnotării sunt:
- O valoare aleatoare prin intermediul funcției standard —
{{ randAlphaNum 6 }}.
Există un detaliu: după fiecare desfășurare folosind un charta cu o astfel de variabilă, valoarea adnotării va fi unică, iar Helm va considera că există modificări. Rezultatul este că vom reporni constant aplicația, chiar dacă nu ne-am schimbat versiunea. Acest lucru nu este critic, deoarece nu vor exista perioade de nefuncționare, dar totuși este neplăcut. - Introducerea datei și orei curente —
{{ .Release.Date }}.
Această variantă este similară cu o valoare aleatoare, având o variabilă constant unică. - O modalitate mai corectă este să folosești sume de control. Acesta este SHA al imaginii sau SHA al ultimei comiteri în Git —
{{ .Values.sha }}.
Acestea trebuie calculate și trimise clientului Helm pe partea apelantă, de exemplu în Jenkins. Dacă aplicația se schimbă, atunci și suma de control se va schimba. Prin urmare, Helm va actualiza aplicația doar atunci când este necesar.
Să rezumăm încercările noastre
- Helm face modificările într-un mod cât mai puțin invaziv, așa că orice schimbare la nivelul imaginii aplicației în Docker Registry nu va duce la actualizări: după executarea comenzii, nimic nu se va întâmpla.
- Cheia
--forceeste utilizat pentru restaurarea versiunilor problematice și nu este legat de actualizarea forțată. - Cheia
--recreate-podsva actualiza forțat aplicațiile, dar o va face într-un mod barbar: va opri abrupt toate containerele. Acest lucru va afecta utilizatorii, astfel că nu ar trebui făcut în producție. - Nu trebuie să facem modificări direct în clusterul Kubernetes prin comanda
kubectl edit: vom încălca consistența, iar comportamentul va varia în funcție de versiunea Helm. - Odată cu lansarea unei noi versiuni Helm, au apărut multe nuanțe. Problemele din repository-ul Helm sunt descrise într-un mod clar, ele te vor ajuta să înțelegi detaliile.
- Adăugarea unei anotări modificabile în chart va face sistemul mai flexibil. Acest lucru va permite desfășurarea aplicației corect, fără downtime.
Ideea din categoria „pacea în întreaga lume”, care funcționează în toate domeniile vieții: citește instrucțiunile înainte de utilizare, nu după. Doar având informații complete, poți construi sisteme fiabile și face utilizatorii fericiți.
Alte linkuri pe tema:
Această prezentare a avut loc pentru prima dată la de la Mail.ru Cloud Solutions. Vezi alte prezentări și abonează-te la anunțurile evenimentelor în Telegram .
Sursa: habr.com
