Problemet med "smart" rengöring av behållarbilder och dess lösning i werf

Problemet med "smart" rengöring av behållarbilder och dess lösning i werf

Artikeln diskuterar problemen med att rensa bilder som ackumuleras i containerregister (Docker Registry och dess analoger) i verkligheten av moderna CI/CD-pipelines för molnbaserade applikationer som levereras till Kubernetes. Huvudkriterierna för bildernas relevans och de resulterande svårigheterna med att automatisera rengöring, spara utrymme och möta teamens behov anges. Slutligen, med hjälp av exemplet på ett specifikt Open Source-projekt, kommer vi att berätta hur dessa svårigheter kan övervinnas.

Inledning

Antalet bilder i ett containerregister kan växa snabbt, ta upp mer lagringsutrymme och därmed avsevärt öka kostnaderna. För att kontrollera, begränsa eller bibehålla acceptabel tillväxt av utrymme som upptas i registret, accepteras det:

  1. använd ett fast antal taggar för bilder;
  2. rengöra bilderna på något sätt.


Den första begränsningen är ibland acceptabel för små team. Om utvecklare har tillräckligt med permanenta taggar (latest, main, test, boris etc.), kommer registret inte att svälla i storlek och under lång tid behöver du inte tänka på att rengöra det alls. Alla irrelevanta bilder raderas trots allt, och det finns helt enkelt inget arbete kvar för rengöring (allt görs av en vanlig sophämtare).

Detta tillvägagångssätt begränsar dock utvecklingen avsevärt och är sällan tillämpligt på moderna CI/CD-projekt. En integrerad del av utvecklingen var automation, som låter dig testa, distribuera och leverera ny funktionalitet till användare mycket snabbare. Till exempel, i alla våra projekt skapas en CI-pipeline automatiskt med varje commit. I den monteras bilden, testas, rullas ut till olika Kubernetes-kretsar för felsökning och återstående kontroller, och om allt är bra når ändringarna slutanvändaren. Och detta är inte längre raketvetenskap, utan en vardaglig händelse för många - troligen för dig, eftersom du läser den här artikeln.

Eftersom fixering av buggar och utveckling av ny funktionalitet utförs parallellt, och releaser kan utföras flera gånger om dagen, är det uppenbart att utvecklingsprocessen åtföljs av ett betydande antal commits, vilket innebär ett stort antal bilder i registret. Som ett resultat uppstår frågan om att organisera en effektiv städning av registret, d.v.s. ta bort irrelevanta bilder.

Men hur avgör man ens om en bild är relevant?

Kriterier för bildens relevans

I de allra flesta fall kommer huvudkriterierna att vara:

1. Den första (den mest uppenbara och mest kritiska av alla) är bilderna som används för närvarande i Kubernetes. Att ta bort dessa bilder kan resultera i betydande produktionsavbrottskostnader (till exempel kan bilderna behövas för replikering) eller förneka ansträngningarna från teamets felsökning på någon av slingorna. (Av denna anledning gjorde vi till och med en speciell Prometheus exportörer, som spårar frånvaron av sådana bilder i något Kubernetes-kluster.)

2. För det andra (mindre uppenbart, men också mycket viktigt och återigen relaterar till exploatering) - bilder som krävs för återställning vid upptäckt av allvarliga problem i den aktuella versionen. Till exempel, i fallet med Helm, är dessa bilder som används i sparade versioner av utgåvan. (Förresten, som standard i Helm är gränsen 256 revisioner, men det är osannolikt att någon verkligen behöver spara en sådan ett stort antal versioner?..) Vi lagrar ju framför allt versioner så att vi kan använda dem senare, d.v.s. "rulla tillbaka" till dem om det behövs.

3. Tredje - utvecklarens behov: Alla bilder som är relaterade till deras nuvarande arbete. Till exempel, om vi överväger en PR, är det vettigt att lämna en bild som motsvarar den senaste commit och, säg, den föregående commit: på så sätt kan utvecklaren snabbt återgå till vilken uppgift som helst och arbeta med de senaste ändringarna.

4. Fjärde - bilder som motsvarar versionerna av vår applikation, dvs. är slutprodukten: v1.0.0, 20.04.01/XNUMX/XNUMX, sierra, etc.

OBS: Kriterierna som definieras här har formulerats baserat på erfarenhet av interaktion med dussintals utvecklingsteam från olika företag. Men naturligtvis, beroende på detaljerna i utvecklingsprocesserna och den infrastruktur som används (till exempel Kubernetes används inte), kan dessa kriterier skilja sig åt.

Behörighet och befintliga lösningar

Populära tjänster med behållarregister erbjuder som regel sina egna policyer för bildrensning: i dem kan du definiera villkoren under vilka en tagg tas bort från registret. Dessa villkor begränsas dock av parametrar som namn, skapelsetid och antal taggar*.

* Beror på specifika containerregisterimplementeringar. Vi övervägde möjligheterna med följande lösningar: Azure CR, Docker Hub, ECR, GCR, GitHub-paket, GitLab Container Registry, Harbor Registry, JFrog Artifactory, Quay.io - från och med september 2020.

Denna uppsättning parametrar är tillräckligt för att uppfylla det fjärde kriteriet - det vill säga att välja bilder som motsvarar versionerna. Men för alla andra kriterier måste man välja någon form av kompromisslösning (en hårdare eller omvänt mildare politik) – beroende på förväntningar och ekonomiska möjligheter.

Till exempel kan det tredje kriteriet – relaterat till utvecklarnas behov – lösas genom att organisera processer inom teamen: specifika namngivning av bilder, upprätthålla särskilda tillåtelselistor och interna avtal. Men i slutändan måste det fortfarande automatiseras. Och om förmågan hos färdiga lösningar inte räcker till måste du göra något eget.

Situationen med de två första kriterierna är liknande: de kan inte uppfyllas utan att ta emot data från ett externt system - det där applikationer distribueras (i vårt fall Kubernetes).

Illustration av arbetsflöde i Git

Låt oss säga att du arbetar ungefär så här i Git:

Problemet med "smart" rengöring av behållarbilder och dess lösning i werf

Ikonen med ett huvud i diagrammet indikerar behållarbilder som för närvarande är distribuerade i Kubernetes för alla användare (slutanvändare, testare, förvaltare, etc.) eller används av utvecklare för felsökning och liknande ändamål.

Vad händer om rensningspolicyer endast tillåter att bilder behålls (inte raderade) genom givna taggnamn?

Problemet med "smart" rengöring av behållarbilder och dess lösning i werf

Uppenbarligen kommer ett sådant scenario inte att göra någon lycklig.

Vad kommer att förändras om policyer tillåter att bilder inte tas bort? enligt ett givet tidsintervall / antal senaste commits?

Problemet med "smart" rengöring av behållarbilder och dess lösning i werf

Resultatet har blivit mycket bättre, men är fortfarande långt ifrån idealiskt. När allt kommer omkring har vi fortfarande utvecklare som behöver bilder i registret (eller till och med distribuerade i K8s) för att felsöka buggar...

För att sammanfatta den nuvarande marknadssituationen: funktionerna som finns tillgängliga i containerregistren erbjuder inte tillräcklig flexibilitet vid rengöring, och den främsta anledningen till detta är inget sätt att interagera med omvärlden. Det visar sig att team som kräver sådan flexibilitet tvingas att självständigt implementera bildradering "från utsidan", med hjälp av Docker Registry API (eller det inbyggda API:et för motsvarande implementering).

Men vi letade efter en universell lösning som skulle automatisera bildrensning för olika team med hjälp av olika register...

Vår väg till universell bildrengöring

Var kommer detta behov ifrån? Faktum är att vi inte är en separat grupp av utvecklare, utan ett team som servar många av dem på en gång, och hjälper till att heltäckande lösa CI/CD-problem. Och det viktigaste tekniska verktyget för detta är Open Source-verktyget werf. Dess egenhet är att den inte utför en enda funktion, utan följer med kontinuerliga leveransprocesser i alla skeden: från montering till driftsättning.

Att publicera bilder till registret* (direkt efter att de byggts) är en självklar funktion av ett sådant verktyg. Och eftersom bilderna placeras där för lagring, då - om din lagring inte är obegränsad - måste du ansvara för deras efterföljande rengöring. Hur vi nådde framgång i detta, med att uppfylla alla angivna kriterier, kommer att diskuteras vidare.

* Även om registren i sig kan vara olika (Docker Registry, GitLab Container Registry, Harbor, etc.), möter deras användare samma problem. Den universella lösningen i vårt fall beror inte på implementeringen av registret, eftersom körs utanför registren själva och erbjuder samma beteende för alla.

Även om vi använder werf som ett exempel på implementering, hoppas vi att de tillvägagångssätt som används kommer att vara användbara för andra team som ställs inför liknande svårigheter.

Så vi fick fullt upp extern implementering av en mekanism för rengöring av bilder - istället för de funktioner som redan är inbyggda i register för behållare. Det första steget var att använda Docker Registry API för att skapa samma primitiva policyer för antalet taggar och tidpunkten för deras skapande (som nämns ovan). Lades till dem tillåt lista baserad på bilder som används i distribuerad infrastruktur, dvs. Kubernetes. För det senare räckte det med att använda Kubernetes API för att iterera genom alla distribuerade resurser och få en lista med värden image.

Denna triviala lösning löste det mest kritiska problemet (kriterium nr 1), men var bara början på vår resa för att förbättra rengöringsmekanismen. Nästa – och mycket mer intressanta – steg var beslutet associera publicerade bilder med Git-historik.

Taggningsscheman

Till att börja med valde vi ett tillvägagångssätt där den slutliga bilden skulle lagra nödvändig information för rengöring, och byggde processen på märkningsscheman. När en bild publicerades valde användaren ett specifikt taggningsalternativ (git-branch, git-commit eller git-tag) och använde motsvarande värde. I CI-system ställdes dessa värden automatiskt baserat på miljövariabler. Faktiskt den slutliga bilden associerades med en specifik Git-primitiv, lagrar nödvändiga data för rengöring i etiketter.

Detta tillvägagångssätt resulterade i en uppsättning policyer som gjorde att Git kunde användas som den enda källan till sanning:

  • Vid borttagning av en gren/tagg i Git raderades de associerade bilderna i registret automatiskt.
  • Antalet bilder associerade med Git-taggar och commits kan styras av antalet taggar som används i det valda schemat och tidpunkten då den associerade commit skapades.

Sammantaget tillfredsställde den resulterande implementeringen våra behov, men en ny utmaning väntade oss snart. Faktum är att när vi använde taggningsscheman baserade på Git-primitiver, stötte vi på ett antal brister. (Eftersom deras beskrivning ligger utanför ramen för denna artikel kan alla bekanta sig med detaljerna här.) Därför, efter att ha beslutat att byta till en mer effektiv metod för taggning (innehållsbaserad taggning), var vi tvungna att ompröva implementeringen av bildrensning.

Ny algoritm

Varför? Med innehållsbaserad taggning kan varje tagg uppfylla flera commits i Git. När du rengör bilder kan du inte längre anta endast från commit där den nya taggen lades till i registret.

För den nya städalgoritmen beslutades att gå bort från taggningsscheman och bygga metabildsprocess, som var och en lagrar ett gäng:

  • den commit som publiceringen utfördes på (det spelar ingen roll om bilden lades till, ändrades eller förblev densamma i behållarregistret);
  • och vår interna identifierare som motsvarar den sammansatta bilden.

Det tillhandahölls med andra ord länka publicerade taggar med commits i Git.

Slutlig konfiguration och allmän algoritm

När du konfigurerar rengöring har användare nu tillgång till policyer som väljer aktuella bilder. Varje sådan policy definieras:

  • många referenser, d.v.s. Git-taggar eller Git-grenar som används under skanning;
  • och gränsen för sökta bilder för varje referens från uppsättningen.

För att illustrera, så här började standardpolicykonfigurationen se ut:

cleanup:
  keepPolicies:
  - references:
      tag: /.*/
      limit:
        last: 10
  - references:
      branch: /.*/
      limit:
        last: 10
        in: 168h
        operator: And
    imagesPerReference:
      last: 2
      in: 168h
      operator: And
  - references:  
      branch: /^(main|staging|production)$/
    imagesPerReference:
      last: 10

Den här konfigurationen innehåller tre policyer som följer följande regler:

  1. Spara bilden för de senaste 10 Git-taggarna (efter datum då taggen skapades).
  2. Spara högst 2 bilder publicerade under den senaste veckan för högst 10 trådar med aktivitet under den senaste veckan.
  3. Spara 10 bilder för grenar main, staging и production.

Den slutliga algoritmen kokar ner till följande steg:

  • Hämtar manifest från containerregistret.
  • Exkluderar bilder som används i Kubernetes, eftersom Vi har redan valt dem i förväg genom att polla K8s API.
  • Skanna Git-historik och exkludera bilder baserat på specificerade policyer.
  • Tar bort återstående bilder.

För att återgå till vår illustration, det här är vad som händer med werf:

Problemet med "smart" rengöring av behållarbilder och dess lösning i werf

Men även om du inte använder werf kan ett liknande tillvägagångssätt för avancerad bildrensning - i en eller annan implementering (enligt den föredragna metoden för bildtaggning) - tillämpas på andra system/verktyg. För att göra detta räcker det att komma ihåg problemen som uppstår och hitta de möjligheter i din stack som låter dig integrera deras lösning så smidigt som möjligt. Vi hoppas att vägen vi har vandrat hjälper dig att titta på just ditt fall med nya detaljer och tankar.

Slutsats

  • Förr eller senare stöter de flesta team på problemet med registerspill.
  • När du söker efter lösningar är det först nödvändigt att bestämma kriterierna för bildens relevans.
  • Verktygen som erbjuds av populära behållarregistertjänster låter dig organisera en mycket enkel rensning som inte tar hänsyn till "omvärlden": bilderna som används i Kubernetes och särdragen i teamets arbetsflöden.
  • En flexibel och effektiv algoritm måste ha förståelse för CI/CD-processer och fungera inte bara med Docker-bilddata.

PS

Läs även på vår blogg:

Källa: will.com

Lägg en kommentar