Problema „inteligentă” a curățării imaginilor containerelor și soluția acesteia în werf

Problema „inteligentă” a curățării imaginilor containerelor și soluția acesteia în werf

Articolul abordează problematica curățării imaginilor care se acumulează în registrele containerelor (Docker Registry și omologii săi) în contextul pipeline-urilor CI/CD actuale pentru aplicații cloud native livrate în Kubernetes. Sunt prezentate principalele criterii de actualitate a imaginilor și dificultățile care decurg din acestea în automatizarea curățării, economisirea spațiului și satisfacerea nevoilor echipelor. În final, vom arăta, prin exemplul unui proiect Open Source specific, cum pot fi depășite aceste dificultăți.

Introducere

Numărul imaginilor din registrul containerelor poate crește rapid, ocupând mai mult spațiu de stocare și, în consecință, crescând semnificativ costul acestuia. Pentru a controla, limita sau menține o creștere acceptabilă a spațiului ocupat în registry, este obișnuit să:

  1. folosești un număr fix de etichete pentru imagini;
  2. curăți imaginile în vreun fel.


Prima limitare este uneori acceptabilă pentru echipe mici. Dacă dezvoltatorilor le sunt suficiente etichetele permanente (latest, main, test, boris etc.), registrul nu se va extinde ca volum și o lungă perioadă de timp nu va fi necesar să te gândești la curățare. Toate imaginile neactualizate sunt înlocuite, așa că nu rămâne muncă de curățare (totul se face de către colectorul standard de deșeuri).

Cu toate acestea, această abordare limitează semnificativ dezvoltarea și este rar aplicabilă în CI/CD pentru proiectele moderne. O parte integrantă a dezvoltării a devenit automatizarea, care permite testarea, desfășurarea și livrarea mai rapidă a noilor funcționalități utilizatorilor. De exemplu, în toate proiectele noastre, la fiecare commit se creează automat un pipeline CI. Aici se construiește imaginea, se testează, se lansează în diverse medii Kubernetes pentru debugging și verificări suplimentare, iar dacă totul este în regulă — modificările ajung la utilizatorul final. Și aceasta nu mai este o știință a rachetelor, ci o cotidiană pentru mulți — probabil și pentru tine, având în vedere că citești acest articol.

Deoarece eliminarea bug-urilor și dezvoltarea de noi funcționalități se desfășoară în paralel, iar lansările pot avea loc de mai multe ori pe zi, este evident că procesul de dezvoltare este însoțit de un număr semnificativ de commit-uri, ceea ce înseamnă — un număr mare de imagini în registry.Ca urmare, apare cu urgență problema organizării unei curățări eficiente a registry-ului, adică eliminarea imaginilor neactualizate.

Dar cum putem determina dacă imaginea este relevantă?

Criteriile de relevanță a imaginii

În cele mai multe cazuri, principalele criterii vor fi următoarele:

1. Primul (cel mai evident și cel mai critic dintre toate) — sunt imaginile care în prezent sunt utilizate în Kubernetes. Eliminarea acestor imagini poate duce la costuri serioase din cauza timpului de nefuncționare a producției (de exemplu, imaginile pot fi necesare pentru replicare) sau poate anula eforturile echipei responsabile cu depanarea în oricare dintre medii. (Din acest motiv, am creat chiar un exportator Prometheus, care monitorizează absența acestor imagini în oricare cluster Kubernetes.)

2. Al doilea (mai puțin evident, dar totuși foarte important și din nou legat de exploatare) — imaginile care sunt necesare pentru restaurare în cazul identificării problemelor serioase în versiunea curentă. De exemplu, în cazul Helm, acestea sunt imaginile utilizate în versiunile salvate ale lansării. (Apropo, implicit în Helm există o limită de 256 de revizuiri, dar cineva are într-adevăr nevoie de salvarea așa de multor versiuni?..) Totuși, noi, printre altele, păstrăm versiuni pentru a le putea utiliza ulterior, adică pentru a putea "revine" la ele în caz de necesitate.

3. Al treilea — nevoile dezvoltatorilor: toate imaginile legate de lucrările lor curente. De exemplu, dacă examinăm un PR, are sens să lăsăm imaginea care corespunde ultimei modificări și, să zicem, modificării anterioare: astfel, dezvoltatorul poate reveni rapid la orice sarcină și lucra cu ultimele actualizări.

4. Al patrulea — imaginile care corespund versiunilor aplicației noastre, adică sunt produsul final: v1.0.0, 20.04.01, sierra etc.

NB: Criteriile formulate aici au fost stabilite pe baza experienței de colaborare cu zeci de echipe de dezvoltare din diferite companii. Cu toate acestea, desigur, în funcție de particularitățile proceselor de dezvoltare și de infrastructura utilizată (de exemplu, în cazul în care nu se utilizează Kubernetes), aceste criterii pot varia.

Conformitatea cu criteriile și soluțiile existente

Serviciile populare cu registry de containere oferă, de obicei, propriile politici de curățare a imaginilor: în acestea, puteți defini condițiile în care un tag este șters din registry. Totuși, opțiunile acestor condiții sunt limitate la parametrii precum numele, timpul de creare și numărul de taguri.

* Depinde de implementările specifice ale registry-urilor de containere. Am analizat opțiunile următoarelor soluții: Azure CR, Docker Hub, ECR, GCR, GitHub Packages, GitLab Container Registry, Harbor Registry, JFrog Artifactory, Quay.io - la data de septembrie 2020.

Această setare de parametri este suficientă pentru a îndeplini cea de-a patra criteriu - adică pentru a selecta imaginile care corespund versiunilor. Cu toate acestea, pentru toate celelalte criterii, trebuie să alegem o soluție de compromis (o politică mai strictă sau, dimpotrivă, mai permisivă) - în funcție de așteptări și posibilitățile financiare.

De exemplu, al treilea criteriu - legat de nevoile dezvoltatorilor - poate fi rezolvat prin organizarea proceselor în interiorul echipelor: denumirea specifică a imaginilor, menținerea unor liste de permisiuni speciale și acorduri interne. Însă, în cele din urmă, acesta trebuie automatizat. Și dacă capacitățile soluțiilor existente nu sunt suficiente, trebuie să realizăm ceva propriu.

Situația este similară cu cele două prime criterii: nu pot fi îndeplinite fără a obține date dintr-un sistem extern - tocmai acela unde se desfășoară implementarea aplicațiilor (în cazul nostru, acesta este Kubernetes).

Ilustrarea fluxului de lucru în Git

Să presupunem că lucrați aproximativ după acest model în Git:

Problema „inteligentă” a curățării imaginilor containerelor și soluția acesteia în werf

În diagramă, cu iconița capului sunt marcate imaginile containerelor care sunt în prezent implementate în Kubernetes pentru diferiți utilizatori (utilizatori finali, testeri, manageri etc.) sau folosite de dezvoltatori pentru depanare și scopuri similare.

Ce se va întâmpla dacă politicile de curățare permit păstrarea (neștergerea) imaginilor doar după numele de taguri specificate?

Problema „inteligentă” a curățării imaginilor containerelor și soluția acesteia în werf

Este evident că un astfel de scenariu nu va bucura pe nimeni.

Ce se va schimba dacă politicile permit neștergerea imaginilor după un interval de timp specificat / numărul ultimelor comituri?

Problema „inteligentă” a curățării imaginilor containerelor și soluția acesteia în werf

Rezultatul a devenit semnificativ mai bun, totuși este încă departe de ideal. Deoarece avem în continuare dezvoltatori care au nevoie de imagini în registry (sau chiar implementate în K8s) pentru a depana bug-uri...

Rezumatul situației actuale de pe piață: funcțiile disponibile în registrele de containere nu oferă suficientă flexibilitate în ceea ce privește curățarea, iar motivul principal este lipsa posibilității de interacțiune cu lumea exterioară. Deci, echipele care necesită o astfel de flexibilitate trebuie să implementeze singure ștergerea imaginilor "din exterior", folosind Docker Registry API (sau API-ul nativ al implementării respective).

Cu toate acestea, noi am căutat o soluție universală, care să automatizeze curățarea imaginilor pentru diferite echipe care folosesc registre variate...

Drumul nostru către curățarea universală a imaginilor

De unde provine această necesitate? Faptul este că nu suntem o echipă de dezvoltatori izolată, ci o echipă care sprijină simultan mai multe asemenea grupuri, ajutând la soluționarea completă a problemelor CI/CD. Iar principalul instrument tehnic pentru aceasta este o unealtă Open Source werf. Caracteristica sa este că nu execută o funcție unică, ci susține procesele de livrare continuă pe toate etapele: de la construire la implementare.

Publicarea în registrul* imaginilor (imediat după ce acestea sunt construite) este o funcție evidentă a unei astfel de unelte. Și, deoarece imaginile sunt stocate acolo, iar dacă stocarea ta nu este nelimitată, trebuie să te ocupi și de curățarea ulterioară a acestora. Despre cum am reușit să avem succes în acest sens, respectând toate criteriile impuse, voi vorbi în continuare.

* Deși registrele în sine pot fi diverse (Docker Registry, GitLab Container Registry, Harbor etc.), utilizatorii lor se confruntă cu aceleași probleme. Soluția universală în cazul nostru nu depinde de implementarea registrului, deoarece este executată în afara registrelor și oferă un comportament uniform pentru toate.

Deși folosim werf ca exemplu de implementare, sperăm că abordările utilizate vor fi utile și altor echipe care se confruntă cu dificultăți similare.

Așadar, ne-am ocupat de implementarea externă a unui mecanism de curățare a imaginilor — în locul posibilităților care sunt deja integrate în registrele de containere. Primul pas a fost utilizarea Docker Registry API pentru a crea aceleași politici primitive referitoare la numărul de etichete și timpul de creare a acestora (menționate mai sus). Acestea au fost completate cu o listă permisivă bazată pe imaginile utilizate în infrastructura desfășurată, adică Kubernetes. Pentru acesta, a fost suficient să parcurgem toate resursele implementate prin API-ul Kubernetes și să obținem o listă de valori. imagine.

Această soluție trivială a rezolvat cea mai critică problemă (criteriul nr. 1), dar a fost doar începutul călătoriei noastre pentru îmbunătățirea mecanismului de curățare. Următorul — și mult mai interesant — pas a fost soluția de a lega imaginile publicate de istoricul Git.

Scheme de etichetare

La început, am ales o abordare în care imaginea finală trebuia să stocheze informațiile necesare pentru curățare, și am construit procesul pe baza schemelor de etichetare. La publicarea imaginii, utilizatorul a ales o anumită opțiune de etichetare (git-branch, git-commit sau git-tag) și a folosit valoarea corespunzătoare. În sistemele CI, setarea acestor valori a fost realizată automat pe baza variabilelor de mediu. Practic imaginea finală a fost legată de un anumit element Git, păstrând datele necesare pentru curățare în etichete.

În cadrul acestei abordări, a rezultat un set de politici care permitea utilizarea Git ca singură sursă de adevăr:

  • La ștergerea unei ramuri/tag în Git, imaginile legate erau șterse automat din registry.
  • Numărul de imagini asociat cu taguri și commit-uri Git putea fi reglat în funcție de numărul de taguri utilizate în schema aleasă și de momentul creării commit-ului asociat.

În general, implementarea rezultată satisface nevoile noastre, dar curând ne aștepta o nouă provocare. Problema era că, în timpul utilizării schemelor de etichetare pe baza elementelor Git, ne-am confruntat cu o serie de dezavantaje. (Deoarece descrierea lor depășește tema acestui articol, toți cei interesați pot consulta detalii aici.) Prin urmare, după ce am decis să trecem la o abordare mai eficientă de etichetare (etichete pe bază de conținut), a trebuit să revizuim și implementarea curățării imaginilor.

Noua algoritm

De ce? La etichetarea pe baza conținutului, fiecare etichetă poate corespunde mai multor commit-uri în Git. La curățarea imaginilor, nu mai este posibil doar să ne bazăm pe commit-ul la care noua etichetă a fost adăugată în registry.

Pentru noua algoritm de curățare, s-a decis să renunțăm la schemele de etichetare și să construim procesul pe baza meta-imagenelor, fiecare dintre acestea stocând o legătură din:

  • commitul pe care s-a efectuat publicarea (indiferent dacă a fost adăugat, modificat sau a rămas același în registrul de containere);
  • și identificatorul nostru intern, corespunzător imaginii create.

Cu alte cuvinte, a fost asigurată corelarea etichetelor publicate cu comitările din Git.

Configurarea finală și algoritmul general

Utilizatorii, la configurarea curățării, au avut acces la politicile prin care se selectează imaginile actuale. Fiecare astfel de politică este definită:

  • de un set de referințe, adică etichete Git sau ramuri Git, care sunt utilizate în timpul scanării;
  • și de limita de imagini căutate pentru fiecare referință din mulțime.

Pentru ilustrare — iată cum arată acum configurarea politicilor implicite:

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

Această configurație conține trei politici, care corespunzător următoarelor reguli:

  1. Menținerea imaginii pentru ultimele 10 etichete Git (după data creării etichetei).
  2. Menținerea a nu mai mult de 2 imagini, publicate în ultima săptămână, pentru nu mai mult de 10 ramuri active în ultima săptămână.
  3. Menținerea a 10 imagini pentru ramuri main, staging și producție.

Algoritmul final se rezumă la următorii pași:

  • Obținerea manifestelor din registrul de containere.
  • Excluderea imaginilor utilizate în Kubernetes, deoarece acestea au fost deja selectate, interogând API-ul K8s.
  • Scanarea istoricului Git și excluderea imaginilor conform politicilor specificate.
  • Ștergerea imaginilor rămase.

Revenind la ilustrarea noastră, iată ce rezultă pentru werf:

Problema „inteligentă” a curățării imaginilor containerelor și soluția acesteia în werf

Cu toate acestea, chiar dacă nu folosiți werf, o abordare similară pentru curățarea avansată a imaginilor — într-o formă sau alta (conform metodei preferate de etichetare a imaginilor) — poate fi aplicată și în alte sisteme/utilitare. Este suficient să țineți cont de problemele care apar și să găsiți acele oportunități în tehnologia dumneavoastră care permit integrarea soluției lor cât mai neted. Sperăm că drumul parcurs de noi va ajuta la analizarea situației dumneavoastră particulare cu noi detalii și gânduri.

Concluzie

  • Mai devreme sau mai târziu, problema umplerii registrului se confruntă cu majoritatea echipelor.
  • Când căutați soluții, trebuie mai întâi să definiți criteriile de relevanță ale imaginilor.
  • Instrumentele oferite de serviciile populare de container registry permit organizarea unei curățări foarte simple, care nu ia în considerare „lumea externă”: imagini utilizate în Kubernetes și caracteristicile proceselor de lucru din echipă.
  • Un algoritm flexibil și eficient trebuie să aibă o înțelegere a proceselor CI/CD, operând nu doar cu datele imaginilor Docker.

P.S.

Citiți și în blogul nostru:

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster