Cloudogu Logo

Hallo, wir sind Cloudogu!

Experten für Software-Lifecycle-Management und Prozess­auto­mati­sierung, Förderer von Open-Source-Soft­ware und Entwickler des Cloudogu EcoSystem.

featured image GitOps-Repository-Strukturen und -Patterns Teil 6: Beispiel-Repositories
11.10.2023 in DevOps

GitOps-Repository-Strukturen und -Patterns Teil 6: Beispiel-Repositories


Johannes Schnatterer
Johannes Schnatterer

Technical Lead


In diesem letzten Teil der GitOps-Repository-Strukturen und -Patterns-Serie zeige ich Beispiel-Repositories, die Vorlagen, Ideen und Tipps für eigene Projekte liefern. Dabei zeigen sich einige wiederkehrende Themen, die teils unterschiedlich benannt werden: Strukturen für Anwendungen oder Teams, Strukturen für cluster-weite Ressourcen und Strukturen fürs Bootstrapping.

Die Beispiele zeigen auch, dass grundsätzlich kaum Unterschiede zwischen Argo CD und Flux bei Strukturen notwendig sind. Diese beschränken sich auf Bootstrapping und Linking. Da Kustomize mittels kustomization.yaml sowohl von Argo CD als auch von Flux verstanden wird, stellt es sich als Operator-agnostisches Werkzeug heraus.

Eine Einführung in die Thematik der GitOps-Repository-Patterns und -Strukturen bekommen Sie im ersten Teil dieser Serie, im zweiten Teil stelle ich Operator-Deployment-Patterns vor, im dritten Teil Repository Patterns, im vierten Teil Promotion Patterns und im fünften Teil Verdrahtungs-Patterns.

Beispiel 1: Argo CD Autopilot

  • Repo pattern: Monorepo
  • Operator pattern: „Instance per Cluster“ oder „Hub and Spoke“
  • Operator: Argo CD
  • Boostrapping: argocd-autopilot CLI
  • Linking: Application, ApplicationSet, Kustomize
  • Features:
    • Automatische Erzeugung der Struktur und YAML via CLI
    • Argo CD selbst per GitOps verwalten
    • Lösung für cluster-weite Ressourcen
  • Quelle: argoproj-labs/argocd-autopilot

Repo-Struktur bei argocd-autopilot Abbildung 1: Repo-Struktur bei argocd-autopilot

Argo CD autopilot ist ein Kommandozeilen-Werkzeug (Command Line Interface, CLI), das die Installation und den Einstieg in Argo CD vereinfachen soll. Dazu bietet es die Möglichkeit, das Bootstrapping von Argo CD im Cluster sowie das Anlegen von Repo-Strukturen durchzuführen.

Das Bootstrapping von Argo CD erfolgt mit einem einzigen Befehl: argocd-autopilot repo bootstrap. Um mit der daraus resultierenden Struktur auch Anwendungen zu deployen, sind zudem ein AppProject (Command project create) und eine Application (Command app create) notwendig. Abbildung 1 zeigt die daraus resultierende Repo-Struktur. Diese Struktur ist bei GitHub unter schnatterer/argocd-autopilot-example einsehbar. Die Zusammenhänge werden im Folgenden anhand der Nummern in der Abbildung beschrieben:

  1. Die Application autopilot-bootstrap verwaltet den Ordner bootstrap und bindet damit alle anderen Applications in dieser Aufzählung ein. Sie selbst steht nicht unter Versionsverwaltung, sondern wird imperativ beim Bootstrapping an den Cluster übermittelt.
  2. Die Application argo-cd verwaltet Argo CD selbst per GitOps.
  3. Dazu enthält sie eine Kustomization, die weitere Ressourcen aus dem Internet einbezieht. Sie verweist direkt auf eine Kustomization im Repo von autopilot, die wiederum alle zur Installation von Argo CD notwendigen Ressourcen aus dem Repo von Argo CD selbst holt. Dabei verweist sie auf den stable Branch von Argo CD.
  4. Das ApplicationSet cluster-resources referenziert mittels git-Generator für Dateien alle JSON-Dateien unter dem Pfad bootstrap/cluster-resources/. Damit kann man cluster-weite Ressourcen verwalten, wie beispielsweise Namespaces, die von mehreren Applications genutzt werden. Standardmäßig liegt hier nur die Datei in-cluster.json, die Werte für die Variablen name und server enthält. Im Template des ApplicationSets werden diese Variablen eingesetzt, sodass eine Application entsteht, welche die Manifeste unterhalb von bootstrap/cluster-resources/in-cluster/ referenziert. Dadurch entsteht der Namespace argocd in dem Cluster, in dem Argo CD deployt ist. Dies eignet sich für das „Instance per Cluster“ Pattern, ist aber erweiterbar auf weitere Cluster, um das Hub and Spoke Pattern zu implementieren.
  5. Die Application root ist dafür zuständig, alle AppProjects und Applications einzubinden, die unterhalb von projects/ angelegt werden. Nach dem Ausführen des Commands bootstrap ist dieser Ordner noch leer.
  6. Bei jeder Ausführung des project Commands werden ein AppProject und zugehöriges ApplicationSet in einer Datei generiert. Diese sind für die Umsetzung verschiedener Environments gedacht. Das ApplicationSet referenziert mittels git-Generator für Dateien alle config.json Dateien, die in Unterordnern des Ordners apps für das jeweilige Environment liegen, beispielsweise apps/my-app/overlays/staging/config.json. Allerdings ist der Ordner apps initial leer und es werden zunächst keine Applications generiert.
  7. Durch Ausführung des Commands app wird der Ordner apps mit der Struktur für eine Application in einem Environment befüllt. Dazu gehören die im letzten Punkt beschriebene config.json, mittels der das im Ordner projects liegende ApplicationSet eine Application erzeugt, die den Ordner selbst deployt, also beispielsweise apps/my-app/overlays/staging. Mittels dieses Ordners kann Config deployt werden, die spezifisch für ein Environment ist.
  8. Zusätzlich wird eine kustomization.yaml erzeugt, die auf den Ordner base zeigt. Mittels dieses Ordners kann Config deployt werden, die in allen Environments gleich ist. Durch diese Aufteilung wird redundante Config vermieden.

Analog zu 6. bis 8. können weitere Environments hinzugefügt werden. Abbildung 1 zeigt hier stellvertretend einen Unterordner production im Ordner apps und eine YAML-Datei in projects.

Abschließend soll erwähnt werden, dass es Gründe gibt, die zur Vorsicht bei der Verwendung von Autopilot in der Produktion raten. Das Projekt bezeichnet sich selbst nicht als stabil, es liegt noch in einer Version „0.x“ vor. Es ist auch nicht Teil der offiziellen „argoproj“ Organisation bei GitHub, sondern liegt unter „argoproj-labs“. Die Commits kommen hauptsächlich von einem Unternehmen: Codefresh. Es ist also denkbar, dass das Projekt eingestellt wird oder Breaking Changes auftreten. Damit ist eine Verwendung in der Produktion nicht ratsam.

Standardmäßig ist außerdem die Version von Argo CD nicht gepinnt. Stattdessen verweist die kustomization.yaml (3. in Abbildung 1) schlussendlich auf den stable-Branch des Argo-CD-Repos. Hier empfehlen wir, per Kustomize eine deterministische Version zu referenzieren. Eine nicht-deterministische Version schreit nach Problemen: Upgrades von Argo CD könnten unbemerkt stattfinden. Wie sieht es bei Breaking Changes in Argo CD aus? Welche Version stellt man im Disaster Recovery Fall wieder her?

Die Repository-Struktur, die der Autopilot erzeugt, ist kompliziert, d.h. schwierig zu verstehen und zu warten. Das Ausmaß an Konzentration, das zum Verständnis von Abbildung 1 und der zugehörigen Beschreibung nötig ist, spricht dabei schon eine deutliche Sprache. Dazu kommen noch weniger offensichtliche Themen: Warum befindet sich die Anwendung autopilot-bootstrap (1. in Abbildung 1) nicht im GitOps-Repository, sondern nur im Cluster? Der Ansatz eines ApplicationSet innerhalb des AppProject’s YAML, das auf eine config.json zeigt, ist schwer zu verstehen (4. und 6. in Abbildung 1). Dazu kommt die Mischung von YAML und JSON. Das cluster-resources ApplicationSet ist generell ein gut skalierbarer Ansatz für die Verwaltung mehrerer Cluster über das Hub and Spoke Pattern. Doch auch hier muss JSON geschrieben werden (4. in Abbildung 1).

Der Autopilot modelliert Environments über Argo CD Projects (6. und 7. in Abbildung 1). Wie wäre bei dieser Monorepo-Struktur eine Trennung unterschiedlicher Entwickler-Teams realisierbar? Eine Idee wäre die Verwendung mehrerer Argo CD-Instanzen nach dem „Instance per Cluster“ Pattern. Dabei müsste jedes Team seine Argo CD-Instanz selbst verwalten.

Viele Organisationen lagern solche Aufgaben gerne an Plattform-Teams aus und implementieren ein Repo per Team Pattern. Dies ist mit dem Autopilot nicht intuitiv. Das 2. Beispiel zeigt hierfür eine Alternative auf.

Beispiel 2: GitOps Playground

  • Repo pattern: „Repo per team“ gemischt mit „Repo per app“
  • Operator pattern: Instance per Cluster („Hub and Spoke“ auch möglich)
  • Operator: Argo CD (Flux auch möglich)
  • Boostrapping: helm, kubectl
  • Linking: Application
  • Features:
    • Argo CD selbst per GitOps verwalten
    • Lösung für cluster-weite Ressourcen
    • Mandantentrennung: Zentraler Operator für mehrere Teams, auf einem Cluster mit Namespace-Environments (auch mehrere Cluster möglich)
    • Env per app Pattern
    • Config Update und Config Replication via CI-Server
    • Gemischte repo patterns
    • Beispiele für Argo CD und Flux
  • Quelle: cloudogu/gitops-playground

Zusammenhang der GitOps-Repos im GitOps Playground (Argo CD) Abbildung 2: Zusammenhang der GitOps-Repos im GitOps Playground (Argo CD)

Der GitOps Playground stellt ein OCI-Image bereit, mit dem ein Kubernetes Cluster mit allem provisioniert werden kann, was für den Betrieb mittels GitOps nötig ist und veranschaulicht dies mittels Beispiel-Applikationen. Zu den installierten Tools gehören GitOps-Operator, Git-Server, Monitoring und Secrets Management. Beim GitOps-Operator hat man die Wahl zwischen Argo CD und Flux. Im Folgenden fokussieren wir auf Argo CD, da es (anders als Flux) selbst keine Vorschläge zur Repo-Struktur macht. Außerdem gibt es weniger öffentliche Beispiele für Repo-Strukturen mit Argo CD, die reif für die Produktion sind.

Im GitOps Playground wird Argo CD so installiert, dass es sich selbst per GitOps betreiben kann. Außerdem wird ein „Repo per Team“ Pattern gemischt mit einem „Repo per App“ Pattern umgesetzt. Abbildung 2 zeigt, wie die GitOps-Repos verdrahtet sind.

Der GitOps Playground führt für das Bootstrapping von Argo CD bei der Installation einige imperative Schritte einmalig durch. Dabei werden drei Repos erstellt und initialisiert:

  • argocd (Verwaltung und Konfiguration von Argo CD selbst)
  • example-apps (Beispiel für das GitOps-Repository eines Entwickler-/Applikations-Teams) und
  • cluster-resources (Beispiel für das GitOps-Repo eines Cluster-Administrators oder eines Infra-/Plattform-Teams)

Argo CD wird einmalig mittels eines Helm-Charts installiert. Hier wird intern helm template verwendet. Eine Alternative wäre die Verwendung von helm install oder helm upgrade -i. Danach sollten allerdings die Secrets, in denen Helm seinen Zustand verwaltet, gelöscht werden. Argo CD nutzt diese nicht, insofern würden sie veralten und nur für Verwirrung sorgen.

Um das Bootstrapping abzuschließen werden außerdem zwei Ressourcen imperativ auf den Cluster angewendet: ein AppProject namens argocd und eine Application namens bootstrap. Diese sind ebenfalls im argocd-Repository enthalten.

Von dort aus wird alles über GitOps verwaltet. Im Folgenden werden die Zusammenhänge anhand der Nummern in der Abbildung beschrieben:

  1. Die Application bootstrap verwaltet den Ordner applications, der auch bootstrap selbst enthält. Damit können Änderungen an bootstrap über GitOps vorgenommen werden. Mittels bootstrap werden weitere andere Anwendungen deployt (App-of-Apps-Pattern).
  2. Die Application argocd verwaltet den Ordner argocd, der die Ressourcen von Argo CD als ein Umbrella Helm Chart enthält. Dabei enthält die values.yaml die eigentlichen Config der Argo CD-Instanz. Zusätzliche Ressourcen (beispielsweise Secrets und Ingresses) können über den Ordner template deployt werden. Das eigentliche Argo CD-Chart wird in der Chart.yaml deklariert.
  3. Die Chart.yaml enthält das Argo CD Helm Chart als Dependency. Sie verweist auf eine deterministische Version des Diagramms (gepinnt per Chart.lock), die aus dem Chart-Repository im Internet gezogen wird. Dieser Mechanismus kann verwendet werden, um Argo CD per GitOps zu aktualisieren.
  4. Die Application projects verwaltet den Ordner projects, der wiederum die folgenden AppProjects enthält:
    • argocd, das für das Bootstrapping verwendet wird,
    • das in Argo CD fest verbaute default (dessen Rechte gegenüber dem Standardverhalten beschränkt werden, um die Angriffsfläche zu reduzieren (siehe Argo CD End User Threat Model)),
    • ein AppProject pro Team (zur Implementierung von Least Privilege und Notifications pro Team):
      • cluster-resources (für Plattform-Admins, benötigt mehr Rechte auf dem Cluster) und
      • example-apps (für Entwickler, benötigt weniger Rechte auf dem Cluster)
  5. Die Application cluster-resources verweist im Repo cluster-resources auf den Ordner argocd. Dieses Repo hat die typische Ordnerstruktur eines GitOps-Repos (wird im nächsten Schritt erklärt). Auf diese Weise nutzen Administratoren GitOps auf die gleiche Weise wie ihre „Kunden“ (die Entwickler) und können so besseren Support leisten.
  6. Die Application example-apps verweist im Repo example-apps auf den Ordner argocd. Wie die cluster-resources hat es auch die typische Ordnerstruktur eines GitOps-Repos:
  7. apps - enthält die Kubernetes-Ressourcen aller Anwendungen (das eigentliche YAML)
  8. argocd - enthält Argo CD Applications, die auf Unterordner von apps verweisen (App Of Apps Pattern)
  9. misc - enthält Kubernetes-Ressourcen, die nicht zu bestimmten Anwendungen gehören (beispielsweise Namespaces und RBAC)
  10. Die Application misc zeigt auf den Ordner misc
  11. Die Application my-app-staging verweist auf den Ordner apps/my-app/staging innerhalb desselben Repos. Dies bietet eine Ordnerstruktur für die Promotion. Die Applications mit dem Präfix my-app- implementieren das „Environment per App“ Pattern. Dieses ermöglicht es jeder Anwendung, individuelle Environments zu verwenden, z.B. production und staging oder gar keine. Das eigentliche YAML kann hier entweder manuell oder automatisiert gepusht werden. Der GitOps Playground enthält Beispiele, die das Config Update per CI-Server auf Basis eines App-Repos realisieren. Dieses Vorgehen ist ein Beispiel für die Mischung der „Repo per Team“ und „Repo per App“ Patterns.
  12. Die zugehörige Produktionsumgebung wird über die Anwendung my-app-production realisiert, die auf den Ordner apps/my-app/production innerhalb desselben Repos verweist. Generell ist es empfehlenswert, alle production-Ordner vor manuellem Zugriff zu schützen, wenn dies seitens des verwendeten SCM möglich ist (beispielsweise mit SCM-Manager). Anstelle der im Diagramm verwendeten unterschiedlichen YAML-Dateien könnten diese Applications auch wie folgt realisiert werden
  13. Zwei Applications in derselben YAML
  14. Zwei Applications mit dem gleichen Namen in verschiedenen Kubernetes Namespaces. Voraussetzung dafür ist, dass diese Namespaces in Argo CD konfiguriert sind.
  15. Ein ApplicationSet, das den git-Generator für Ordner verwendet.

Der GitOps Playground selbst verwendet zur Vereinfachung einen einzelnen Kubernetes-Cluster und implementiert damit das „Instance per Cluster“-Pattern. Die gezeigte Repo-Struktur kann jedoch auch für mehrere Cluster nach dem „Hub and Spoke“ Pattern verwendet werden: Zusätzliche Cluster können entweder in der vaules.yaml oder als Secrets mittels des templates Ordners definiert werden.

Beispiel 3: Flux Monorepo

  • Repo pattern: Monorepo
  • Operator pattern: Instance per Cluster
  • Operator: Flux (wäre ähnlich mit Argo CD umsetzbar)
  • Boostrapping: flux CLI
  • Linking: Flux Kustomization, Kustomize
  • Features:
    • Flux selbst per GitOps verwalten
    • Lösung für cluster-weite Ressourcen
  • Quelle: fluxcd/flux2-kustomize-helm-example

Repo-Struktur von flux2-kustomize-helm-example Abbildung 3: Repo-Struktur von flux2-kustomize-helm-example

Nach diesen nicht trivialen Beispielen im Kontext von Argo CD beginnt unser praktischer Einblick in die Welt von Flux mit einer positiven Überraschung: Hier sind keine Vorüberlegungen oder externe Tools zur Installation notwendig. Das Flux CLI bringt einen bootstrap Command mit, der das Bootstrapping von Flux im Cluster sowie das Anlegen von Repo-Strukturen durchführt. Zusätzlich bietet Flux offizielle Beispiele, die verschiedene Patterns implementieren. Wir beginnen mit dem Monorepo. Abbildung 3 zeigt die Zusammenhänge. Im Folgenden werden diese anhand der Nummern in der Abbildung beschrieben:

  1. In den Ordner flux-system generiert der Command flux bootstrap alle zur Installation von Flux notwendigen Ressourcen, sowie ein Git Repository und eine Kustomization. Zum Bootstrapping wendet flux diese einmalig imperativ auf den Cluster an. Die Kustomization referenziert dann ihren eigenen Überordner Ordner production. Ab hier wird alles über GitOps verwaltet.
  2. Die flux-system Kustomization deployt zudem eine weitere Kustomization infrastructure, die auf den gleichnamigen Ordner zeigt. Darüber können cluster-weite Ressourcen wie Ingress Controller und Network Policies deployt werden.
  3. Außerdem deployt flux-system die Kustomization apps. Diese zeigt auf den Unterordner des jeweiligen Environments unter apps, beispielsweise apps/production.
  4. In diesem Ordner liegt eine kustomization.yaml, mittels der die Ordner aller Anwendungen eingebunden werden.
  5. Im Ordner jeder Anwendung liegt eine weitere kustomization.yaml, die die eigentlichen Ressourcen für jede Anwendung in einem Environment zusammenstellt: Als Grundlage dient, typisch für Kustomize, ein Unterordner von base (beispielsweise apps/base/app1). Dieser enthält Config, die in allen Environments gleich ist. Dazu kommt Config, die spezifisch für jedes Environment ist. Diese wird mittels Patches aus dem jeweiligen Ordner des Environments (beispielsweise apps/production/app1) über die base gelegt.

Analog zum Ordner apps gibt es im Ordner clusters ebenfalls je einen Ordner pro Environment. Flux implementiert hier also eine Instance per Cluster Pattern: eine Flux-Instanz pro Environment.

Das öffentliche Beispiel selbst zeigt nur die Verwaltung einer einzelnen Anwendung und es ist nicht offensichtlich, wie weitere hinzugefügt werden. Unsere Erfahrung aus der Praxis ist, dass eine Flux-Instanz meist mehrere Anwendungen verwaltet. Daher zeigt Abbildung 3 eine um die Erkenntnisse aus einem Issue erweiterte Variante, die mehrere Anwendungen unterstützt. Diese Struktur kann auch bei GitHub unter schnatterer/flux2-kustomize-helm-example eingesehen und ausprobiert werden.

Diese Struktur hat den Nachteil, dass alle Anwendungen unter apps von einer einzigen Kustomization pro Environment deployt werden. Beispielsweise bei der Verwendung der grafische Oberfläche von Weave GitOps werden dann alle darin enthaltenen Ressourcen als eine „App“ auf der Oberfläche angezeigt (siehe Abbildung 4). Dies ist schnell unübersichtlich. Analog zur Verwendung von Applications bei Argo CD (siehe vorherige Beispiele) ist es auch hier denkbar eine Kustomization pro Anwendung anzulegen, statt einer einzigen der Kustomization in der Datei apps.yaml. Dies bedarf zwar mehr Wartung, sorgt aber für übersichtlichere Strukturen.

Mehrere Anwendungen in einer Kustomization (Screenshot Weave GitOps) Abbildung 4: Mehrere Anwendungen in einer Kustomization (Screenshot Weave GitOps)

Die hier beschriebene Repo-Struktur würde mit wenigen Änderungen auch für Argo CD funktionieren. Statt der Kustomizations müssten Applications zum Linking eingesetzt werden. Die kustomization.yamls werden von beiden Tools verstanden.

Beispiel 4: Flux repo per team

  • Repo pattern: Repo per team
  • Operator pattern: Instance per Cluster
  • Operator: Flux (prinzipiell auch Argo CD)
  • Boostrapping: flux CLI
  • Linking: Flux Kustomization, Kustomize
  • Features: Wie Beispiel 3
  • Quelle: fluxcd/flux2-multi-tenancy

Zusammenhang der GitOps-Repos bei flux2-multi-tenancy Abbildung 5: Zusammenhang der GitOps-Repos bei flux2-multi-tenancy

Wer für seine Organisation lieber ein Repo pro Team verwenden möchte, findet im Flux Projekt auch dafür ein offizielles Beispiel. Bei Flux wird dieses unter dem Begriff „multi-tenancy“ geführt. Statt dem allgemeineren Begriff „Tenant“ (engl. Mandant) verwenden wir hier den zum Pattern passenden Begriff „Team“.

Einige Punkte sind bereits aus dem vorherigen Beispiel bekannt. Dazu gehören das Bootstrapping mittels des Ordners clusters und die cluster-weiten Ressourcen im Ordner infrastructure. Abbildung 5 zeigt die Zusammenhänge. Im Folgenden werden diese anhand der Nummern in der Abbildung beschrieben:

  1. Die Kustomization flux-system deployt eine Kustomization tenants, die auf den Ordner tenants/production zeigt.
  2. In diesem Ordner liegt eine kustomization.yaml, mittels der die Ordner aller Teams in einem Environment eingebunden werden, beispielsweise tenants/production/team1.
  3. Im Ordner jedes Teams liegt eine weitere kustomization.yaml, die Ressourcen für jedes Team in einem Environment zusammenstellt. Als Grundlage dient, typisch für Kustomize, ein Unterordner von base (beispielsweise tenants/base/team1). Dieser enthält Config, die in allen Environments gleich ist. Dazu kommt Config, die spezifisch für jedes Environment ist. Diese wird mittels Patches aus dem jeweiligen Ordner des Environments (beispielsweise typisch/production/team1) über die base gelegt.
  4. Konkret können sich im Ordner base mehrere Ressourcen befinden, die über noch eine weitere kustomization.yaml zusammengefügt werden.
  5. Das Team-Repo wird dabei über die Datei sync.yaml eingebunden, in der sich ein Git Repository und eine weitere Kustomization befinden. Spezifisch für jedes Environment ist dann nur noch der Pfad im Team-Repo, der mittels eines Patches aus dem jeweiligen Ordner des Environments darüber gelegt wird, beispielsweise tenants/production/team1/path.yaml.

Der Aufbau des Team-Repos entspricht dann genau dem des Ordners app aus dem Beispiel davor. Auch hier haben wir dieses so angepasst, dass es mehrere Anwendungen unterstützt. Diese Struktur kann auch bei GitHub unter schnatterer/flux2-multi-tenancy eingesehen und ausprobiert werden. Wie bei diesem gilt auch hier der Nachteil, dass alle Anwendungen im Team-Repo von einer Kustomization pro Environment deployt werden und dies unübersichtlich wird.

Beispiel 5: The Path to GitOps

  • Repo pattern: Monorepo
  • Operator pattern: Instance per Cluster
  • Operator: Argo CD (oder Flux)
  • Boostrapping: kubectl
  • Linking: Application, ApplicationSet, Kustomize
  • Features:
    • Lösung für cluster-weite Ressourcen
    • Env per app Pattern
    • Beispiele für Argo CD und Flux
  • Quelle: christianh814/example-kubernetes-go-repo

Die Repo-Struktur bei example-kubernetes-go-repo Abbildung 6: Die Repo-Struktur bei example-kubernetes-go-repo

In seinem Buch „The Path to GitOps“ widmet Christian Hernandez, der sich seit Jahren bei Akuity, RedHat und Codefresh mit dem Thema GitOps auseinandersetzt, dem Thema Repo- und Ordnerstrukturen ein Kapitel. Abbildung 6 zeigt sein Beispiel für ein Monorepo. Es ist auch bei GitHub einsehbar, und zwar sowohl für Argo CD als auch für Flux. Die Bezeichnungen der Ordner unterscheiden sich teilweise zwischen Buch und Repo bei GitHub. Dies passt zu einem Tipp aus dem Buch, dass die Namen der Repos nicht wichtig sind, sondern die Konzepte, die diese repräsentieren.

In diesem Beispiel findet sich viel wieder, was bereits in den vorherigen Beispielen bekannt ist:

  • Es gibt einen Ordner apps für Anwendungen. Im Repo bei GitHub heißt dieser tenants, ist hier also wie in Beispiel 4 auf Teams bezogen.
  • Für cluster-weite Ressourcen gibt es einen Ordner cluster-config, der im Repo bei GitHub core heißt.
  • Im Ordner bootstrap ist das Bootstrapping des Operators realisiert. Hier wird Argo CD ähnlich wie bei Autopilot (siehe Beispiel 1) mittels Kustomize direkt aus dem öffentlichen Argo CD-Repo übers Internet installiert.

Um Wiederholungen zu vermeiden, werden die Zusammenhänge der Repo-Struktur an dieser Stelle nicht im Detail beschrieben. Interessant sind jedoch die folgenden Punkte.

  • Dieses Repo teilt die Config des Operators in zwei Ordner auf: Den bereits bekannten Ordner bootstrap und einen Ordner components.
  • Zudem befindet sich die gesamte Struktur in einem Ordner cluster-XXXX, was vermuten lässt, dass sich die gesamte Struktur inklusive Argo CD auf einen Cluster bezieht. Hier wird also das Instance per Cluster-Pattern implementiert.
  • Für die Promotion wird hier eine „Env per app“ Pattern per Kustomize implementiert, siehe Ordner apps in Abbildung 6. Dies wird im Buch beschrieben, ist allerdings nicht im GitHub-Repo umgesetzt.

Wie erwähnt, gibt es die gleiche Repo-Struktur auch für Flux. Es ist generell interessant, dass die gleiche Struktur mit kleineren Änderungen sowohl für Argo CD als auch für Flux verwendbar ist. Bei Flux empfiehlt es sich allerdings stattdessen, die Struktur des flux bootstrap Commands zu verwenden. Da dieser in Flux implementiert ist, kann man sie als Good Practice für Flux ansehen. Er erleichtert das Verständnis und die Wartung, beispielsweise bei Updates von Flux.

Beispiel 6: Environment-Varianten

Die Ordnerstruktur bei gitops-environment-promotion Abbildung 7: Die Ordnerstruktur bei gitops-environment-promotion

Dieses letzte Beispiel unterscheidet sich von den bisherigen dahingehend, dass es nicht die Struktur eines ganzen Repos beschreibt, sondern nur die einer einzelnen Anwendung. Es kann daher mit den anderen Beispielen kombiniert werden. Dieses Beispiel fokussiert sich auf die Umsetzung einer großen Anzahl von Environments. Es zeigt, wie verschiedene Environments (integration, load, prod, qa und staging), in unterschiedlichen Regionen (asia, eu, us) ausgerollt werden können, ohne dass viel redundanten Config entsteht. Insgesamt entstehen so 11 Environments. In jedem Environment soll zudem zwischen prod und non-prod unterschieden werden. Hier zeigt sich, dass Kustomize zur Umsetzung einer so umfangreichen Struktur ohne Redundanzen gut geeignet ist. Obwohl das Beispiel aus dem Umfeld von Argo CD kommt kann es ohne Änderungen auch mit Flux eingesetzt werden, da das Linking ausschließlich per kustomization.yaml durchgeführt wird.

Abbildung 7 zeigt die Struktur vereinfacht auf fünf Environments. Ausgangspunkt sind die Unterordner eines Environments im Ordner envs, beispielsweise envs/prod-eu. Diese Unterordner würde man per Argo CD Application oder Flux Kustomization einbinden. In jedem Unterordner liegt eine kustomization.yaml, die als Grundlage den Ordner base verwendet, der in allen Environments identische Config enthält. Die Config der Varianten liegt in Unterordnern vonvariants, beispielsweise eu und prod. Dazu kommt die für jedes Environment spezifische Config mittels Patches aus dem jeweiligen Unterordner von env, beispielsweise envs/prod-eu. Grundsätzlich könnte dieses Beispiel auch per Helm umgesetzt werden, dies wäre aber umständlicher und es würde die Verwendung von speziellen CRDs, statt universeller kustomization.yaml, erfordern.

Dieses Beispiel liefert außerdem eine Idee zur Vereinfachung der Promotion: In jedem Ordner befinden sich viele YAML Dateien, eine Datei pro Property. Der Vorteil ist, dass die Promotion dann durch simples Kopieren einer Datei durchgeführt werden kann. Es ist nicht notwendig, Text auszuschneiden und einzufügen, was den Prozess vereinfacht und das Risiko von Fehlern verringert. Außerdem sind die Diffs einfacher zu lesen.

Fazit

Anhand wiederkehrender Elemente bestehender GitOps-Tools beschreibt diese Artikelserie GitOps-Patterns und ordnet sie in die vier Kategorien Operator Deployment, Repository, Promotion und Verdrahtung ein. Die Patterns geben zum Einen einen Überblick über die Möglichkeiten bei Design-Entscheidungen für den GitOps-Prozess und für die Struktur der zugehörigen Repositories und was dabei zu beachten ist. Zum Anderen können sie dazu beitragen, Begriffe für die jeweiligen Patterns zu finden, diese zu vereinheitlichen und die Kommunikation zu erleichtern.

Dieser letzte Teil der Serie beschreibt Beispiele aus der Praxis für die Patterns. Diese liefern Vorlagen, Ideen und Tipps für eigene Projekte. Dabei zeigen sich einige wiederkehrende Themen, die teils unterschiedlich benannt werden: Strukturen für Anwendungen oder Teams, Strukturen für cluster-weite Ressourcen und Strukturen fürs Bootstrapping. Die Beispiele zeigen auch, dass grundsätzlich kaum Unterschiede zwischen Argo CD und Flux bei Strukturen notwendig sind. Diese beschränken sich auf Bootstrapping und Linking. Da Kustomize mittels kustomization.yaml sowohl von Argo CD als auch von Flux verstanden wird, stellt es sich als Operator-agnostisches Werkzeug heraus. Vieles von dem hier beschriebenen lässt sich mit dem in Beispiel 2 gezeigten GitOps Playground einfach ausprobieren.