3-weg samenvoeging naar werf: implementatie naar Kubernetes met Helm “op steroïden”

Waar wij (en niet alleen wij) lang op hebben gewacht, gebeurde: werf, ons Open Source-hulpprogramma voor het bouwen van applicaties en het leveren ervan aan Kubernetes, ondersteunt nu het toepassen van wijzigingen met behulp van 3-weg merge-patches! Daarnaast is het mogelijk om bestaande K8s-bronnen over te nemen in Helm-releases zonder deze bronnen opnieuw op te bouwen.

3-weg samenvoeging naar werf: implementatie naar Kubernetes met Helm “op steroïden”

Als het erg kort is, dan plaatsen we WERF_THREE_WAY_MERGE=enabled – we krijgen inzet “zoals in kubectl apply", compatibel met bestaande Helm 2-installaties en zelfs iets meer.

Maar laten we beginnen met de theorie: wat zijn 3-way-merge-patches precies, hoe kwamen mensen op de aanpak om deze te genereren, en waarom zijn ze belangrijk in CI/CD-processen met op Kubernetes gebaseerde infrastructuur? En laten we daarna eens kijken wat 3-way-merge in werf is, welke modi standaard worden gebruikt en hoe we dit kunnen beheren.

Wat is een 3-weg-merge-patch?

Laten we dus beginnen met de taak om de bronnen die worden beschreven in YAML-manifesten uit te rollen naar Kubernetes.

Om met bronnen te werken biedt de Kubernetes API de volgende basisbewerkingen: aanmaken, patchen, vervangen en verwijderen. Er wordt aangenomen dat het met hun hulp noodzakelijk is om een ​​gemakkelijke continue uitrol van middelen naar het cluster te construeren. Hoe?

kubectl imperatieve opdrachten

De eerste benadering voor het beheren van objecten in Kubernetes is het gebruik van kubectl imperatieve opdrachten om deze objecten te maken, wijzigen en verwijderen. Simpel gezegd:

  • team kubectl run u kunt Implementatie of Taak uitvoeren:
    kubectl run --generator=deployment/apps.v1 DEPLOYMENT_NAME --image=IMAGE
  • team kubectl scale — verander het aantal replica's:
    kubectl scale --replicas=3 deployment/mysql
  • etc.

Deze aanpak lijkt op het eerste gezicht misschien handig. Er zijn echter problemen:

  1. Het is moeilijk automatiseren.
  2. Als weerspiegelen de configuratie in Git? Hoe kunt u de wijzigingen bekijken die in het cluster plaatsvinden?
  3. Hoe te verstrekken reproduceerbaarheid configuraties bij opnieuw opstarten?
  4. ...

Het is duidelijk dat deze aanpak niet goed past bij het opslaan van applicaties en infrastructuur als code (IaC; of zelfs Gitops als een modernere optie, die aan populariteit wint in het Kubernetes-ecosysteem). Daarom hebben deze opdrachten geen verdere ontwikkeling ondergaan in kubectl.

Bewerkingen maken, ophalen, vervangen en verwijderen

Met primair schepping het is eenvoudig: stuur het manifest naar de operatie create kube API en de bron is gemaakt. De YAML-representatie van het manifest kan worden opgeslagen in Git en worden gemaakt met behulp van de opdracht kubectl create -f manifest.yaml.

С Verwijderen ook simpel: vervang hetzelfde manifest.yaml van Git naar team kubectl delete -f manifest.yaml.

Operatie replace Hiermee kunt u de resourceconfiguratie volledig vervangen door een nieuwe, zonder de resource opnieuw te maken. Dit betekent dat voordat u een wijziging aan een resource aanbrengt, het logisch is om met de bewerking de huidige versie op te vragen get, wijzig het en update het met de bewerking replace. kube apiserver is ingebouwd optimistische vergrendeling en, indien na een operatie get het object is gewijzigd, daarna de bewerking replace zal niet passeren.

Om de configuratie in Git op te slaan en bij te werken met behulp van vervangen, moet je de bewerking uitvoeren get, voeg de configuratie van Git samen met wat we hebben ontvangen en voer deze uit replace. Standaard kunt u met kubectl alleen de opdracht gebruiken kubectl replace -f manifest.yamlWaar manifest.yaml — een reeds volledig voorbereid (in ons geval samengevoegd) manifest dat moet worden geïnstalleerd. Het blijkt dat de gebruiker merge-manifesten moet implementeren, en dit is geen triviale zaak...

Het is ook vermeldenswaard dat hoewel manifest.yaml en is opgeslagen in Git, kunnen we van tevoren niet weten of het nodig is om een ​​object te maken of bij te werken - dit moet door gebruikerssoftware worden gedaan.

Totaal: kunnen we een continue uitrol bouwen alleen gebruik maken van create, vervangen en verwijderen, waarbij ervoor wordt gezorgd dat de infrastructuurconfiguratie samen met de code en handige CI/CD in Git wordt opgeslagen?

In principe kunnen we... Hiervoor u moet de samenvoegbewerking implementeren manifesten en een soort binding die:

  • controleert de aanwezigheid van een object in het cluster,
  • voert de initiële creatie van hulpbronnen uit,
  • updatet of verwijdert het.

Houd er bij het updaten rekening mee de bron kan veranderd zijn sinds laatst get en automatisch omgaan met het geval van optimistische vergrendeling - voer herhaalde updatepogingen uit.

Maar waarom zou u het wiel opnieuw uitvinden als kube-apiserver een andere manier biedt om bronnen bij te werken: de bewerking patch, wat de gebruiker van enkele van de beschreven problemen verlost?

Patch

Nu komen we bij de pleisters.

Patches zijn de belangrijkste manier om wijzigingen toe te passen op bestaande objecten in Kubernetes. Operatie patch het werkt als volgt:

  • de kube-apiserver-gebruiker moet een patch in JSON-vorm verzenden en het object specificeren,
  • en apiserver zelf zal de huidige status van het object afhandelen en het in de gewenste vorm brengen.

Optimistische vergrendeling is in dit geval niet vereist. Deze operatie is meer declaratief dan vervangen, hoewel het in eerste instantie misschien andersom lijkt.

dus:

  • gebruik maken van een operatie create we creëren een object volgens het manifest van Git,
  • via delete — verwijderen als het object niet langer nodig is,
  • via patch — we veranderen het object en brengen het naar de vorm beschreven in Git.

Om dit te doen, moet u echter creëren juiste pleister!

Hoe patches werken in Helm 2: 2-way-merge

Wanneer u een release voor het eerst installeert, voert Helm de bewerking uit create voor kaartbronnen.

Wanneer u een Helm-release voor elke resource bijwerkt:

  • houdt rekening met de patch tussen de bronversie van de vorige kaart en de huidige kaartversie,
  • past deze patch toe.

We zullen deze patch noemen 2-weg samenvoegpatch, omdat er bij de totstandkoming ervan twee manifesten betrokken zijn:

  • resourcemanifest uit de vorige release,
  • resourcemanifest van de huidige resource.

Bij het verwijderen delete in kube apiserver wordt aangeroepen voor bronnen die in de vorige release zijn gedeclareerd, maar niet in de huidige release.

De tweezijdige merge-patchbenadering heeft een probleem: het leidt tot niet synchroon met de werkelijke status van de bron in het cluster en het manifest in Git.

Illustratie van het probleem met een voorbeeld

  • In Git slaat een diagram een ​​manifest op waarin het veld image Implementatie is belangrijk ubuntu:18.04.
  • Gebruiker via kubectl edit veranderde de waarde van dit veld in ubuntu:19.04.
  • Bij het opnieuw implementeren van het Helm-diagram genereert geen patch, omdat het veld image in de vorige versie van de release en in de huidige grafiek zijn hetzelfde.
  • Na herplaatsing image stoffelijk overschot ubuntu:19.04, hoewel de grafiek dat zegt ubuntu:18.04.

We kregen desynchronisatie en verloren declarativiteit.

Wat is een gesynchroniseerde bron?

Algemeen gesproken vol Het is onmogelijk om een ​​overeenkomst te verkrijgen tussen het bronmanifest in een actief cluster en het manifest uit Git. Omdat er in een echt manifest serviceannotaties/labels, extra containers en andere gegevens kunnen zijn die door sommige controllers dynamisch worden toegevoegd aan en verwijderd uit de bron. Wij kunnen en willen deze gegevens niet in Git bewaren. We willen echter dat de velden die we expliciet in Git hebben opgegeven bij de uitrol de juiste waarden aannemen.

Het blijkt zo algemeen gesynchroniseerde bronregel: bij het uitrollen van een bron kunt u alleen die velden wijzigen of verwijderen die expliciet zijn opgegeven in het manifest van Git (of die in een eerdere versie zijn opgegeven en nu zijn verwijderd).

3-weg samenvoegpatch

kerngedachte 3-weg samenvoegpatch: we genereren een patch tussen de laatst toegepaste versie van het manifest uit Git en de doelversie van het manifest uit Git, rekening houdend met de huidige versie van het manifest uit het actieve cluster. De resulterende patch moet voldoen aan de gesynchroniseerde bronregel:

  • nieuwe velden die aan de doelversie worden toegevoegd, worden toegevoegd met behulp van een patch;
  • eerder bestaande velden in de laatst toegepaste versie en niet bestaande in de doelversie worden gereset met behulp van een patch;
  • velden in de huidige versie van het object die afwijken van de doelversie van het manifest worden bijgewerkt met behulp van de patch.

Het is volgens dit principe dat het patches genereert kubectl apply:

  • de laatst toegepaste versie van het manifest wordt opgeslagen in de annotatie van het object zelf,
  • doel - overgenomen uit het opgegeven YAML-bestand,
  • de huidige is afkomstig van een actief cluster.

Nu we de theorie hebben uitgezocht, is het tijd om te vertellen wat we in de werf hebben gedaan.

Wijzigingen doorvoeren op werf

Voorheen gebruikte werf, net als Helm 2, 2-way-merge-patches.

Reparatie patch

Om over te schakelen naar een nieuw type patches - 3-way-merge - hebben we als eerste stap de zogenaamde reparatie pleisters.

Bij het implementeren wordt een standaard 2-way-merge-patch gebruikt, maar werf genereert bovendien een patch die de werkelijke status van de bron synchroniseert met wat in Git is geschreven (zo'n patch wordt gemaakt met behulp van dezelfde gesynchroniseerde bronregel die hierboven is beschreven) .

Als er een desynchronisatie plaatsvindt, ontvangt de gebruiker aan het einde van de implementatie een WAARSCHUWING met een bijbehorend bericht en een patch die moet worden toegepast om de bron in een gesynchroniseerde vorm te brengen. Deze patch is ook opgenomen in een speciale annotatie werf.io/repair-patch. Er wordt aangenomen dat de handen van de gebruiker zichzelf zal deze patch toepassen: werf zal deze helemaal niet toepassen.

Het genereren van reparatiepatches is een tijdelijke maatregel waarmee u het maken van patches daadwerkelijk kunt testen op basis van het 3-way-merge-principe, maar deze patches niet automatisch toepast. Momenteel is deze bedrijfsmodus standaard ingeschakeld.

3-weg-merge-patch alleen voor nieuwe releases

Vanaf 1 december 2019 beginnen de bèta- en alfaversies van werf standaard gebruik volwaardige 3-way-merge-patches om wijzigingen alleen toe te passen op nieuwe Helm-releases die via de werf zijn uitgerold. Bestaande releases zullen de 2-way-merge + reparatiepatches-aanpak blijven gebruiken.

Deze bedrijfsmodus kan expliciet worden ingeschakeld door instelling WERF_THREE_WAY_MERGE_MODE=onlyNewReleases nu.

Noot: de functie verscheen in verschillende releases in de werf: in het alfakanaal werd deze klaar met versie v1.0.5-alpha.19, en in het bètakanaal - met v1.0.4-bèta.20.

3-weg-merge-patch voor alle releases

Vanaf 15 december 2019 beginnen bèta- en alfaversies van werf standaard volledige 3-way-merge-patches te gebruiken om wijzigingen op alle releases toe te passen.

Deze bedrijfsmodus kan expliciet worden ingeschakeld door instelling WERF_THREE_WAY_MERGE_MODE=enabled nu.

Wat te doen met het automatisch schalen van bronnen?

Er zijn 2 soorten automatisch schalen in Kubernetes: HPA (horizontaal) en VPA (verticaal).

Horizontaal selecteert automatisch het aantal replica's, verticaal: het aantal bronnen. Zowel het aantal replica's als de resourcevereisten worden gespecificeerd in het resourcemanifest (zie Resourcemanifest). spec.replicas of spec.containers[].resources.limits.cpu, spec.containers[].resources.limits.memory и anderen).

Probleem: als een gebruiker een bron in een diagram zo configureert dat deze bepaalde waarden voor bronnen of replica's specificeert en autoscalers zijn ingeschakeld voor deze bron, dan zal werf bij elke implementatie deze waarden resetten naar wat er in het diagrammanifest staat geschreven .

Er zijn twee oplossingen voor het probleem. Om te beginnen kun je het beste vermijden om automatisch geschaalde waarden expliciet op te geven in het diagrammanifest. Als deze optie om een ​​of andere reden niet geschikt is (bijvoorbeeld omdat het handig is om initiële resourcelimieten en het aantal replica's in de grafiek in te stellen), biedt werf de volgende annotaties:

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

Als een dergelijke annotatie aanwezig is, zal werf de overeenkomstige waarden niet bij elke implementatie resetten, maar deze pas instellen wanneer de resource voor het eerst wordt aangemaakt.

Voor meer details, zie de projectdocumentatie voor HPA и VPA.

Verbied het gebruik van een 3-way-merge-patch

De gebruiker kan momenteel het gebruik van nieuwe patches in de werf verbieden met behulp van een omgevingsvariabele WERF_THREE_WAY_MERGE_MODE=disabled. Echter, starten Vanaf 1 maart 2020 geldt dit verbod niet meer. en het zal alleen mogelijk zijn om 3-way-merge-patches te gebruiken.

Adoptie van middelen in de werf

Door de methode van het toepassen van wijzigingen met 3-way-merge-patches onder de knie te krijgen, konden we onmiddellijk een dergelijke functie implementeren, zoals het overnemen van bronnen die in het cluster aanwezig waren in de Helm-release.

Helm 2 heeft een probleem: u kunt geen bron toevoegen aan diagrammanifesten die al in het cluster bestaan ​​zonder deze bron helemaal opnieuw te maken (zie. #6031, #3275). We leerden werf bestaande bronnen te accepteren voor vrijgave. Om dit te doen, moet u een annotatie installeren op de huidige versie van de bron van het actieve cluster (bijvoorbeeld met behulp van kubectl edit):

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

Nu moet de resource in het diagram worden beschreven en de volgende keer dat werf een release met de juiste naam implementeert, wordt de bestaande resource in deze release geaccepteerd en onder controle gehouden. Bovendien zal werf, tijdens het accepteren van een resource voor vrijgave, de huidige status van de resource van het actieve cluster naar de status brengen die in de grafiek wordt beschreven, met behulp van dezelfde 3-way-merge-patches en de gesynchroniseerde resourceregel.

Noot: instelling WERF_THREE_WAY_MERGE_MODE heeft geen invloed op de adoptie van bronnen - in het geval van adoptie wordt altijd een 3-way-merge-patch gebruikt.

Details - binnen documentatie.

Conclusies en toekomstplannen

Ik hoop dat het na dit artikel duidelijker is geworden wat 3-way-merge-patches zijn en waarom ze er zijn gekomen. Vanuit praktisch oogpunt van de ontwikkeling van het werfproject was de implementatie ervan een nieuwe stap in de richting van het verbeteren van de Helm-achtige inzet. Nu kunt u de problemen met configuratiesynchronisatie vergeten, die vaak ontstonden bij het gebruik van Helm 2. Tegelijkertijd werd een nieuwe handige functie voor het overnemen van reeds gedownloade Kubernetes-bronnen toegevoegd aan de Helm-release.

Er zijn nog steeds enkele problemen en uitdagingen met Helm-achtige implementaties, zoals het gebruik van Go-sjablonen, die we zullen blijven aanpakken.

Informatie over methoden voor het bijwerken van bronnen en de adoptie ervan is ook te vinden op deze documentatiepagina.

Roer 3

Een speciale opmerking waard uitgegeven onlangs een nieuwe hoofdversie van Helm - v3 - die ook 3-way-merge-patches gebruikt en Tiller verwijdert. De nieuwe versie van Helm vereist migratie bestaande installaties om ze te converteren naar het nieuwe release-opslagformaat.

Werf heeft op zijn beurt het gebruik van Tiller momenteel afgeschaft, is overgestapt op 3-way-merge en heeft dit toegevoegd veel meer, terwijl het compatibel blijft met bestaande Helm 2-installaties (er hoeven geen migratiescripts te worden uitgevoerd). Totdat de werf overschakelt naar Helm 3 verliezen werfgebruikers dus niet de belangrijkste voordelen van Helm 3 ten opzichte van Helm 2 (werf heeft ze ook).

De overstap van werf naar de Helm 3-codebase is echter onvermijdelijk en zal in de nabije toekomst plaatsvinden. Vermoedelijk zal dit werf 1.1 of werf 1.2 zijn (op dit moment is de hoofdversie van werf 1.0; voor meer informatie over het werfversie-apparaat, zie hier). Gedurende deze tijd heeft Helm 3 de tijd om te stabiliseren.

PS

Lees ook op onze blog:

Bron: www.habr.com

Voeg een reactie