Das Problem der „intelligenten“ Reinigung von Containerbildern und seine Lösung in werf

Das Problem der „intelligenten“ Reinigung von Containerbildern und seine Lösung in werf

Der Artikel erörtert die Probleme beim Bereinigen von Bildern, die sich in Containerregistern (Docker Registry und seine Analoga) ansammeln, in der Realität moderner CI/CD-Pipelines für Cloud-native Anwendungen, die an Kubernetes geliefert werden. Die Hauptkriterien für die Relevanz von Bildern und die daraus resultierenden Schwierigkeiten bei der Automatisierung der Reinigung, der Platzeinsparung und der Erfüllung der Bedürfnisse von Teams werden angegeben. Abschließend zeigen wir Ihnen am Beispiel eines konkreten Open-Source-Projekts, wie diese Schwierigkeiten überwunden werden können.

Einführung

Die Anzahl der Bilder in einer Containerregistrierung kann schnell wachsen, mehr Speicherplatz beanspruchen und damit die Kosten erheblich erhöhen. Um ein akzeptables Wachstum des im Register belegten Speicherplatzes zu kontrollieren, zu begrenzen oder aufrechtzuerhalten, wird Folgendes akzeptiert:

  1. Verwenden Sie eine feste Anzahl von Tags für Bilder.
  2. Bereinigen Sie die Bilder auf irgendeine Weise.


Die erste Einschränkung ist manchmal für kleine Teams akzeptabel. Wenn Entwickler über genügend permanente Tags verfügen (latest, main, test, boris usw.), wird die Registry nicht an Größe anschwellen und Sie müssen lange Zeit überhaupt nicht daran denken, sie zu bereinigen. Schließlich werden alle irrelevanten Bilder gelöscht und es bleibt einfach keine Arbeit mehr für die Reinigung übrig (alles wird von einem normalen Müllsammler erledigt).

Allerdings schränkt dieser Ansatz die Entwicklung stark ein und ist selten auf moderne CI/CD-Projekte anwendbar. Ein wesentlicher Bestandteil der Entwicklung war Automatisierung, wodurch Sie neue Funktionen viel schneller testen, bereitstellen und den Benutzern bereitstellen können. Beispielsweise wird in allen unseren Projekten bei jedem Commit automatisch eine CI-Pipeline erstellt. Darin wird das Image zusammengestellt, getestet, zum Debuggen und für verbleibende Überprüfungen auf verschiedene Kubernetes-Schaltkreise verteilt, und wenn alles in Ordnung ist, erreichen die Änderungen den Endbenutzer. Und das ist kein Hexenwerk mehr, sondern für viele ein alltägliches Ereignis – höchstwahrscheinlich auch für Sie, da Sie diesen Artikel lesen.

Da das Beheben von Fehlern und die Entwicklung neuer Funktionen parallel erfolgen und Releases mehrmals am Tag durchgeführt werden können, ist es offensichtlich, dass der Entwicklungsprozess mit einer erheblichen Anzahl von Commits einhergeht eine große Anzahl von Bildern in der Registrierung. Infolgedessen stellt sich die Frage der Organisation einer effektiven Bereinigung des Registers, d. h. Entfernen irrelevanter Bilder.

Doch wie stellt man überhaupt fest, ob ein Bild relevant ist?

Kriterien für die Relevanz des Bildes

In den allermeisten Fällen sind die Hauptkriterien:

1. Das erste (das offensichtlichste und kritischste von allen) sind die Bilder, die Wird derzeit in Kubernetes verwendet. Das Entfernen dieser Bilder kann zu erheblichen Kosten für Produktionsausfälle führen (die Bilder sind beispielsweise möglicherweise für die Replikation erforderlich) oder den Aufwand des Teams für das Debuggen in einer der Schleifen zunichte machen. (Aus diesem Grund haben wir sogar ein Special gemacht Prometheus-Exporteur, der das Fehlen solcher Bilder in jedem Kubernetes-Cluster verfolgt.)

2. Zweitens (weniger offensichtlich, aber auch sehr wichtig und bezieht sich wiederum auf Ausbeutung) – Bilder, die Wird für das Rollback benötigt, wenn schwerwiegende Probleme festgestellt werden in der aktuellen Version. Im Fall von Helm handelt es sich beispielsweise um Bilder, die in gespeicherten Versionen der Version verwendet werden. (Übrigens liegt das Limit in Helm standardmäßig bei 256 Revisionen, aber es ist unwahrscheinlich, dass jemand wirklich speichern muss wie eine große Anzahl von Versionen?..) Schließlich speichern wir gerade Versionen, um sie später verwenden zu können, d. h. bei Bedarf einen „Rollback“ durchführen.

3. Dritter - Entwicklerbedürfnisse: Alle Bilder, die einen Bezug zu ihrer aktuellen Arbeit haben. Wenn wir beispielsweise eine PR in Betracht ziehen, ist es sinnvoll, ein Image zu hinterlassen, das dem letzten Commit und beispielsweise dem vorherigen Commit entspricht: Auf diese Weise kann der Entwickler schnell zu jeder Aufgabe zurückkehren und mit den neuesten Änderungen arbeiten.

4. Viertens – Bilder, die entsprechen den Versionen unserer Anwendung, d.h. sind das Endprodukt: v1.0.0, 20.04.01, Sierra usw.

Hinweis: Die hier definierten Kriterien wurden auf der Grundlage von Erfahrungen aus der Interaktion mit Dutzenden von Entwicklungsteams verschiedener Unternehmen formuliert. Abhängig von den Besonderheiten der Entwicklungsprozesse und der verwendeten Infrastruktur (z. B. wird kein Kubernetes verwendet) können diese Kriterien jedoch natürlich abweichen.

Berechtigung und bestehende Lösungen

Beliebte Dienste mit Container-Registrierungen bieten in der Regel eigene Richtlinien zur Bildbereinigung an: In ihnen können Sie die Bedingungen definieren, unter denen ein Tag aus der Registrierung entfernt wird. Diese Bedingungen werden jedoch durch Parameter wie Namen, Erstellungszeit und Anzahl der Tags* begrenzt.

* Hängt von bestimmten Container-Registry-Implementierungen ab. Wir haben die Möglichkeiten der folgenden Lösungen geprüft: Azure CR, Docker Hub, ECR, GCR, GitHub Packages, GitLab Container Registry, Harbor Registry, JFrog Artifactory, Quay.io – Stand September 2020.

Dieser Parametersatz reicht völlig aus, um das vierte Kriterium zu erfüllen – also Bilder auszuwählen, die den Versionen entsprechen. Bei allen anderen Kriterien muss man sich jedoch je nach Erwartungen und finanziellen Möglichkeiten für eine Art Kompromisslösung (eine härtere oder umgekehrt mildere Politik) entscheiden.

Beispielsweise kann das dritte Kriterium – bezogen auf die Bedürfnisse von Entwicklern – durch die Organisation von Prozessen innerhalb von Teams gelöst werden: spezifische Benennung von Bildern, Pflege spezieller Zulassungslisten und interne Vereinbarungen. Letztendlich muss es aber noch automatisiert werden. Und wenn die Fähigkeiten vorgefertigter Lösungen nicht ausreichen, müssen Sie etwas Eigenes tun.

Ähnlich verhält es sich mit den ersten beiden Kriterien: Sie können nicht erfüllt werden, ohne Daten von einem externen System zu erhalten – dem System, in dem Anwendungen bereitgestellt werden (in unserem Fall Kubernetes).

Illustration des Workflows in Git

Nehmen wir an, Sie arbeiten in Git etwa so:

Das Problem der „intelligenten“ Reinigung von Containerbildern und seine Lösung in werf

Das Symbol mit Kopf im Diagramm weist auf Container-Images hin, die derzeit in Kubernetes für beliebige Benutzer (Endbenutzer, Tester, Manager usw.) bereitgestellt werden oder von Entwicklern für Debugging- und ähnliche Zwecke verwendet werden.

Was passiert, wenn Bereinigungsrichtlinien nur das Beibehalten (nicht Löschen) von Bildern zulassen? durch gegebene Tag-Namen?

Das Problem der „intelligenten“ Reinigung von Containerbildern und seine Lösung in werf

Offensichtlich wird ein solches Szenario niemanden glücklich machen.

Was ändert sich, wenn die Richtlinien zulassen, dass Bilder nicht gelöscht werden? entsprechend einem bestimmten Zeitintervall/Anzahl der letzten Commits?

Das Problem der „intelligenten“ Reinigung von Containerbildern und seine Lösung in werf

Das Ergebnis ist deutlich besser geworden, aber noch lange nicht optimal. Schließlich gibt es immer noch Entwickler, die Bilder in der Registrierung benötigen (oder sogar in K8s bereitgestellt werden), um Fehler zu beheben ...

Um die aktuelle Marktsituation zusammenzufassen: Die in Containerregistern verfügbaren Funktionen bieten nicht genügend Flexibilität bei der Reinigung, und der Hauptgrund dafür ist keine Möglichkeit, mit der Außenwelt zu interagieren. Es stellt sich heraus, dass Teams, die eine solche Flexibilität benötigen, gezwungen sind, das Löschen von Bildern unabhängig „von außen“ mithilfe der Docker Registry API (oder der nativen API der entsprechenden Implementierung) zu implementieren.

Wir waren jedoch auf der Suche nach einer universellen Lösung, die die Bildbereinigung für verschiedene Teams mithilfe unterschiedlicher Register automatisiert.

Unser Weg zur universellen Bildreinigung

Woher kommt dieses Bedürfnis? Tatsache ist, dass wir keine separate Gruppe von Entwicklern sind, sondern ein Team, das viele von ihnen gleichzeitig betreut und dabei hilft, CI/CD-Probleme umfassend zu lösen. Und das wichtigste technische Werkzeug dafür ist das Open-Source-Dienstprogramm Hof. Seine Besonderheit besteht darin, dass es keine einzelne Funktion übernimmt, sondern kontinuierliche Lieferprozesse in allen Phasen begleitet: von der Montage bis zum Einsatz.

Das Veröffentlichen von Bildern in der Registrierung* (unmittelbar nach ihrer Erstellung) ist eine offensichtliche Funktion eines solchen Dienstprogramms. Und da die Bilder dort zur Speicherung abgelegt werden, müssen Sie – wenn Ihr Speicherplatz nicht unbegrenzt ist – für die anschließende Reinigung verantwortlich sein. Wie es uns dabei gelungen ist, alle genannten Kriterien zu erfüllen, wird weiter besprochen.

* Obwohl die Registrys selbst unterschiedlich sein können (Docker Registry, GitLab Container Registry, Harbor usw.), stehen ihre Benutzer vor den gleichen Problemen. Die universelle Lösung hängt in unserem Fall nicht von der Implementierung der Registrierung ab, denn läuft außerhalb der Registrys selbst und bietet für alle das gleiche Verhalten.

Obwohl wir werf als Beispielimplementierung verwenden, hoffen wir, dass die verwendeten Ansätze für andere Teams nützlich sein werden, die mit ähnlichen Schwierigkeiten konfrontiert sind.

Also machten wir uns an die Arbeit extern Implementierung eines Mechanismus zum Bereinigen von Bildern – anstelle der Funktionen, die bereits in Register für Container integriert sind. Der erste Schritt bestand darin, die Docker Registry API zu verwenden, um dieselben primitiven Richtlinien für die Anzahl der Tags und den Zeitpunkt ihrer Erstellung (oben erwähnt) zu erstellen. Hinzugefügt Zulassungsliste basierend auf Bildern, die in der bereitgestellten Infrastruktur verwendet werden, d.h. Kubernetes. Für Letzteres reichte es aus, die Kubernetes-API zu verwenden, um alle bereitgestellten Ressourcen zu durchlaufen und eine Werteliste zu erhalten image.

Diese triviale Lösung löste das kritischste Problem (Kriterium Nr. 1), war aber nur der Anfang unserer Reise zur Verbesserung des Reinigungsmechanismus. Der nächste – und viel interessantere – Schritt war die Entscheidung Verknüpfen Sie veröffentlichte Bilder mit dem Git-Verlauf.

Tagging-Schemata

Zunächst wählten wir einen Ansatz, bei dem das endgültige Bild die für die Reinigung notwendigen Informationen speichern sollte, und bauten den Prozess auf Tagging-Schemata auf. Beim Veröffentlichen eines Bildes hat der Benutzer eine bestimmte Tagging-Option ausgewählt (git-branch, git-commit oder git-tag) und den entsprechenden Wert verwendet. In CI-Systemen wurden diese Werte automatisch basierend auf Umgebungsvariablen festgelegt. Tatsächlich Das endgültige Bild war einem bestimmten Git-Grundelement zugeordnet, Speicherung der für die Reinigung notwendigen Daten in Etiketten.

Dieser Ansatz führte zu einer Reihe von Richtlinien, die es ermöglichten, Git als einzige Quelle der Wahrheit zu verwenden:

  • Beim Löschen eines Zweigs/Tags in Git wurden die zugehörigen Bilder in der Registrierung automatisch gelöscht.
  • Die Anzahl der mit Git-Tags und Commits verknüpften Bilder kann durch die Anzahl der im ausgewählten Schema verwendeten Tags und den Zeitpunkt gesteuert werden, zu dem der zugehörige Commit erstellt wurde.

Im Großen und Ganzen erfüllte die resultierende Implementierung unsere Anforderungen, doch schon bald erwartete uns eine neue Herausforderung. Tatsache ist, dass wir bei der Verwendung von Tagging-Schemata, die auf Git-Primitiven basieren, auf eine Reihe von Mängeln gestoßen sind. (Da ihre Beschreibung den Rahmen dieses Artikels sprengen würde, kann sich jeder mit den Details vertraut machen hier.) Nachdem wir uns entschieden hatten, auf einen effizienteren Tagging-Ansatz (inhaltsbasiertes Tagging) umzusteigen, mussten wir die Implementierung der Bildbereinigung überdenken.

Neuer Algorithmus

Warum? Mit inhaltsbasiertem Tagging kann jedes Tag mehrere Commits in Git erfüllen. Beim Reinigen von Bildern können Sie nicht mehr davon ausgehen nur aus dem Commit, bei dem das neue Tag zur Registrierung hinzugefügt wurde.

Für den neuen Reinigungsalgorithmus wurde beschlossen, von Tagging-Schemata abzuweichen und diese zu erstellen Metabildprozess, von denen jedes eine Reihe von Folgendem speichert:

  • das Commit, bei dem die Veröffentlichung durchgeführt wurde (es spielt keine Rolle, ob das Bild hinzugefügt, geändert oder in der Container-Registrierung gleich geblieben ist);
  • und unsere interne Kennung, die dem zusammengesetzten Bild entspricht.

Mit anderen Worten: Es wurde bereitgestellt Verknüpfen veröffentlichter Tags mit Commits in Git.

Endgültige Konfiguration und allgemeiner Algorithmus

Beim Konfigurieren der Bereinigung haben Benutzer jetzt Zugriff auf Richtlinien, die aktuelle Bilder auswählen. Jede dieser Richtlinien ist wie folgt definiert:

  • viele Referenzen, d.h. Git-Tags oder Git-Zweige, die beim Scannen verwendet werden;
  • und die Grenze der gesuchten Bilder für jede Referenz aus dem Satz.

Zur Veranschaulichung: So sah die Standardrichtlinienkonfiguration aus:

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

Diese Konfiguration enthält drei Richtlinien, die den folgenden Regeln entsprechen:

  1. Speichern Sie das Bild für die letzten 10 Git-Tags (nach Tag-Erstellungsdatum).
  2. Speichern Sie nicht mehr als 2 in der letzten Woche veröffentlichte Bilder für nicht mehr als 10 Threads mit Aktivität in der letzten Woche.
  3. Speichern Sie 10 Bilder für Zweige main, staging и production.

Der endgültige Algorithmus läuft auf die folgenden Schritte hinaus:

  • Abrufen von Manifesten aus der Containerregistrierung.
  • Ausgenommen Bilder, die in Kubernetes verwendet werden, weil Wir haben sie bereits durch Abfrage der K8s-API vorab ausgewählt.
  • Scannen des Git-Verlaufs und Ausschließen von Bildern basierend auf angegebenen Richtlinien.
  • Verbleibende Bilder werden entfernt.

Zurück zu unserer Illustration: Folgendes passiert mit werf:

Das Problem der „intelligenten“ Reinigung von Containerbildern und seine Lösung in werf

Selbst wenn Sie werf nicht verwenden, kann ein ähnlicher Ansatz zur erweiterten Bildbereinigung – in der einen oder anderen Implementierung (je nach dem bevorzugten Ansatz zur Bildkennzeichnung) – auf andere Systeme/Dienstprogramme angewendet werden. Dazu reicht es aus, sich an die auftretenden Probleme zu erinnern und in Ihrem Stack die Möglichkeiten zu finden, die es Ihnen ermöglichen, ihre Lösung so reibungslos wie möglich zu integrieren. Wir hoffen, dass der von uns eingeschlagene Weg Ihnen dabei hilft, Ihren konkreten Fall mit neuen Details und Gedanken zu betrachten.

Abschluss

  • Früher oder später stoßen die meisten Teams auf das Problem eines Registrierungsüberlaufs.
  • Bei der Suche nach Lösungen müssen zunächst die Kriterien für die Relevanz des Bildes ermittelt werden.
  • Mit den von beliebten Container-Registrierungsdiensten angebotenen Tools können Sie eine sehr einfache Bereinigung organisieren, die die „Außenwelt“ nicht berücksichtigt: die in Kubernetes verwendeten Bilder und die Besonderheiten der Arbeitsabläufe des Teams.
  • Ein flexibler und effizienter Algorithmus muss Verständnis für CI/CD-Prozesse haben und nicht nur mit Docker-Bilddaten arbeiten.

PS

Lesen Sie auch auf unserem Blog:

Source: habr.com

Kommentar hinzufügen