
W artykule omówiono problem czyszczenia obrazów gromadzących się w rejestrach kontenerów (Docker Registry i jego odpowiedniki) w realiach nowoczesnych procesów CI/CD dla aplikacji chmurowych dostarczanych do Kubernetesa. Przedstawiono główne kryteria trafności obrazów i wynikające z nich trudności w automatyzacji czyszczenia, oszczędzaniu miejsca i zaspokajaniu potrzeb zespołów. Na koniec, wykorzystując jako przykład konkretny projekt Open Source, opowiemy, jak można pokonać te trudności.
Wprowadzenie
Liczba obrazów w rejestrze kontenerów może szybko rosnąć, zajmując coraz więcej miejsca na dysku, a co za tym idzie, znacznie zwiększając jego koszt. Aby kontrolować, ograniczać lub utrzymywać akceptowalny wzrost zajmowanej przestrzeni w rejestrze, przyjmuje się:
- użyj ustalonej liczby tagów dla obrazów;
- wyczyść obrazy w jakiś sposób.
Pierwsze ograniczenie jest czasem akceptowalne w przypadku małych zespołów. Jeśli programiści mają wystarczająco dużo stałych tagów (latest, main, test, boris itp.), rejestr nie będzie pęczniał i przez długi czas w ogóle nie będziesz mógł myśleć o jego czyszczeniu. Ostatecznie wszystkie nieistotne obrazy zostają usunięte i po prostu nie ma już potrzeby ich czyszczenia (wszystko wykonuje zwykły zbieracz śmieci).
Jednak podejście to znacznie ogranicza rozwój i rzadko można je stosować w CI/CD współczesnych projektów. Integralną częścią rozwoju było automatyzacja, co pozwala na testowanie, wdrażanie i dostarczanie użytkownikom nowych funkcjonalności znacznie szybciej. Na przykład we wszystkich naszych projektach proces CI jest tworzony automatycznie przy każdym zatwierdzeniu. Obraz zostaje w nim utworzony, przetestowany, wdrożony w różnych obwodach Kubernetes w celu debugowania i wykonania pozostałych kontroli, a jeśli wszystko jest w porządku, zmiany docierają do użytkownika końcowego. I nie jest to już żadna filozofia, ale dla wielu osób – a najprawdopodobniej dla Ciebie, skoro czytasz ten artykuł – coś oczywistego.
Ponieważ usuwanie błędów i opracowywanie nowych funkcjonalności odbywa się równolegle, a wydania mogą być wydawane kilka razy dziennie, oczywistym jest, że procesowi rozwoju towarzyszy znaczna liczba zatwierdzeń, co oznacza - duża liczba obrazów w rejestrze. W związku z tym pojawia się paląca kwestia zorganizowania skutecznego czyszczenia rejestru, tj. usuwania nieistotnych obrazków.
Ale jak możemy stwierdzić, czy obraz jest istotny?
Kryteria trafności obrazu
W zdecydowanej większości przypadków głównymi kryteriami będą:
1. Pierwszym (najbardziej oczywistym i najważniejszym ze wszystkich) są obrazy, które obecnie używany w Kubernetes. Usunięcie tych obrazów może skutkować znacznym przestojem w produkcji (na przykład obrazy mogą być wymagane do replikacji) lub zniweczyć wysiłki zespołu zajmującego się debugowaniem jednej z pętli. (Z tego powodu zrobiliśmy nawet specjalny , który śledzi brak takich obrazów w dowolnym klastrze Kubernetes.)
2. Drugi (mniej oczywisty, ale również bardzo ważny i znów związany z eksploatacją) – obrazy, które wymagane do wycofania w przypadku wykrycia poważnych problemów w obecnej wersji. Na przykład w przypadku Helm są to obrazy wykorzystane w zapisanych wersjach wydania. (Nawiasem mówiąc, Helm ma domyślny limit 256 rewizji, ale mało prawdopodobne, aby ktokolwiek naprawdę potrzebował zapisywać taki a duża liczba wersji?..) W końcu my, w szczególności, przechowujemy wersje, aby móc z nich później korzystać, tzn. „przywracać” je w razie potrzeby.
3. Trzeci jest Potrzeby programisty:wszystkie obrazy związane z ich obecną pracą. Na przykład, jeśli rozważamy PR, ma sens pozostawić obraz odpowiadający najnowszemu zatwierdzeniu i, powiedzmy, poprzedniemu zatwierdzeniu: w ten sposób programista może szybko powrócić do dowolnego zadania i pracować z najnowszymi zmianami.
4. Czwarta to obrazy, które odpowiadają wersjom naszej aplikacji, czyli są to produkty finalne: v1.0.0, 20.04.01, sierra, itd.
UWAGA: Kryteria określone w tym miejscu zostały sformułowane na podstawie doświadczeń zdobytych we współpracy z kilkudziesięcioma zespołami programistycznymi z różnych firm. Oczywiście, w zależności od specyfiki procesów programistycznych i wykorzystywanej infrastruktury (na przykład nieużywania Kubernetesa), kryteria te mogą się różnić.
Zgodność z kryteriami i istniejącymi rozwiązaniami
Popularne usługi rejestru kontenerów zazwyczaj oferują własne zasady czyszczenia obrazów: można w nich zdefiniować warunki, na podstawie których tag zostanie usunięty z rejestru. Możliwości tych warunków są jednak ograniczone parametrami takimi jak nazwy, czas utworzenia i liczba tagów*.
* Zależy od konkretnej implementacji rejestru kontenerów. Braliśmy pod uwagę następujące rozwiązania: Azure CR, Docker Hub, ECR, GCR, GitHub Packages, GitLab Container Registry, Harbor Registry, JFrog Artifactory, Quay.io — stan na wrzesień 2020 r.
Ten zestaw parametrów w zupełności wystarcza do spełnienia czwartego kryterium, czyli doboru obrazów odpowiadających wersjom. Natomiast w przypadku wszystkich pozostałych kryteriów należy wybierać pewnego rodzaju rozwiązania kompromisowe (politykę twardszą lub odwrotnie – łagodniejszą) - w zależności od oczekiwań i możliwości finansowych.
Przykładowo trzecie kryterium – związane z potrzebami deweloperów – można rozwiązać poprzez zorganizowanie procesów w ramach zespołów: konkretne nazewnictwo obrazów, prowadzenie specjalnych list dozwolonych i wewnętrznych umów. Ale ostatecznie nadal trzeba to zautomatyzować. A jeśli gotowe rozwiązania okażą się niewystarczające, trzeba stworzyć coś własnego.
Podobnie jest w przypadku pierwszych dwóch kryteriów: nie mogą one zostać spełnione bez otrzymania danych z systemu zewnętrznego – tego samego, w którym odbywa się wdrożenie aplikacji (w naszym przypadku jest to Kubernetes).
Ilustracja przepływu pracy w Git
Załóżmy, że pracujesz w Gicie nad czymś takim:

Ikona głowy na diagramie reprezentuje obrazy kontenerów, które są obecnie wdrażane w Kubernetesie dla niektórych użytkowników (użytkowników końcowych, testerów, menedżerów itp.) lub wykorzystywane przez programistów do debugowania i podobnych celów.
Co się stanie, jeśli zasady czyszczenia pozwolą na pozostawienie (nie usuwanie) tylko obrazów według podanych nazw tagów?

Oczywiste jest, że nikt nie byłby zadowolony z takiego scenariusza.
Co się zmieni, jeśli zasady pozwolą na nieusuwanie obrazów według określonego przedziału czasu / liczby ostatnich zatwierdzeń?

Wynik jest znacznie lepszy, ale nadal daleki od ideału. W końcu nadal mamy programistów, którzy potrzebują obrazów w rejestrze (lub nawet wdrożonych na K8) w celu debugowania błędów...
Podsumowując obecną sytuację na rynku: funkcje dostępne w rejestrach kontenerowych nie oferują wystarczającej elastyczności w czyszczeniu, a głównym powodem tego jest brak możliwości interakcji ze światem zewnętrznym. Okazuje się, że zespoły wymagające takiej elastyczności zmuszone są do samodzielnego wdrożenia usuwania obrazów „z zewnątrz”, korzystając z interfejsu API rejestru Docker (lub natywnego interfejsu API odpowiedniej implementacji).
Szukaliśmy jednak uniwersalnego rozwiązania, które zautomatyzowałoby oczyszczanie 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? Chodzi o to, że nie jesteśmy pojedynczą grupą programistów, ale zespołem, który obsługuje wielu z nich naraz, pomagając w kompleksowym rozwiązywaniu problemów CI/CD. A głównym narzędziem technicznym do tego celu jest narzędzie Open Source . Jego unikalną cechą jest to, że nie pełni pojedynczej funkcji, ale towarzyszy ciągłym procesom dostaw na wszystkich etapach: od montażu po wdrożenie.
Publikowanie obrazów w rejestrze* (natychmiast po ich zmontowaniu) jest oczywistą funkcją takiego narzędzia. A ponieważ zdjęcia są tam umieszczane w celach magazynowych, to - jeśli przestrzeń dyskowa nie jest nieograniczona - musisz wziąć na siebie odpowiedzialność za ich późniejsze czyszczenie. Jak udało nam się osiągnąć sukces, spełniając wszystkie podane kryteria, zostanie omówione dalej.
* Mimo że same rejestry mogą się różnić (Docker Registry, GitLab Container Registry, Harbor itp.), ich użytkownicy napotykają te same problemy. Uniwersalne rozwiązanie w naszym przypadku nie zależy od implementacji rejestru, ponieważ działa poza samymi rejestrami i oferuje to samo zachowanie wszystkim.
Chociaż jako przykład implementacji używamy werf, mamy nadzieję, że zastosowane podejścia okażą się przydatne innym zespołom stającym przed podobnymi wyzwaniami.
No więc zabraliśmy się do pracy zewnętrzny implementacja mechanizmu czyszczenia obrazków - zamiast możliwości, które są już wbudowane w rejestry kontenerów. Pierwszym krokiem było użycie interfejsu API rejestru Docker do utworzenia takich samych podstawowych zasad dotyczących liczby tagów i czasu ich utworzenia (o których wspomniano powyżej). Dodano do nich lista dozwolonych obrazów oparta na obrazach używanych w wdrożonej infrastrukturze, tj. Kubernetes. W przypadku tego drugiego wystarczyło przejrzeć wszystkie wdrożone zasoby za pomocą interfejsu API Kubernetes i uzyskać listę wartości image.
To trywialne rozwiązanie rozwiązało najpoważniejszy problem (kryterium nr 1), ale stanowiło dopiero początek naszej drogi ku udoskonaleniu mechanizmu czyszczenia. Następnym – znacznie 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 zawierać informacje niezbędne do czyszczenia i oparliśmy ten proces na schematach tagowania. Podczas publikowania obrazu użytkownik wybrał określoną opcję tagowania (git-branch, git-commit lub git-tag) i użyłem odpowiedniej wartości. W systemach CI wartości te były ustawiane automatycznie na podstawie zmiennych środowiskowych. W istocie ostateczny obraz został połączony z konkretnym prymitywem Git, przechowując niezbędne dane dotyczące czyszczenia na etykietach.
Podejście to zaowocowało zestawem zasad, dzięki którym Git mógł być używany jako jedyne źródło prawdy:
- Po usunięciu gałęzi/tagi w Gicie, powiązane obrazy w rejestrze zostały automatycznie usunięte.
- Liczbę obrazów powiązanych z tagami i zatwierdzeniami Git można kontrolować za pomocą liczby tagów użytych w wybranym schemacie i czasu utworzenia powiązanego zatwierdzenia.
Ogólnie rzecz biorąc, wdrożenie spełniło nasze oczekiwania, lecz wkrótce pojawiło się przed nami nowe wyzwanie. Faktem jest, że w trakcie korzystania ze schematów tagowania bazujących na prymitywach Gitu napotkaliśmy szereg niedociągnięć. (Ponieważ ich opis wykracza poza zakres tego artykułu, każdy zainteresowany może zapoznać się ze szczegółami .) W związku z tym, po podjęciu decyzji o przejściu na bardziej efektywne podejście do tagowania (tagowanie oparte na treści), musieliśmy ponownie rozważyć wdrożenie oczyszczania obrazów.
Nowy algorytm
Dlaczego? Dzięki tagowaniu opartemu na treści każdy tag może odpowiadać wielu zatwierdzeniom w Gicie. Podczas czyszczenia obrazów nie można już wyjść tylko od 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 na meta-obrazach, z których każdy przechowuje pakiet:
- zatwierdzenie, na podstawie którego wykonano publikację (nie ma znaczenia, czy obraz w rejestrze kontenerów został dodany, zmieniony czy pozostał taki sam);
- oraz nasz wewnętrzny identyfikator odpowiadający zebranemu obrazowi.
Innymi słowy, 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 definiowana przez:
- zestaw odniesień, tj. tagi Git lub gałęzie Git, które są używane podczas skanowania;
- i limitu żądanych obrazów dla każdego odniesienia z zestawu.
Dla zobrazowania poniżej przedstawiono domyślną konfigurację 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:
- Przechowuj obraz dla ostatnich 10 tagów Git (według daty utworzenia tagu).
- Nie przechowuj więcej niż 2 zdjęć opublikowanych w ostatnim tygodniu dla nie więcej niż 10 oddziałów, które były aktywne w ostatnim tygodniu.
- Zapisz 10 obrazów na gałąź
main,stagingиproduction.
Ostateczny algorytm sprowadza się do następujących kroków:
- Pobieranie manifestów z rejestru kontenerów.
- Wykluczono obrazy używane w Kubernetes, ponieważ zostały już wstępnie wyselekcjonowane poprzez zapytanie do interfejsu API K8s.
- Przeskanuj historię Git i wyklucz obrazy na podstawie określonych zasad.
- Usuwanie pozostałych obrazów.
Wracając do naszej ilustracji, oto co się dzieje z werfem:

Jednak nawet jeśli nie używasz werf, podobne podejście do zaawansowanego czyszczenia obrazu — w jednej lub drugiej implementacji (w zależności od preferowanego podejścia do tagowania obrazu) — można zastosować także w innych systemach/narzędziach. Aby tego dokonać, wystarczy zapamiętać pojawiające się problemy i znaleźć w swoim stosie możliwości, które pozwolą na najsprawniejszą integrację ich rozwiązania. Mamy nadzieję, że droga, którą wybraliśmy, pomoże Państwu spojrzeć na Wasz konkretny przypadek z nowej perspektywy i przemyśleć go.
wniosek
- Wcześniej czy później większość zespołów spotyka się z problemem przepełnienia rejestru.
- Przy poszukiwaniu rozwiązań należy przede wszystkim ustalić kryteria trafności obrazu.
- Narzędzia oferowane przez popularne usługi rejestru kontenerów umożliwiają bardzo proste czyszczenie, które nie bierze pod uwagę „świata zewnętrznego”: obrazów używanych w Kubernetes i specyfiki przepływów pracy zespołu.
- Elastyczny i wydajny algorytm musi rozumieć procesy CI/CD i działać nie tylko na danych obrazu Dockera.
PS
Przeczytaj także na naszym blogu:
- «";
- «";
- «";
- «".
Źródło: www.habr.com
