Pentru Kubernetes există mai multe opțiuni pentru actualizarea resurselor: apply, edit, patch și replace. Există confuzie cu privire la ceea ce face fiecare și când să le aplici. Să le analizăm.

Dacă fraza "kubernetes apply vs replace", găsește , care nu este corect. "kubernetes apply vs patch" primul rezultat — documentația pentru kubectl patch, care nu include o comparație aplicare și patch. În acest articol vor fi prezentate diverse opțiuni, precum și utilizarea corectă a fiecăreia dintre ele.
Pe parcursul ciclului de viață al unei resurse Kubernetes (serviciu, deployment, ingress etc.) este necesar uneori să schimbați, să adăugați sau să eliminați anumite proprietăți ale acestei resurse. De exemplu, să adăugați un comentariu, să creșteți sau să diminuați numărul de replici.
Kubernetes CLI
Dacă lucrați deja cu clustere Kubernetes prin CLI, sunteți deja familiarizați cu aplicare și edit. Comanda aplicare citește specificația resursei dintr-un fișier și efectuează un "upsert" în clusterul Kubernetes, adică creează resursa dacă nu există și o actualizează dacă există. Comanda edit citește resursa prin API, după care scrie specificația resursei într-un fișier local, care este deschis apoi într-un editor de text. După ce editați și salvați fișierul, kubectl va trimite modificările realizate înapoi prin API, care se va asigura că aceste modificări sunt aplicate resursei.
Nu toată lumea cunoaște comenzile patch și replace. Comanda patch permite modificarea unei părți din specificația resursei, oferind doar partea modificată în linia de comandă. Comanda replace funcționează la fel ca și edit, dar totul trebuie să fie realizat manual: este necesar să descărcați versiunea curentă a specificației resursei, de exemplu, folosind kubectl get -o yaml, să o editați, apoi să folosiți replace pentru a actualiza resursa conform specificației modificate. Comanda replace nu va funcționa dacă între citirea și înlocuirea resursei au avut loc schimbări.
Kubernetes API
Probabil că sunteți familiarizat cu metodele CoreV1().Pods().Update(), replaceNamespacedService sau patch_namespaced_deployment, dacă lucrați cu clustere prin folosind un anumit limbaj de programare. Biblioteca gestionează aceste metode prin intermediul cererilor HTTP, folosind metodele PUT și PATCH. Trebuie menționat că update și replace utilizată PUT, iar patch, oricât de banal ar părea, folosește PATCH.
Merită menționat că kubectl funcționează de asemenea cu clustere prin API. Cu alte cuvinte, kubectl– este un wrapper peste biblioteca client pentru limbajul Go, care oferă în mare măsură posibilitatea de a oferi subcomenzi într-o formă mai compactă și lizibilă, pe lângă capacitățile standard ale API-ului. De exemplu, așa cum ați observat deja, metoda aplicare nu a fost menționată mai sus în paragraful anterior. În prezent (mai 2020, nota traducătorului) toată logica kubectl apply, adică crearea de resurse inexistente și actualizarea celor existente, funcționează complet pe partea de cod kubectl. Se depun eforturi aplicare pe partea de API, dar aceasta este încă în etapa de testare beta. Voi detalia mai jos.
Patch implicit
Se aplică cel mai bine patch, dacă doriți să actualizați o resursă. Așa funcționează atât bibliotecile client deasupra API-ului Kubernetes, cât și kubectl (nu e de mirare, fiindcă este un wrapper al bibliotecii client, nota traducătorului).
A lucra strategic
Toate comenzile kubectl aplicare, edit și patch utilizează metoda PATCH în cererile HTTP pentru a actualiza o resursă existentă. Dacă ne uităm mai în detaliu la implementarea comenzilor, se folosește întotdeauna abordarea pentru actualizarea resurselor, deși comanda patch poate folosi și alte abordări (mai multe despre asta mai jos). Abordarea strategic-merge patching încearcă să "facă totul corect" atunci când combină specificația oferită cu specificația existentă. Mai exact, încearcă să combine atât obiecte, cât și matrice, ceea ce înseamnă că modificările sunt, de obicei, aditive. De exemplu, lansarea comenzii patch cu o nouă variabilă de mediu în specificația containerului pod, va adăuga această variabilă de mediu la variabilele de mediu existente, fără a le suprascrie. Pentru a șterge folosind această abordare, trebuie să setați forțat valoarea parametrului la null în specificația furnizată. Ce comenzi kubectl pentru actualizare sunt cele mai potrivite?
Dacă creați și gestionați resursele dvs. cu ajutorul kubectl apply, este întotdeauna mai bine să folosiți kubectl apply, astfel încât kubectl a putut gestiona configurația și să urmărească în mod corect modificările solicitate de la o aplicație la alta. Avantajul de a folosi întotdeauna aplicare constă în faptul că urmărește specificația aplicată anterior, permițându-i să știe când proprietățile specificației și elementele din matrice sunt eliminate în mod explicit. Acest lucru permite utilizarea aplicare pentru a elimina proprietăți și elemente dintr-un array, în timp ce un simplu fusionare strategică nu va funcționa. Comenzile edit și patch nu actualizează notele, care kubectl apply sunt folosite pentru a urmări modificările, așadar orice modificări care sunt urmărite și efectuate prin API-ul Kubernetes, dar realizate prin comenzi edit și patch, sunt invizibile pentru comenzile ulterioare aplicare, adică aplicare nu le elimină, chiar dacă acestea nu apar în specificația de intrare pentru aplicare (Documentația menționează că edit și patch efectuează actualizări ale notelor utilizate aplicare, dar în practică – nu).
Dacă nu folosiți comanda aplicare, se poate folosi ca edit, sau patch, alegând comanda care se potrivește cel mai bine cu modificarea efectuată. În cazul adăugării și modificării proprietăților specificației, ambele abordări sunt asemănătoare. Când se elimină proprietăți din specificație sau elemente dintr-un array edit comportă ca o execuție unică aplicare, inclusiv urmărește care a fost specificația înainte și după editare, astfel încât se pot elimina explicit proprietățile și elementele din array din resursă. Trebuie să se stabilească explicit valoarea proprietății ca null în specificație pentru patch, pentru a o elimina din resursă. Eliminarea unui element din array folosind strategic-merge patching este mai complexă, deoarece necesită utilizarea directivelor de fuziune. Consultați alte abordări de actualizare de mai jos pentru a alege alternative mai acceptabile.
Pentru a implementa în biblioteca client metodele de actualizare care se comportă asemănător comenzilor de mai sus kubectl, este necesar să se specificație content-type în application/strategic-merge-patch+json. Dacă doriți să eliminați proprietăți din specificație, trebuie să le stabiliți explicit valorile ca null similar cu kubectl patch. Dacă este necesar să eliminați elemente din array, ar trebui să includeți directivele de fuziune în specificația de actualizare sau să folosiți o altă abordare pentru actualizări.
Alte abordări de actualizări
Kubernetes suportă două alte abordări pentru actualizări: și . Abordarea JSON merge patch acceptă o specificație parțială Kubernetes ca date de intrare și suportă combinarea obiectelor similar cu abordarea strategic-merge patching. Diferența dintre ele constă în faptul că suportă doar înlocuirea array-urilor, inclusiv array-ul containerelor în specificația pod-ului. Aceasta înseamnă că, atunci când folosești JSON merge patch, trebuie să oferi specificații complete pentru toate containerele în cazul în care se modifică vreo proprietate a oricărui container. Prin urmare, această abordare este utilă pentru eliminarea elementelor din array în specificație. În linia de comandă poți alege JSON merge patch, utilizând kubectl patch --type=merge. Când lucrezi cu API Kubernetes, ar trebui să folosești metoda de solicitare PATCH și setarea content-type în application/merge-patch+json.
Abordarea JSON patch, în loc să ofere o specificație parțială a resursei, utilizează furnizarea modificărilor pe care dorești să le faci la resursă sub formă de array, în care fiecare element al array-ului reprezintă o descriere a modificării aduse resursei. Această abordare este un mod mai flexibil și mai puternic de a exprima modificările efectuate, dar prin faptul că lista modificărilor vine într-un format separat, non-Kubernetes, în loc să trimită o specificație parțială a resursei. În kubectl poți alege JSON patch, utilizând kubectl patch --type=json. Atunci când folosești API Kubernetes, această abordare funcționează utilizând metoda de solicitare PATCH și setarea content-type în application/json-patch+json.
Ai nevoie de siguranță — folosim replace
În unele cazuri, ai nevoie de siguranță că resursa nu va suferi modificări între momentul citirii resursei și actualizarea acesteia. Cu alte cuvinte, trebuie să te asiguri că toate modificările vor fi atomice. În acest caz, pentru actualizarea resurselor ar trebui să utilizezi replace. De exemplu, dacă există un ConfigMap cu un contor, actualizat de mai multe surse, trebuie să fii sigur că două surse nu vor actualiza contorul simultan, ceea ce ar duce la pierderea actualizării. Pentru a demonstra, imaginează-ți o secvență de evenimente folosind abordarea patch:
- A și B obțin starea actuală a resursei din API
- Fiecare dintre ei actualizează local specificația, crescând contorul cu o unitate și adăugând "A" sau "B" respectiv în notația "updated-by"
- A actualizează resursa puțin mai repede
- B actualizează resursa
În urma actualizării A, s-a pierdut. Ultima operațiune patch câștigă, contorul crește cu o unitate în loc de două, iar valoarea notei "updated-by" se încheie cu "B" și nu conține "A". Să comparăm cele spuse mai sus cu ceea ce se întâmplă atunci când actualizările sunt efectuate folosind abordarea replace:
- A și B obțin starea actuală a resursei din API
- Fiecare dintre ei actualizează local specificația, crescând contorul cu o unitate și adăugând "A" sau "B" respectiv în notația "updated-by"
- A actualizează resursa puțin mai repede
- B încearcă să actualizeze resursa, dar actualizarea este respinsă de API, deoarece versiunea resursei din specificație
replacenu se potrivește cu versiunea curentă a resursei în Kubernetes, deoarece versiunea resursei a fost crescută în timpul operațiunii de înlocuire din partea A.
În cazul de mai sus, B va trebui să reobțină resursa, să facă modificările necesare, să o aducă la noul stadiu și să încerce din nou să facă replace. Drept urmare, contorul va crește cu două, iar nota "updated-by" va conține "AB" la final.
Exemplul de mai sus implică faptul că în timpul replace se efectuează înlocuirea completă a întregii resurse. Specificația utilizată pentru replace, trebuie să fie completă, nu parțială sau în fragmente ca în aplicare, ci completă, inclusiv adăugarea resourceVersion în metadatele specificației. Dacă nu ați inclus resourceVersion sau versiunea pe care ați furnizat-o nu este actuală, înlocuirea va fi respinsă. Astfel, cea mai bună abordare de utilizat replace este să citiți resursa, să o actualizați și să o înlocuiți imediat. Folosind kubectl, aceasta poate arăta astfel:
$ kubectl get deployment my-deployment -o json
| jq '.spec.template.spec.containers[0].env[1].value = "new value"'
| kubectl replace -f -Merită menționat că următoarele două comenzi, executate succesiv, se vor finaliza cu succes, deoarece deployment.yaml nu conține proprietatea .metadata.resourceVersion
$ kubectl create -f deployment.yaml
$ kubectl replace -f deployment.yamlSe pare că acest lucru contrazice ceea ce s-a spus mai sus, adică "adăugarea resourceVersion în metadatele specificației". Este greșit să afirmi asta? Nu, nu este, deoarece dacă kubectl observă că nu ați specificat resourceVersion, el o va citi din resursă și o va adăuga în specificația pe care ați furnizat-o și abia apoi va executa replace. Deoarece aceasta este potențial periculoasă, dacă te bazezi pe atomicitate, magia aceasta funcționează complet pe partea kubectl, nu ar trebui să te bazezi pe ea atunci când utilizezi biblioteci client care interacționează cu API. În acest caz, va trebui să citești specificația curentă a resursei, să o actualizezi și apoi să efectuezi PUT cererea.
Nu se poate face patch - se face replace
Uneori este necesar să faci modificări care nu pot fi gestionate de API. În aceste cazuri, poți forța înlocuirea resursei, ștergând-o și recreând-o. Acest lucru se face cu ajutorul kubectl replace --force. Rularea comenzii șterge imediat resursele și apoi le recreează conform specificației furnizate. API-ul nu dispune de un handler "forțează înlocuirea", iar pentru a face acest lucru prin API, trebuie să efectuezi două operații. Mai întâi, trebuie să ștergi resursa, setând gracePeriodSeconds la zero (0) și propagationPolicy la “Background”, după care trebuie să recreezi această resursă conform specificației dorite.
Atenție: această abordare este potențial periculoasă, deoarece poate duce la un stadiu nedeterminat.
Apply pe partea server-ului
După cum s-a menționat mai sus, dezvoltatorii Kubernetes lucrează la implementarea logicii aplicare din kubectl în API-ul Kubernetes. Logica aplicare este disponibilă în Kubernetes 1.18 prin kubectl apply --server-side sau prin API, folosind metoda PATCH de content-type application/apply-patch+YAML.
Notă: JSON este, de asemenea, un YAML valid, astfel că poți trimite specificația sub formă de JSON, chiar dacă
content-typevaapplication/apply-patch+yaml.
În plus, logica kubectl devine disponibilă pentru toți prin API, aplicare pe partea server-ului, urmărind respondenții pentru câmpurile din specificație, astfel permițând un acces multiplu sigur pentru editarea ei fără conflicte. Cu alte cuvinte, dacă aplicare pe partea server-ului va obține o distribuție mai largă, va apărea o interfață de gestionare a resurselor, sigură și universală, pentru diferite clienți, cum ar fi kubectl, Pulumi sau Terraform, GitOps, precum și scripturi personalizate care utilizează biblioteci client.
Concluzii
Sper că acest scurt rezumat al diferitelor modalități de actualizare a resurselor în clustere a fost util pentru tine. Este important să știi că nu există un simplu apply versus replace, deoarece poți actualiza o resursă prin apply, edit, patch sau replace. Fiecare abordare are, în principiu, domeniul său de aplicare. Pentru modificări atomice, preferi replace, altfel ar trebui să folosești strategic-merge patch prin apply. În ultima instanță, sper că ai înțeles că nu trebuie să te bazezi pe Google sau StackOverflow când cauți "kubernetes apply vs replace". Cel puțin până când acest articol nu va înlocui răspunsul curent.

Sursa: habr.com
