Dogodilo se ono što smo (i ne samo mi) dugo čekali: , naš Open Source alat za izgradnju aplikacija i njihovu isporuku u Kubernetes, sada podržava primjenu promjena korištenjem 3-way-merge patch-eva! Osim toga, uvedena je mogućnost usvajanja postojećih K8s resursa u Helm relize bez ponovnog stvaranja tih resursa.

Ukratko, postavljamo WERF_THREE_WAY_MERGE=enabled — dobivamo isporuku „kao u kubectl apply“, kompatibilnu s postojećim instalacijama na Helm 2, pa čak i malo više.
Ali hajde da počnemo s teorijom: što su zapravo 3-way-merge patch-evi, kako su ljudi došli do pristupa njihovoj generaciji i zašto su važni u CI/CD procesima s infrastrukturom temeljenom na Kubernetesu? A nakon toga — pogledat ćemo što zapravo predstavlja 3-way-merge u werfu, koji se načini koriste prema zadanim postavkama i kako tim upravljati.
Što je 3-way-merge patch?
Dakle, započnimo s problematikom isporuke resursa opisanih u YAML manifestima, u Kubernetes.
Za rad s resursima Kubernetes API nudi sljedeće osnovne operacije: create, patch, replace i delete. Pretpostavlja se da je njihova svrha olakšati kontinuiranu isporuku resursa u klaster. Kako?
Imperativne komande kubectl
Prvi pristup upravljanju objektima u Kubernetesu — korištenje imperativnih komandi kubectl za kreiranje, promjenu i brisanje tih objekata. Drugim riječima:
- komandom
kubectl runmože pokrenuti Deployment ili Job:kubectl run --generator=deployment/apps.v1 IME_DEPLOYMENT-a --image=IMAGE - komandom
kubectl scale— promijeniti broj replik:kubectl scale --replicas=3 deployment/mysql - itd.
Ovaj pristup može se učiniti jednostavnim na prvi pogled. Međutim, postoje problemi:
- Teško je automatizirati.
- Kako odraziti konfiguraciju u Git? Kako napraviti pregled promjena, koje se događaju s klasterom?
- Kako osigurati reproducibilnost konfiguracije prilikom ponovnog pokretanja?
- …
Jasno je da se takav pristup loše usklađuje s pohranjivanjem zajedno s kodom aplikacije i infrastrukturom kao kodom (IaC; ili čak kao modernija opcija, koja postaje sve popularnija u Kubernetes ekosustavu). Stoga daljnji razvoj ovih komandi u kubectl nije se dogodio.
Operacije create, get, replace i delete
S osnovnim kreiranjem sve je jednostavno: šaljemo manifest u operaciju create kube API-ju i resurs je kreiran. YAML prikaz manifesta može se pohraniti u Git, a za kreiranje — koristiti komandu kubectl create -f manifest.yaml.
S brisanje također je jednostavno: umetnemo isti manifest.yaml iz Gita u komandu kubectl delete -f manifest.yaml.
Operacija replace omogućuje potpuno zamjenu konfiguracije resursa s novom, bez ponovnog stvaranja resursa. To znači da je prije nego što se izvrši promjena resursa logično zatražiti trenutnu verziju operacijom preuzmi, izmijeniti je i ažurirati operacijom replace. U kube apiserveru ugrađen je i, ako se nakon operacije preuzmi objekt promijenio, tada operacija replace neće proći.
Kako bismo pohranili konfiguraciju u Git i ažurirali je pomoću replace, potrebno je izvršiti operaciju preuzmi, spojiti konfiguraciju iz Gita s onim što smo dobili, i izvršiti replace. Standardno, kubectl omogućuje samo korištenje naredbe kubectl replace -f manifest.yaml, gdje manifest.yaml — već potpuno pripremljen (u našem slučaju — spojeni) manifest koji treba instalirati. Dakle, korisniku je potrebno implementirati spajanje manifestacija, a to nije trivijalno…
Također valja napomenuti da, iako manifest.yaml se pohranjuje u Git, ne možemo unaprijed znati je li potrebno stvoriti objekt ili ga ažurirati — to bi trebao učiniti korisnički softver.
Ukratko: možemo li izgraditi kontinuirani isporučivanje samo pomoću create, replace i delete, osiguravajući pohranu konfiguracije infrastrukture u Gitu zajedno s kodom i prikladnim CI/CD-om?
U načelu, možemo… Za to bit će potrebno implementirati operaciju spajanja manifestacija i neku vrstu omotača koji:
- provjerava postojanje objekta u klasteru,
- izvodi inicijalno stvaranje resursa,
- ažurira ili briše ga.
Prilikom ažuriranja treba uzeti u obzir da resurs je mogao promijeniti od posljednjeg preuzmi i automatski obraditi slučaj optimističkog zaključavanja — izvršavati ponovne pokušaje ažuriranja.
Međutim, zašto izmišljati kotač kada kube-apiserver nudi drugi način ažuriranja resursa: operaciju patch, koja preuzima dio opisanih problema od korisnika?
Patch
Evo nas do zakrpa.
Zakrpe su glavni način primjene promjena postojećim objektima u Kubernetesu. Operacija patch funkcionira tako da:
- korisniku kube-apiservera treba poslati zakrpu u JSON formatu i navesti objekt,
- a apiserver će sam razumjeti trenutno stanje objekta i dovesti ga u željeni oblik.
Optimističko zaključavanje u ovom slučaju nije potrebno. Ova operacija je deklarativnija u usporedbi s replace, iako se isprva može činiti obrnuto.
Dakle:
- uz pomoć operacije
createstvaramo objekt prema manifestu iz Gita, - pomoću
izbriši— brišemo ga ako objekt više nije potreban, - pomoću
patch— mijenjamo objekt, dovodeći ga u oblik opisano u Gitu.
Međutim, da biste to učinili, potrebno je stvoriti ispravan patch!
Kako rade patch-evi u Helm 2: 2-way-merge
Pri prvoj instalaciji Helm izdanja vrši se operacija create za resurse charta.
Pri ažuriranju izdanja Helm za svaki resurs:
- uzima patch između verzije resursa iz prethodnog charta i trenutne verzije charta,
- primjenjuje taj patch.
Takav patch ćemo zvati 2-way-merge patch, jer se u njegovom stvaranju sudjeluje 2 manifesta:
- manifest resursa iz prethodnog izdanja,
- manifest resursa iz trenutnog resursa.
Pri brisanju operacija izbriši u kube apiserveru se poziva za resurse koji su bili deklarirani u prethodnom izdanju, ali nisu deklarirani u trenutnom.
Pristup s 2-way merge patch ima problem: dovodi do nesinkronizacije stvarnog stanja resursa u klasteru i manifesta u Gitu.
Ilustracija problema na primjeru
- U Gitu, u chartu se čuva manifest, u kojem polje
imageza Deployment ima vrijednostubuntu:18.04. - Korisnik je preko
kubectl editpromijenio vrijednost tog polja naubuntu:19.04. - Pri ponovnoj depoymentu charta Helma ne generira patch, jer su polja
imageu prethodnoj verziji izdanja i trenutnom chartu ista. - Nakon ponovnog deploya
imageostajeubuntu:19.04, iako u chartu pišeubuntu:18.04.
Dobili smo nesinkronizaciju i izgubili deklarativnost.
Što je to sinkronizirani resurs?
Općenito, potpuno usklađenost manifesta resursa u aktivnom klasteru i manifesta iz Gita nije moguće postići. Jer u stvarnom manifestu mogu biti sistemske anotacije/oznake, dodatni kontejneri i drugi podaci koji se dinamički dodaju i uklanjaju iz resursa od strane kontrolera. Te podatke ne možemo i ne želimo držati u Gitu. Međutim, želimo da prilikom implementacije ta polja koja smo jasno naveli u Gitu imaju odgovarajuće vrijednosti.
Ispada da je to opće pravilo sinkroniziranog resursa: prilikom implementacije resursa mogu se mijenjati ili brisati samo ona polja koja su jasno navedena u manifestu iz Gita (ili su bila navedena u prethodnoj verziji, a sada su uklonjena).
3-way-merge patch
Osnovna ideja : generira patch između posljednje primijenjene verzije manifesta iz Gita i ciljne verzije manifesta iz Gita uzimajući u obzir trenutnu verziju manifesta iz aktivnog klastera. Konačni patch mora odgovarati pravilu sinkroniziranog resursa:
- nova polja, dodana u ciljnu verziju, dodaju se pomoću patch-a;
- polja koja su postojala u posljednjoj primijenjenoj verziji i ne postoje u ciljnome - poništavaju se pomoću zakrpe;
- polja u trenutnoj verziji objekta koja se razlikuju od ciljne verzije manifesta - ažuriraju se pomoću zakrpe.
Na upravo takvom principu generiraju se zakrpe kubectl apply:
- posljednja primijenjena verzija manifesta čuva se u anotaciji samog objekta,
- ciljna - preuzima se iz navedene YAML datoteke,
- trenutna - iz radnog klastera.
Sada kada smo se pozabavili teorijom, vrijeme je da kažemo što smo napravili u werf.
Primjena promjena u werf
Ranije je werf, kao i Helm 2, koristio 2-way-merge zakrpe.
Repair zakrpa
Kako bismo prešli na novi tip zakrpa - 3-way-merge, prvi korak koji smo poduzeli bio je uvođenje takozvanih repair zakrpa.
Prilikom implementacije koristi se standardna 2-way-merge zakrpa, no werf dodatno generira takvu zakrpu koja bi sinkronizirala stvarno stanje resursa s onim što je napisano u Gitu (stvara se ta zakrpa koristeći isto pravilo sinkroniziranog resursa opisano iznad).
U slučaju nesinkronizacije, na kraju implementacije korisnik dobiva WARNING s odgovarajućom porukom i zakrpom koju treba primijeniti kako bi se resurs doveo u sinkronizirani položaj. Također, ova zakrpa se zapisuje u posebnu anotaciju werf.io/repair-patch. Predviđa se da korisnik ručno samo primijeni tu zakrpu: werf je neće primijeniti principijelno.
Generiranje repair zakrpa je privremena mjera koja omogućava isprobavanje stvaranja zakrpa prema principu 3-way-merge, ali automatski te zakrpe ne primjenjuju. U ovom trenutku ovaj način rada je uključен prema zadanim postavkama.
3-way-merge zakrpa samo za nove izdanja
Počevši od 1. prosinca 2019. beta i alpha verzije werf počinju po defaultu koristiti potpune 3-way-merge zakrpe za primjenu promjena samo za nove Helm izdanja koja se implementiraju putem werf. Već postojeća izdanja nastavit će koristiti pristup s 2-way-merge + repair zakrpama.
Ovaj način rada može se uključiti izrijekom postavkom WERF_THREE_WAY_MERGE_MODE=onlyNewReleases već sada.
Napomena: funkcija se pojavila u werf-u tijekom nekoliko izdanja: u alpha kanalu postala je gotova s verzijom , a u beta kanalu - od .
3-way-merge zakrpa za sve verzije
Od 15. prosinca 2019. beta i alpha verzije werf po defaultu koriste potpune 3-way-merge patch-eve za primjenu promjena na svim izdanjima.
Ovaj način rada može se uključiti izrijekom postavkom WERF_THREE_WAY_MERGE_MODE=enabled već sada.
Kako se nositi s automatskim skaliranjem resursa?
U Kubernetesu postoje 2 tipa automatskog skaliranja: HPA (horizontalno) i VPA (vertikalno).
Horizontalno automatski određuje broj replika, dok vertikalno određuje količinu resursa. I broj replika i zahtjevi za resursima su navedeni u manifestu resursa (vidi. spec.replicas ili spec.containers[].resources.limits.cpu, spec.containers[].resources.limits.memory i ).
Problem: ako korisnik konfiguriše resurs u chartu tako da ima određene vrijednosti za resurse ili replike i za taj resurs su uključeni automatski skaleri, tada će werf pri svakoj implementaciji resetirati ove vrijednosti na ono što je zapisano u manifestu charta.
Postoje dva rješenja za problem. Prvo, najbolje je odustati od eksplicitnog navođenja automatski skaliranih vrijednosti u manifestu charta. Ako ova opcija iz nekog razloga nije pogodna (na primjer, jer je zgodno postaviti početna ograničenja resursa i broj replika unutar charta), werf nudi sljedeće anotacije:
-
werf.io/set-replicas-only-on-creation=true -
werf.io/set-resources-only-on-creation=true
U prisustvu takve anotacije, werf neće resetirati odgovarajuće vrijednosti pri svakoj implementaciji, već će ih postaviti samo prilikom prvog kreiranja resursa.
Više informacija — vidi u dokumentaciji projekta o i .
Zabraniti korištenje 3-way-merge patch-a
Korisnik može trenutno zabraniti korištenje novih patch-eva u werf pomoću varijable okruženja WERF_THREE_WAY_MERGE_MODE=disabled. Međutim, od 1. ožujka 2020. ovo pravilo prestaje vrijediti i bit će moguće samo korištenje 3-way-merge patch-eva.
Usvajanje resursa u werf
Uvođenje metode primjene promjena 3-way-merge patch-evima omogućilo nam je odmah implementirati funkciju poput usvajanja postojećih resursa u klasteru u Helm izdanju.
Helm 2 ima problem: nije moguće dodati resurs koji već postoji u klasteru u manifeste charta bez ponovnog stvaranja tog resursa (vidi. , ). Naučili smo werf da prihvati postojeće resurse u izdanju. Za to je potrebno na trenutnu verziju resursa iz aktivnog klastera postaviti anotaciju (na primjer, pomoću kubectl edit):
"werf.io/allow-adoption-by-release": RELEASE_NAMESada resurs treba opisati u chartu, a prilikom sljedeće implementacije werf-a s odgovarajućim imenom, postojeći resurs će biti prihvaćen u tu verziju i ostat će pod njegovim upravljanjem. Štoviše, tijekom prihvaćanja resursa u verziju, werf će dovesti trenutno stanje resursa iz radnog klastera u stanje opisano u chartu, koristeći iste 3-way-merge zakrpe i pravilo sinkroniziranog resursa.
Napomena: konfiguracija WERF_THREE_WAY_MERGE_MODE ne utječe na usvajanje resursa — u slučaju usvajanja uvijek se koristi 3-way-merge zakrpa.
Detalji — u .
Zaključci i daljnji planovi
Nadam se da je nakon ovog članka jasnije što su 3-way-merge zakrpe i zašto su uvedene. Iz praktične perspektive razvoja projekta werf, njihova implementacija bila je još jedan korak ka poboljšanju Helm-slične implementacije. Sada možemo zaboraviti na probleme sa sinkronizacijom konfiguracija koji su se često javljali pri korištenju Helm 2. Također, dodana je nova korisna značajka usvajanja već preuzetih Kubernetes resursa u Helm verziju.
U Helm-sličnim implementacijama i dalje ostaju neki problemi i poteškoće, poput korištenja Go predložaka, a nastavit ćemo ih rješavati.
Informacije o metodama ažuriranja resursa i usvajanju također se mogu pronaći na .
Helm 3
Posebnu pažnju zaslužuje nova glavna verzija Helma — v3, — koja također koristi 3-way-merge zakrpe i uklanja Tiller. Nova verzija Helma zahtijeva već postojećih instalacija kako bi ih konvertirala u novi format pohrane verzija.
Werf je sa svoje strane već uklonio korištenje Tillera, prešao na 3-way-merge i dodao , pri čemu je ostao kompatibilan s već postojećim instalacijama na Helmu 2 (nema potrebe za izvođenjem skripti za migraciju). Stoga, dok werf ne pređe na Helm 3, korisnici werfa ne gube osnovne prednosti Helma 3 u odnosu na Helm 2 (one su također prisutne u werfu).
Ipak, prebacivanje werfa na kodnu bazu Helma 3 neizbježno je i dogodit će se u bliskoj budućnosti. Pretpostavlja se da će to biti werf 1.1 ili werf 1.2 (trenutno je glavna verzija werfa — 1.0; više o strukturi verzioniranja werfa pogledajte ). U to vrijeme Helm 3 će se uspjeti stabilizirati.
P.S.
Pročitajte i na našem blogu:
- Ciklus bilješki o novinama u werf:
- «»;
- «»;
- «».
- «»;
- «»;
- «».
Izvor: habr.com
