Îmbinare în trei căi la werf: implementare în Kubernetes cu Helm „pe steroizi”

Ceea ce noi (și nu numai noi) așteptăm de mult timp s-a întâmplat: werf, utilitarul nostru Open Source pentru crearea de aplicații și livrarea lor către Kubernetes, acceptă acum aplicarea modificărilor folosind corecțiile de îmbinare în trei direcții! În plus, este posibil să se adopte resurse K3s existente în versiunile Helm fără a reconstrui aceste resurse.

Îmbinare în trei căi la werf: implementare în Kubernetes cu Helm „pe steroizi”

Dacă este foarte scurt, atunci punem WERF_THREE_WAY_MERGE=enabled — obținem desfășurare „ca în kubectl apply”, compatibil cu instalațiile Helm 2 existente și chiar puțin mai mult.

Dar să începem cu teoria: ce sunt exact patch-urile de îmbinare în trei direcții, cum au venit oamenii cu abordarea pentru a le genera și de ce sunt acestea importante în procesele CI/CD cu infrastructura bazată pe Kubernetes? Și după aceea, să vedem ce este 3-way-merge în werf, ce moduri sunt utilizate implicit și cum să o gestionăm.

Ce este un patch de îmbinare în trei căi?

Deci, să începem cu sarcina de a lansa resursele descrise în manifestele YAML în Kubernetes.

Pentru a lucra cu resurse, API-ul Kubernetes oferă următoarele operațiuni de bază: crearea, corecția, înlocuirea și ștergerea. Se presupune că, cu ajutorul lor, este necesar să se construiască o lansare continuă convenabilă a resurselor către cluster. Cum?

comenzi imperative kubectl

Prima abordare a gestionării obiectelor în Kubernetes este utilizarea comenzilor imperative kubectl pentru a crea, modifica și șterge aceste obiecte. Pur și simplu pune:

  • echipă kubectl run puteți rula Deployment sau Job:
    kubectl run --generator=deployment/apps.v1 DEPLOYMENT_NAME --image=IMAGE
  • echipă kubectl scale — modificați numărul de replici:
    kubectl scale --replicas=3 deployment/mysql
  • etc

Această abordare poate părea convenabilă la prima vedere. Cu toate acestea, există probleme:

  1. Este greu a automatiza.
  2. Ca reflectă configurația în Git? Cum să revizuiți modificările care au loc în cluster?
  3. Cum se oferă reproductibilitatea configurații la repornire?
  4. ...

Este clar că această abordare nu se potrivește bine cu stocarea aplicației și infrastructurii ca cod (IaC; sau chiar GitOps ca o opțiune mai modernă, câștigând popularitate în ecosistemul Kubernetes). Prin urmare, aceste comenzi nu au primit o dezvoltare suplimentară în kubectl.

Creați, obțineți, înlocuiți și ștergeți operațiuni

Cu primar creare este simplu: trimite manifestul la operație create kube api și resursa a fost creată. Reprezentarea YAML a manifestului poate fi stocată în Git și creată folosind comanda kubectl create -f manifest.yaml.

С îndepărtare de asemenea simplu: înlocuiți același manifest.yaml de la Git la echipă kubectl delete -f manifest.yaml.

Operație replace vă permite să înlocuiți complet configurația resursei cu una nouă, fără a recrea resursa. Aceasta înseamnă că înainte de a face o modificare la o resursă, este logic să interoghezi versiunea curentă cu operația get, schimbați-l și actualizați-l cu operația replace. kube apiserver este încorporat blocare optimistă și, dacă după operație get obiectul s-a schimbat, apoi operația replace nu va trece.

Pentru a stoca configurația în Git și a o actualiza folosind înlocuire, trebuie să faceți operația get, îmbinați configurația din Git cu ceea ce am primit și executați replace. În mod implicit, kubectl vă permite doar să utilizați comanda kubectl replace -f manifest.yamlUnde manifest.yaml — un manifest deja complet pregătit (în cazul nostru, fuzionat) care trebuie instalat. Se pare că utilizatorul trebuie să implementeze manifeste de îmbinare, iar aceasta nu este o chestiune banală...

De asemenea, este de remarcat faptul că, deși manifest.yaml și este stocat în Git, nu putem ști în prealabil dacă este necesar să creăm un obiect sau să-l actualizăm - acest lucru trebuie făcut de software-ul utilizatorului.

Total: putem construi o lansare continuă folosind doar crearea, înlocuirea și ștergerea, asigurându-vă că configurația infrastructurii este stocată în Git împreună cu codul și CI/CD convenabil?

În principiu, putem... Pentru asta va trebui să implementați operația de îmbinare manifeste și un fel de legare care:

  • verifică prezența unui obiect în cluster,
  • realizează crearea inițială a resurselor,
  • îl actualizează sau îl șterge.

Când actualizați, vă rugăm să rețineți că resursa poate să se fi schimbat de ultima dată get și gestionați automat cazul de blocare optimistă - faceți încercări repetate de actualizare.

Totuși, de ce să reinventeze roata când kube-apiserver oferă o altă modalitate de actualizare a resurselor: operațiunea patch, care scutește utilizatorul de unele dintre problemele descrise?

Plasture

Acum ajungem la patch-uri.

Patch-urile sunt modalitatea principală de a aplica modificări obiectelor existente în Kubernetes. Operațiune patch funcționează așa:

  • utilizatorul kube-apiserver trebuie să trimită un patch în formă JSON și să specifice obiectul,
  • iar apiserver însuși se va ocupa de starea curentă a obiectului și îl va aduce la forma necesară.

Blocarea optimistă nu este necesară în acest caz. Această operațiune este mai mult declarativă decât înlocuire, deși la început poate părea invers.

În acest fel:

  • folosind o operație create creăm un obiect conform manifestului din Git,
  • via delete — ștergeți dacă obiectul nu mai este necesar,
  • via patch — schimbăm obiectul, aducându-l la forma descrisă în Git.

Cu toate acestea, pentru a face acest lucru, trebuie să creați plasture corect!

Cum funcționează patch-urile în Helm 2: 2-way-merge

Când instalați pentru prima dată o versiune, Helm efectuează operația create pentru resursele grafice.

Când actualizați o versiune Helm pentru fiecare resursă:

  • ia în considerare patch-ul dintre versiunea resursei din diagrama anterioară și versiunea diagramă curentă,
  • aplică acest plasture.

Vom numi acest patch Patch de îmbinare în 2 căi, deoarece în crearea lui sunt implicate 2 manifeste:

  • manifest de resurse din versiunea anterioară,
  • manifestă resursă din resursa curentă.

La scoaterea operației delete în kube apiserver este apelat pentru resursele care au fost declarate în versiunea anterioară, dar nu au fost declarate în cea actuală.

Abordarea patch-urilor de îmbinare în două căi are o problemă: duce la desincronizat cu starea reală a resursei din cluster și cu manifestul din Git.

Ilustrarea problemei cu un exemplu

  • În Git, o diagramă stochează un manifest în care câmpul image Desfășurarea contează ubuntu:18.04.
  • Utilizator prin kubectl edit a schimbat valoarea acestui câmp în ubuntu:19.04.
  • Când reinstalați diagrama Helm nu generează un patch, deoarece domeniul image în versiunea anterioară a ediției și în graficul actual sunt aceleași.
  • După redistribuire image rămâne ubuntu:19.04, deși graficul spune ubuntu:18.04.

Ne-am desincronizat și am pierdut declarativitatea.

Ce este o resursă sincronizată?

În general vorbind complet Este imposibil să obțineți o potrivire între manifestul resursei dintr-un cluster care rulează și manifestul din Git. Pentru că într-un manifest real pot exista adnotări/etichete de serviciu, containere suplimentare și alte date care sunt adăugate și eliminate din resursă în mod dinamic de către unii controlori. Nu putem și nu dorim să păstrăm aceste date în Git. Cu toate acestea, dorim ca câmpurile pe care le-am specificat în mod explicit în Git să ia valorile adecvate la lansare.

Se dovedește atât de general regula resurselor sincronizate: atunci când lansați o resursă, puteți modifica sau șterge numai acele câmpuri care sunt specificate în mod explicit în manifest din Git (sau au fost specificate într-o versiune anterioară și sunt acum șterse).

Patch de îmbinare în 3 căi

idee centrală Patch de îmbinare în 3 căi: generăm un patch între ultima versiune aplicată a manifestului din Git și versiunea țintă a manifestului din Git, ținând cont de versiunea curentă a manifestului din clusterul care rulează. Patch-ul rezultat trebuie să respecte regula resurselor sincronizate:

  • câmpurile noi adăugate la versiunea țintă sunt adăugate folosind un patch;
  • câmpurile existente anterior în ultima versiune aplicată și care nu există în versiunea țintă sunt resetate folosind un patch;
  • câmpurile din versiunea curentă a obiectului care diferă de versiunea țintă a manifestului sunt actualizate folosind patch-ul.

Pe acest principiu generează patch-uri kubectl apply:

  • ultima versiune aplicată a manifestului este stocată în adnotarea obiectului însuși,
  • țintă - luat din fișierul YAML specificat,
  • cel actual este dintr-un cluster care rulează.

Acum că am rezolvat teoria, este timpul să vă spunem ce am făcut în werf.

Aplicarea modificărilor la werf

Anterior, werf, ca Helm 2, folosea corecții de îmbinare în două direcții.

Patch de reparare

Pentru a trece la un nou tip de patch-uri - 3-way-merge - primul pas am introdus așa-numitul repara petice.

La implementare, se folosește un patch standard de îmbinare în două direcții, dar werf generează suplimentar un patch care ar sincroniza starea reală a resursei cu ceea ce este scris în Git (un astfel de patch este creat folosind aceeași regulă de resurse sincronizate descrisă mai sus) .

Dacă are loc o desincronizare, la sfârșitul implementării utilizatorul primește un AVERTISMENT cu un mesaj corespunzător și un patch care trebuie aplicat pentru a aduce resursa într-o formă sincronizată. Acest patch este înregistrat și într-o adnotare specială werf.io/repair-patch. Se presupune că mâinile utilizatorului însuși va aplica acest patch: werf nu îl va aplica deloc.

Generarea de patch-uri de reparare este o măsură temporară care vă permite să testați efectiv crearea de patch-uri pe baza principiului 3-way-merge, dar nu aplicați automat aceste patch-uri. Momentan, acest mod de operare este activat implicit.

Patch de îmbinare în trei căi numai pentru versiunile noi

Începând cu 1 decembrie 2019, încep versiunile beta și alfa ale werf implicit utilizați corecții de îmbinare în trei direcții pentru a aplica modificări numai noilor versiuni Helm lansate prin werf. Versiunile existente vor continua să folosească abordarea 3-way-merge + repair patchs.

Acest mod de operare poate fi activat explicit prin setare WERF_THREE_WAY_MERGE_MODE=onlyNewReleases acum.

Nota: caracteristica a apărut în werf de-a lungul mai multor versiuni: în canalul alfa a devenit gata cu versiune v1.0.5-alfa.19, iar în canalul beta - cu v1.0.4-beta.20.

Patch de îmbinare în trei căi pentru toate versiunile

Începând cu 15 decembrie 2019, versiunile beta și alfa ale werf încep să utilizeze în mod prestabilit corecții complete de îmbinare în trei direcții pentru a aplica modificări tuturor versiunilor.

Acest mod de operare poate fi activat explicit prin setare WERF_THREE_WAY_MERGE_MODE=enabled acum.

Ce să faci cu scalarea automată a resurselor?

Există 2 tipuri de autoscaling în Kubernetes: HPA (orizontală) și VPA (verticală).

Orizontală selectează automat numărul de replici, verticală - numărul de resurse. Atât numărul de replici, cât și cerințele de resurse sunt specificate în manifestul resurselor (consultați Manifestul resurselor). spec.replicas sau spec.containers[].resources.limits.cpu, spec.containers[].resources.limits.memory и alții).

Problemă: dacă un utilizator configurează o resursă într-o diagramă astfel încât să specifice anumite valori pentru resurse sau replici și autoscalers sunt activați pentru această resursă, atunci cu fiecare implementare werf va reseta aceste valori la ceea ce este scris în manifestul diagramei .

Există două soluții la problemă. Pentru început, cel mai bine este să evitați specificarea explicită a valorilor autoscalate în manifestul diagramei. Dacă această opțiune nu este potrivită dintr-un anumit motiv (de exemplu, pentru că este convenabil să setați limitele inițiale de resurse și numărul de replici din diagramă), atunci werf oferă următoarele adnotări:

  • werf.io/set-replicas-only-on-creation=true
  • werf.io/set-resources-only-on-creation=true

Dacă o astfel de adnotare este prezentă, werf nu va reseta valorile corespunzătoare pentru fiecare implementare, ci le va seta doar atunci când resursa este creată inițial.

Pentru mai multe detalii, consultați documentația proiectului pentru HPA и VPA.

Interziceți utilizarea patch-ului de îmbinare în trei căi

În prezent, utilizatorul poate interzice utilizarea de noi patch-uri în werf folosind o variabilă de mediu WERF_THREE_WAY_MERGE_MODE=disabled. Totuși, începând De la 1 martie 2020, această interdicție nu se va mai aplica. și va fi posibil să utilizați doar patch-uri de îmbinare în trei căi.

Adoptarea resurselor în werf

Stăpânirea metodei de aplicare a modificărilor cu corecții de îmbinare în trei direcții ne-a permis să implementăm imediat o astfel de caracteristică precum adoptarea resurselor existente în cluster în versiunea Helm.

Helm 2 are o problemă: nu puteți adăuga o resursă la manifestele grafice care există deja în cluster fără a recrea această resursă de la zero (vezi. #6031, #3275). L-am învățat pe werf să accepte resursele existente pentru eliberare. Pentru a face acest lucru, trebuie să instalați o adnotare pe versiunea curentă a resursei din clusterul care rulează (de exemplu, folosind kubectl edit):

"werf.io/allow-adoption-by-release": RELEASE_NAME

Acum resursa trebuie descrisă în diagramă și data viitoare când werf implementează o versiune cu numele corespunzător, resursa existentă va fi acceptată în această ediție și va rămâne sub controlul acesteia. Mai mult, în procesul de acceptare a unei resurse pentru lansare, werf va aduce starea curentă a resursei din clusterul care rulează în starea descrisă în diagramă, folosind aceleași corecții de îmbinare în trei căi și regula resurselor sincronizate.

Nota: setare WERF_THREE_WAY_MERGE_MODE nu afectează adoptarea resurselor - în cazul adoptării, se folosește întotdeauna un patch de îmbinare în trei căi.

Detalii - in documentație.

Concluzii și planuri de viitor

Sper că după acest articol a devenit mai clar ce sunt patch-urile 3-way-merge și de ce au venit la ele. Din punct de vedere practic al dezvoltării proiectului werf, implementarea lor a fost un alt pas către îmbunătățirea implementării Helm-like. Acum puteți uita de problemele legate de sincronizarea configurației, care au apărut adesea atunci când utilizați Helm 2. În același timp, la versiunea Helm a fost adăugată o nouă caracteristică utilă de adoptare a resurselor Kubernetes deja descărcate.

Există încă unele probleme și provocări cu implementările tip Helm, cum ar fi utilizarea șabloanelor Go, pe care le vom continua să le abordăm.

Informații despre metodele de actualizare și adoptare a resurselor pot fi găsite și la această pagină de documentație.

Cârma 3

Demn de remarcat special eliberată chiar zilele trecute o nouă versiune majoră a Helm - v3 - care folosește și patch-uri de îmbinare în trei direcții și scapă de Tiller. Noua versiune de Helm necesită migraţiile instalațiile existente pentru a le converti în noua versiune format de stocare.

Werf, la rândul său, a scăpat în prezent de utilizarea Tiller, a trecut la 3-way-merge și a adăugat mult mai mult, rămânând în același timp compatibil cu instalările Helm 2 existente (nu trebuie executate scripturi de migrare). Prin urmare, până când werf trece la Helm 3, utilizatorii werf nu pierd principalele avantaje ale Helm 3 față de Helm 2 (le are și werf).

Cu toate acestea, trecerea werf la baza de cod Helm 3 este inevitabilă și se va întâmpla în viitorul apropiat. Probabil că acesta va fi werf 1.1 sau werf 1.2 (în prezent, versiunea principală a werf este 1.0; pentru mai multe informații despre dispozitivul de versiune werf, consultați aici). În acest timp, Helm 3 va avea timp să se stabilizeze.

PS

Citește și pe blogul nostru:

Sursa: www.habr.com

Adauga un comentariu