Problem „inteligentnego” czyszczenia obrazów kontenerów i jego rozwiązanie w werf

Problem „inteligentnego” czyszczenia obrazów kontenerów i jego rozwiązanie w werf

W artykule omówiono problemy czyszczenia obrazów gromadzących się w rejestrach kontenerów (Docker Registry i jego analogi) w realiach współczesnych potoków CI/CD dla aplikacji chmurowych dostarczanych do Kubernetes. Podano główne kryteria trafności obrazów i wynikające z nich trudności w automatyzacji sprzątania, oszczędzaniu miejsca i zaspokajaniu potrzeb zespołów. Na koniec, na przykładzie konkretnego projektu Open Source, powiemy, jak można pokonać te trudności.

Wprowadzenie

Liczba obrazów w rejestrze kontenerów może szybko rosnąć, zajmując więcej miejsca na dysku i tym samym znacznie zwiększając jego koszt. W celu kontroli, ograniczenia lub utrzymania akceptowalnego wzrostu zajmowanej powierzchni w rejestrze przyjmuje się:

  1. użyj stałej liczby tagów dla obrazów;
  2. w jakiś sposób oczyścić obrazy.


Pierwsze ograniczenie jest czasami akceptowalne w przypadku małych zespołów. Jeśli programiści mają wystarczającą liczbę trwałych tagów (latest, main, test, boris itp.), rejestr nie będzie puchnąć i przez długi czas nie będziesz musiał w ogóle myśleć o jego czyszczeniu. W końcu wszystkie nieistotne obrazy są usuwane i po prostu nie ma już nic do czyszczenia (wszystko robi zwykły śmieciarz).

Jednak takie podejście znacznie ogranicza rozwój i rzadko ma zastosowanie w nowoczesnych projektach CI/CD. Integralną częścią rozwoju było automatyzacja, co pozwala znacznie szybciej testować, wdrażać i dostarczać użytkownikom nowe funkcjonalności. Na przykład we wszystkich naszych projektach potok CI jest tworzony automatycznie przy każdym zatwierdzeniu. W nim obraz jest składany, testowany, wdrażany do różnych obwodów Kubernetesa w celu debugowania i pozostałych kontroli, a jeśli wszystko jest w porządku, zmiany docierają do użytkownika końcowego. I nie jest to już nauka o rakietach, ale dla wielu codzienność – najprawdopodobniej dla Ciebie, ponieważ czytasz ten artykuł.

Ponieważ naprawianie błędów i tworzenie nowych funkcjonalności odbywa się równolegle, a wydania mogą odbywać się kilka razy dziennie, oczywistym jest, że procesowi rozwoju towarzyszy znaczna liczba zatwierdzeń, co oznacza, że duża liczba obrazów w rejestrze. W efekcie pojawia się kwestia zorganizowania skutecznego czyszczenia rejestru, tj. usuwanie nieistotnych obrazów.

Ale jak w ogóle ustalić, czy obraz jest istotny?

Kryteria trafności obrazu

W zdecydowanej większości przypadków głównymi kryteriami będą:

1. Pierwszy (najbardziej oczywisty i krytyczny ze wszystkich) to obrazy, które obecnie używany w Kubernetesie. Usunięcie tych obrazów może skutkować znacznymi kosztami przestojów w produkcji (na przykład obrazy mogą być wymagane do replikacji) lub zniweczyć wysiłki zespołu debugującego w dowolnej pętli. (Z tego powodu zrobiliśmy nawet coś specjalnego Eksporterzy Prometheusa, który śledzi brak takich obrazów w dowolnym klastrze Kubernetes.)

2. Po drugie (mniej oczywiste, ale także bardzo ważne i ponownie związane z eksploatacją) - obrazy, które wymagane do wycofania zmian w przypadku wykrycia poważnych problemów w aktualnej wersji. Przykładowo w przypadku Helma są to obrazy, które są wykorzystywane w zapisanych wersjach wydania. (Nawiasem mówiąc, domyślnie w Helmie limit wynosi 256 wersji, ale jest mało prawdopodobne, aby ktokolwiek naprawdę musiał zapisywać taki a duża ilość wersji?..) Przecież my w szczególności przechowujemy wersje, żeby móc z nich później skorzystać, tj. „cofnij się” do nich, jeśli to konieczne.

3. Trzeci - potrzeby dewelopera: wszystkie obrazy związane z ich obecną pracą. Na przykład, jeśli rozważamy PR, sensowne jest pozostawienie obrazu odpowiadającego ostatniemu zatwierdzeniu i, powiedzmy, poprzedniemu zatwierdzeniu: w ten sposób programista może szybko wrócić do dowolnego zadania i pracować z najnowszymi zmianami.

4. Po czwarte – obrazy, które odpowiadają wersjom naszej aplikacji, tj. to produkt końcowy: v1.0.0, 20.04.01, sierra itp.

Uwaga: Zdefiniowane tutaj kryteria zostały sformułowane w oparciu o doświadczenia z interakcji z dziesiątkami zespołów programistycznych z różnych firm. Jednak oczywiście w zależności od specyfiki procesów deweloperskich i wykorzystywanej infrastruktury (np. nie jest używany Kubernetes) kryteria te mogą się różnić.

Kwalifikowalność i istniejące rozwiązania

Popularne usługi z rejestrami kontenerów z reguły oferują własne zasady czyszczenia obrazów: w nich możesz zdefiniować warunki usunięcia tagu z rejestru. Warunki te są jednak ograniczone parametrami takimi jak nazwa, czas utworzenia i liczba tagów*.

* Zależy od konkretnych implementacji rejestru kontenerów. Rozważaliśmy możliwości następujących rozwiązań: Azure CR, Docker Hub, ECR, GCR, GitHub Packages, GitLab Container Registry, Harbour Registry, JFrog Artifactory, Quay.io – stan na wrzesień 2020.

Ten zestaw parametrów wystarczy, aby spełnić czwarte kryterium - czyli wybrać obrazy odpowiadające wersjom. Jednak dla wszystkich pozostałych kryteriów trzeba wybrać jakieś rozwiązanie kompromisowe (surowszą lub odwrotnie łagodną politykę) – w zależności od oczekiwań i możliwości finansowych.

Przykładowo trzecie kryterium – związane z potrzebami programistów – można rozwiązać organizując procesy w zespołach: konkretne nazewnictwo obrazów, prowadzenie list specjalnych dozwolonych i uzgodnień wewnętrznych. Ale ostatecznie nadal wymaga to automatyzacji. A jeśli możliwości gotowych rozwiązań nie wystarczą, trzeba zrobić coś własnego.

Podobnie jest z dwoma pierwszymi kryteriami: nie da się ich spełnić bez otrzymania danych z systemu zewnętrznego – tego, w którym wdrażane są aplikacje (w naszym przypadku Kubernetesa).

Ilustracja przepływu pracy w Git

Powiedzmy, że pracujesz w Git w podobny sposób:

Problem „inteligentnego” czyszczenia obrazów kontenerów i jego rozwiązanie w werf

Ikona z głową na diagramie wskazuje obrazy kontenerów, które są obecnie wdrożone w Kubernetes dla dowolnych użytkowników (użytkowników końcowych, testerów, menedżerów itp.) lub są używane przez programistów do debugowania i podobnych celów.

Co się stanie, jeśli zasady czyszczenia zezwalają jedynie na zachowanie (a nie usunięcie) obrazów według podanych nazw tagów?

Problem „inteligentnego” czyszczenia obrazów kontenerów i jego rozwiązanie w werf

Oczywiście taki scenariusz nikogo nie uszczęśliwi.

Co się zmieni, jeśli zasady zabraniają usuwania obrazów? według zadanego przedziału czasowego/liczby ostatnich zatwierdzeń?

Problem „inteligentnego” czyszczenia obrazów kontenerów i jego rozwiązanie w werf

Wynik jest znacznie lepszy, ale nadal daleki od ideału. W końcu wciąż mamy programistów, którzy potrzebują obrazów w rejestrze (lub nawet wdrożonych w K8), aby debugować błędy...

Podsumowując obecną sytuację rynkową: funkcje dostępne w rejestrach kontenerów nie zapewniają wystarczającej elastyczności podczas czyszczenia, a głównym powodem jest to, że nie ma możliwości kontaktu ze światem zewnętrznym. Okazuje się, że zespoły wymagające takiej elastyczności zmuszone są samodzielnie wdrożyć usuwanie obrazów „z zewnątrz”, korzystając z API Docker Registry (lub natywnego API odpowiedniej implementacji).

Szukaliśmy jednak uniwersalnego rozwiązania, które zautomatyzowałoby czyszczenie obrazów dla różnych zespołów korzystających z różnych rejestrów...

Nasza droga do uniwersalnego czyszczenia obrazu

Skąd bierze się ta potrzeba? Faktem jest, że nie jesteśmy odrębną grupą programistów, ale zespołem obsługującym wielu z nich na raz, pomagając kompleksowo rozwiązywać problemy CI/CD. Głównym narzędziem technicznym do tego jest narzędzie Open Source werf. Jego osobliwością jest to, że nie pełni jednej funkcji, ale towarzyszy ciągłym procesom dostaw na wszystkich etapach: od montażu po wdrożenie.

Publikowanie obrazów w rejestrze* (zaraz po ich zbudowaniu) jest oczywistą funkcją takiego narzędzia. A ponieważ obrazy są tam umieszczane w celu przechowywania, to - jeśli Twoje miejsce do przechowywania nie jest nieograniczone - musisz być odpowiedzialny za ich późniejsze czyszczenie. O tym, w jaki sposób osiągnęliśmy w tym sukces, spełniając wszystkie określone kryteria, omówimy dalej.

* Choć same rejestry mogą się różnić (Docker Registry, GitLab Container Registry, Harbour itp.), ich użytkownicy borykają się z tymi samymi problemami. Uniwersalne rozwiązanie w naszym przypadku nie jest uzależnione od wykonania rejestru, gdyż działa poza samymi rejestrami i oferuje to samo zachowanie dla wszystkich.

Chociaż używamy werf jako przykładowej implementacji, mamy nadzieję, że zastosowane podejścia będą przydatne dla innych zespołów borykających się z podobnymi trudnościami.

Więc byliśmy zajęci zewnętrzny wdrożenie mechanizmu czyszczenia obrazów - zamiast tych możliwości, które są już wbudowane w rejestry kontenerów. Pierwszym krokiem było wykorzystanie API Docker Registry do stworzenia tych samych prymitywnych polityk dotyczących ilości tagów i czasu ich utworzenia (wspomnianych powyżej). Dodano do nich lista dozwolonych oparta na obrazach używanych we wdrożonej infrastrukturze, tj. Kubernetes. W tym drugim przypadku wystarczyło użycie Kubernetes API, aby iterować po wszystkich wdrożonych zasobach i uzyskać listę wartości image.

To trywialne rozwiązanie rozwiązało najbardziej krytyczny problem (kryterium nr 1), ale było dopiero początkiem naszej drogi do ulepszenia mechanizmu czyszczącego. Kolejnym – i dużo ciekawszym – krokiem była decyzja powiąż opublikowane obrazy z historią Git.

Schematy tagowania

Na początek wybraliśmy podejście, w którym końcowy obraz powinien przechowywać informacje niezbędne do czyszczenia, i zbudowaliśmy proces na schematach tagowania. Publikując obraz, użytkownik wybrał konkretną opcję tagowania (git-branch, git-commit lub git-tag) i użył odpowiedniej wartości. W systemach CI wartości te były ustawiane automatycznie na podstawie zmiennych środowiskowych. W rzeczywistości ostateczny obraz został powiązany z konkretnym prymitywem Git, przechowując niezbędne dane do czyszczenia na etykietach.

Takie podejście zaowocowało zestawem zasad, które pozwoliły używać Gita jako pojedynczego źródła prawdy:

  • Podczas usuwania gałęzi/tagu w Git powiązane obrazy w rejestrze zostały automatycznie usunięte.
  • Liczba obrazów powiązanych ze znacznikami i zatwierdzeniami Git może być kontrolowana przez liczbę znaczników użytych w wybranym schemacie i czas utworzenia powiązanego zatwierdzenia.

Ogólnie rzecz biorąc, powstałe wdrożenie spełniło nasze potrzeby, ale wkrótce czekało na nas nowe wyzwanie. Faktem jest, że korzystając ze schematów tagowania opartych na prymitywach Gita natrafiliśmy na szereg niedociągnięć. (Ponieważ ich opis wykracza poza zakres tego artykułu, każdy może zapoznać się ze szczegółami tutaj.) Dlatego też, decydując się na bardziej efektywne podejście do tagowania (tagowanie oparte na treści), musieliśmy ponownie rozważyć wdrożenie czyszczenia obrazu.

Nowy algorytm

Dlaczego? Dzięki tagowaniu opartemu na treści każdy tag może spełnić wiele zatwierdzeń w Git. Podczas czyszczenia obrazów nie można już zakładać tylko z zatwierdzenia, w którym nowy tag został dodany do rejestru.

W przypadku nowego algorytmu czyszczenia zdecydowano się odejść od schematów tagowania i zbudować proces metaobrazu, z których każdy przechowuje kilka:

  • commit, na którym dokonano publikacji (nie ma znaczenia, czy obraz został dodany, zmieniony, czy pozostał taki sam w rejestrze kontenerów);
  • oraz nasz wewnętrzny identyfikator odpowiadający zmontowanemu obrazowi.

Inaczej mówiąc, zostało to zapewnione łączenie opublikowanych tagów z zatwierdzeniami w Git.

Konfiguracja końcowa i ogólny algorytm

Podczas konfigurowania czyszczenia użytkownicy mają teraz dostęp do zasad wybierających bieżące obrazy. Każda taka polityka jest zdefiniowana:

  • wiele referencji, tj. Tagi Git lub gałęzie Git używane podczas skanowania;
  • oraz limit wyszukiwanych obrazów dla każdej referencji ze zbioru.

Aby to zilustrować, tak zaczęła wyglądać domyślna konfiguracja zasad:

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

Ta konfiguracja zawiera trzy zasady zgodne z następującymi regułami:

  1. Zapisz obraz dla ostatnich 10 tagów Git (według daty utworzenia tagu).
  2. Zapisz nie więcej niż 2 obrazy opublikowane w ostatnim tygodniu dla nie więcej niż 10 wątków z aktywnością w ostatnim tygodniu.
  3. Zapisz 10 obrazów dla gałęzi main, staging и production.

Ostateczny algorytm sprowadza się do następujących kroków:

  • Pobieranie manifestów z rejestru kontenerów.
  • Z wyłączeniem obrazów używanych w Kubernetes, ponieważ Wybraliśmy je już wstępnie, odpytując API K8.
  • Skanowanie historii Git i wykluczanie obrazów w oparciu o określone zasady.
  • Usuwanie pozostałych obrazów.

Wracając do naszej ilustracji, oto co dzieje się z werfem:

Problem „inteligentnego” czyszczenia obrazów kontenerów i jego rozwiązanie w werf

Jednak nawet jeśli nie używasz werf, podobne podejście do zaawansowanego czyszczenia obrazów - w tej czy innej implementacji (zgodnie z preferowanym podejściem do tagowania obrazów) - można zastosować w innych systemach/narzędziach. Aby to zrobić, wystarczy pamiętać o pojawiających się problemach i znaleźć w swoim stosie takie możliwości, które pozwolą Ci możliwie sprawnie zintegrować ich rozwiązanie. Mamy nadzieję, że przebyta przez nas droga pomoże Państwu spojrzeć na Państwa konkretny przypadek z nowymi szczegółami i przemyśleniami.

wniosek

  • Wcześniej czy później większość zespołów napotyka problem przepełnienia rejestru.
  • Poszukując rozwiązań, należy najpierw określić kryteria trafności obrazu.
  • Narzędzia oferowane przez popularne usługi rejestracji kontenerów pozwalają zorganizować bardzo proste porządki, które nie uwzględniają „świata zewnętrznego”: obrazów używanych w Kubernetesie i specyfiki przepływów pracy zespołu.
  • Elastyczny i wydajny algorytm musi rozumieć procesy CI/CD i operować nie tylko danymi obrazu Dockera.

PS

Przeczytaj także na naszym blogu:

Źródło: www.habr.com

Dodaj komentarz