6Repositories und Ordner strukturieren

Ein zentraler Punkt bei der Einführung von GitOps ist das Design des GitOps-Prozesses und der zugehörigen Repos. Beim Design des Prozesses entscheiden wir über

Anhand dieser Punkte erkennen wir in diesem Kapitel wiederkehrende Elemente als GitOps-Patterns und vermeiden Verwirrung durch Nennung von Synonymen. Die Patterns ordnen wir dann in vier Kategorien ein, die einen roten Faden beim Design des GitOps-Prozesses liefern können.

Die Patterns illustrieren wir anhand von sechs öffentlichen Beispielen für Config-Repos im Detail. Diese können Impulse für eigene Repos geben oder sogar als Grundlage dienen.

Abschließend widmen wir uns dem Thema Mandantentrennung. Dabei können wir das Vorwissen aus Patterns und Beispielen direkt einsetzen.

6.1Design des GitOps-Prozesses

Stellen wir uns vor, wir wären dabei, GitOps in unserer Organisation einzuführen. Einen GitOps-Operator haben wir bereits ausgewählt. Nun geht es darum, den GitOps-Prozess zu entwerfen. An dieser Stelle steigt dieses Kapitel ein.

Die Wahl des GitOps-Operators fällt unserer Erfahrung nach meist auf Flux oder Argo CD, weshalb wir in in diesem Kapitel Beispiele aus deren Kontext verwenden. Einen umfangreichen Vergleich bietet Kapitel 4 auf Seite 69.

Infrastruktur oder Anwendungen

Eine der ersten Fragen, die wir uns stellen sollten, ist die nach dem Anwendungsfall. Was wollen wir eigentlich per GitOps deployen? Infrastruktur oder Anwendungen? Dies kann das Design beeinflussen. Dieses Kapitel geht vom wahrscheinlich häufigsten Anwendungsfall aus: dem Deployment von Anwendungen. Dabei berücksichtigen wir auch clusterweite Ressourcen wie Ingress-Controller, Monitoring, Secrets Management etc. Dem Thema »Infrastruktur per GitOps verwalten« widmen wir uns in Kapitel 11 auf Seite 291.

Begriffe und Synonyme

Im Kontext des Platform Engineering werden teilweise andere Begriffe verwendet, die wir als Synonyme begreifen:

Platform Engineering

Ein weiterer wichtiger Punkt ist die Verantwortlichkeit: Wer kümmert sich in einer Organisation um den GitOps-Prozess? Je nach Teamtopologie und Grad der technischen Teamautonomie kümmern sich Applikationsteams selbst um den GitOps-Operator oder delegieren dies. Die Komplexität von Kundenanforderung und Produktentwicklung auf der einen bis hin zu stark auf Betrieb bezogenen Themen wie GitOps auf der anderen Seite ist hoch. Unter anderem deshalb setzt sich vermehrt das Thema Platform Engineering durch. Dabei entwickelt und betreibt ein dediziertes Plattformteam eine sogenannte Internal Developer Platform für die Applikationsteams. Der Fokus liegt dabei auf Developer Experience und Self-Service: Für die Applikationsteams soll es einfach sein, ihre Anwendungen zu deployen und zu betreiben. In einem solchen Szenario liegt es nahe, dass das Plattformteam den GitOps-Prozess designt und Abstraktionen schafft, die einen Teil der Gesamtkomplexität verstecken. Ziel sind die einfache Benutzbarkeit und geringer Betriebsaufwand für die Applikationsteams. Außerdem erleichtert ein einheitlicher Prozess die Wartung und Komplexität.

Gesetz von Conway

Organizations which design systems … are constrained to produce designs which are copies of the communication structures of these organizations1.

Bei dieser Diskussion über Teams und Softwareprodukte liegen Gedanken an das Gesetz von Conway nahe. Mel Conway erkannte bereits 1968 eine Gesetzmäßigkeit bei der Architektur von Softwaresystemen: Die Kommunikationsstrukturen in einer Organisation haben direkten Einfluss auf das Design. Dies trifft auch beim Design des GitOps-Prozesses zu. Insofern gibt es absichtlich keinen Standard für GitOps-Prozesse und für die Struktur der zugehörigen Repos. Jede Organisation ist verschieden, also werden auch die GitOps-Prozesse unterschiedlich sein.

Infra

 

Real-world

Repos

 

Firma/Abteilungen

Ordner/Branches

 

Teams/Projekte

Cluster/Namespaces

Wie abbilden?

Anwendungen/Dienste

Operator-Instanzen

Stages/Environments

Operator-CRDs

 

Kunden/Mandanten

 

 

etc.

Abb. 6–1
GitOps Chasm

Was ist beim Design dieser Prozesse eigentlich zu tun? Die Herausforderung ist die Abbildung der »echten Welt« auf die Infrastruktur mit ihren jeweils unterschiedlichsten Konstrukten, wie sie Abb. 6–1 zeigt. Diese Abbildung kann man sich bildlich wie einen Abgrund vorstellen, den es zu überwinden gilt – den GitOps Chasm (engl. chasm für tiefe Kluft). Der GitOps Chasm ist Herausforderung und Chance zugleich. Zur Überwindung ist ein Konzept zur Abbildung der »echten Welt« auf die Infrastruktur notwendig. Bei der Erstellung des Konzepts beantworten wir unter anderem die folgenden Fragen:

Dieses Konzept sorgt für Struktur und damit Wartbarkeit. Verglichen mit einem Start ohne GitOps ist das ein klarer Vorteil: Ohne GitOps kann man ohne Konzept von verschiedenen Stellen, Repos, CI-Jobs oder manuell auf den Cluster deployen. Auf lange Sicht kann keiner mehr nachvollziehen, wer wann was wieso deployt hat.

6.2Kategorien von GitOps-Patterns

Begriffe und Synonyme

Mehr oder weniger synonym zu Patterns tauchen folgende Begriffe in Literatur und in Vorträgen auf: Strategies, Models, Approaches und Best Practices.

Es gibt also nicht den einen GitOps-Prozess, der zu jeder Organisation passt. Aus unserer Erfahrung zeigen sich jedoch gewisse wiederkehrende Elemente, die wir in diesem Kapitel kategorisieren und beschreiben. Bisher sind diese Patterns weder einheitlich benannt noch kategorisiert. Zeit, dies zu ändern! Dieses Kapitel benennt und beschreibt die folgenden GitOps-Patterns in vier Kategorien:

Viele der Namen für die Patterns werden auch in anderer Literatur verwendet. Eine Übersicht der Patterns und Quellen findet sich im Repo »cloudogu/gitops-patterns« bei GitHub2.

6.3Operator Deployment Patterns

Die Patterns der Kategorie Operator Deployments beschreiben Möglichkeiten, das Verhältnis von GitOps-Operatoren zu Kubernetes-Clustern oder Namespaces zu gestalten. Dieser Abschnitt beschreibt die drei unserer Erfahrung nach gängigsten Patterns:

  1. Instance per Cluster
  2. Hub and Spoke
  3. Instance per Namespace

Über diese grundlegenden Patterns hinaus gibt es noch Mischformen, wie sie beispielsweise Nicholas Morey in seinem Artikel »How many do you need? Argo CD Architectures Explained«3 vorstellt. Ebenso beschreibt Dan Garfield in seinem Post »A Comprehensive Overview of Argo CD Architectures«4 die Patterns Split Instance und Control Plane.

Diese Patterns erscheinen uns nicht als sehr gängig, weshalb wir uns auf die oben genannten beschränken.

Generell ist es wichtig, bei der Auswahl des Operator Deployment- Patterns auch der geplante Umgang mit Mandanten zu betrachten, worauf Abschnitt 6.8 auf Seite 189 im Detail eingeht.

6.3.1Instance per Cluster

Begriffe und Synonyme

Synonym zu Instance per Cluster ist gelegentlich von Standalone die Rede. Unserer Meinung nach grenzt dieser Begriff den Bezug zu Cluster und Namespaces nicht genau genug ab. Standalone könnte sowohl auf eine Instanz pro Namespace als auch auf eine Instanz pro Cluster zutreffen. Wie wir in Abschnitt 4.11 auf Seite 88 sehen, muss nicht jeder Operator auch alle Deployment Patterns unterstützen.

image

Abb. 6–2
Instance per Cluster Pattern

Instance per Cluster bezeichnet die Verwendung von einem GitOps-Operator mit einem Kubernetes-Cluster, siehe Abb. 6–2. Dieses Pattern bietet starke Isolation. Dies hat zum einen Vorteile in Bezug auf Security, da im Falle eines Einbruchs nur die Anwendungen der einen Instanz in Gefahr sind. Zum anderen skaliert dieses Vorgehen besser in Bezug auf Last und Verfügbarkeit. Der Nachteil ist der Aufwand für die Wartung, da man statt einer zentralen Instanz mehrere betreibt. Außerdem steigt der Ressourcenbedarf.

6.3.2Hub and Spoke

Begriffe und Synonyme

Synonym zu Hub and Spoke ist der Begriff Management Cluster im Einsatz. Dieser Begriff ist zwar bereits geläufig beispielsweise von der Cluster API (siehe Abschnitt 10.2 auf Seite 265), jedoch setzt er einen Cluster voraus. In SaaS/ PaaS-Konstellationen wird die zentrale Instanz gegebenenfalls gar nicht in einem Cluster betrieben. Insofern kommt uns der Begriff Hub and Spoke hier treffender vor, zumal auch dieser vielen bekannt ist von Computernetzwerken oder der Logistik.

image

Abb. 6–3
Operator Deployment Pattern Hub and Spoke

Im Gegensatz zu Instance per Cluster steht das Pattern Hub and Spoke (engl. für Nabe und Speiche), siehe Abb. 6–3. Dabei kommt ein zentraler GitOps-Operator für mehrere Kubernetes-Cluster zum Einsatz. Entsprechend sind die Vor- und Nachteile genau invertiert zum Instance per Cluster-Pattern: Der Wartungsaufwand ist geringer, dafür gibt es einen Single Point of Failure, was schlechter skaliert. Das Aufsetzen kann dafür aufwendiger sein, da gegebenenfalls eine Mandantentrennung innerhalb der Instanz stattfinden muss. Die setzt auch voraus, dass der Operator diese Trennung überhaupt unterstützt. Generell ist diese geringere Isolation auch weniger sicher. Abschnitt 4.11 auf Seite 88 zeigt, wie dies bei Argo CD und Flux aussehen kann.

6.3.3Instance per Namespace

image

Abb. 6–4
Instance per Namespace Pattern

Das Pattern Instance per Namespace ist ähnlich zu Instance per Cluster, allerdings gibt es hier einen Operator pro Namespace statt pro Cluster, siehe Abb. 6–4. Die Verwendung dieses Patterns kann sinnvoll sein, wenn man eine hohe Isolation anstrebt, aber der Betrieb eigener Kubernetes-Cluster nicht in Frage kommt. Dies kann beispielsweise aufgrund hoher Kosten on-Premises oder aufgrund organisatorischer Einschränkungen vorkommen.

Dieses Pattern hat prinzipiell die gleichen Vorteile wie Instance per Cluster. Zu beachten ist, dass die Isolation von Kubernetes Namespaces generell geringer ist, als bei getrennten Clustern. Auch ist zu beachten, ob Operatoren die Unterteilung in Namespaces überhaupt unterstützen (siehe beispielsweise Abschnitt 4.11 auf Seite 88).

6.4Repository Patterns

Die Repository Patterns beantworten die Frage nach der Anzahl der Config-Repos. In dieser Kategorie gibt es einige Patterns zur Auswahl. Diese Auswahl muss jedoch nicht exklusiv getroffen werden. Einige Patterns sind kombinierbar.

Bei den Repository Patterns unterscheiden wir generell das Monorepo-Pattern von den drei Polyrepo-Patterns Repo per Team, Repo per Application und Repo per Environment. Bei Repo per Application betrachten wir zudem vier Wege, es zu implementieren: Repo Separation, Config Replication und Repo Pointer und Config Split.

6.4.1Monorepo

Wir sprechen von Monorepo, wenn sich die gesamte Config in einem einzigen Repo befindet. Unserer Meinung nach ist es fraglich, ob die typischen Vorteile von Monorepo in der Softwareentwicklung, wie sie einige Big-Tech-Unternehmen einsetzen, beispielsweise einfacheres Refactoring und Dependency Management5, auch für GitOps gelten. Zu den Nachteilen von Monorepos zählen oft schwieriger zu konfigurierende Autorisierung pro Ordner und schlechtere Performance durch das große Repo aufgrund der vielen Commits. Bei ArgoCD können beispielsweise konkrete Performanceprobleme entstehen, wenn eine große Anzahl Applications in einem Repo liegt6.

Als Gegenteil von Monorepo lesen wir vereinzelt den Begriff Polyrepo. Dieser Begriff bezeichnet allerdings nicht ein einzelnes Pattern, sondern mehrere, die wir in den folgenden Abschnitten genauer betrachten.

6.4.2Repo per Team

Begriffe und Synonyme

Verallgemeinert liest man statt Team auch manchmal den Begriff Tenant (engl. für Mandant).

Wenn mehrere Config-Repos existieren, denken die meisten zuerst an das Repo per Team-Pattern. Dieses bietet den Vorteil, dass die Autorisierung auf Repo-Ebene im SCM typischerweise einfach handhabbar ist. Wie bereits eingangs erwähnt, fühlt es sich außerdem oft natürlich an, die Strukturen der Organisation nachzubilden (Gesetz von Conway). Zudem sieht jedes Team auch nur das, was es betrifft. Dadurch verringern sich Komplexität und Mental Load und die Sicherheit verbessert sich (Least Privilege).

6.4.3Repo per Application

Begriffe und Synonyme

Unter Third-Party- oder (dazu synonym) Off-the-shelf-Apps verstehen wir Anwendungen, die betrieben, aber nicht selbst entwickelt werden.

Wer es feingranularer mag, kann auch ein Repo pro Applikation verwalten. Hier sprechen wir vom Repo per Application-Pattern.

Dabei ist eine kontrovers diskutierte Frage, ob die Config und der Code im selben oder in getrennten Repos gespeichert sind. Hier kommen die Begriffe App-Repo und Config-Repo ins Spiel, die wir schon aus Abschnitt 2.3.1 auf Seite 29 kennen.

Code und Config in einem Repo

Abhängig von der Ausprägung der DevOps-Kultur und Historie einer Organisation ist nun die Frage, ob nicht einfach Code und Config in einem Repo liegen können. Unserer Erfahrung nach ist dieser Ansatz vor allem beliebt bei

Hier ist der Vorteil, dass alles, was zu einer Anwendung gehört, in einem Repo liegt. Dazu gehören der Code der Anwendung, Dokumentation und Config. Beispielsweise vereinfacht dies Entwicklung, Tagging und Refactoring.

Aufteilung in App-Repo und Config-Repo

Wenn wir uns mehr von der Seite des Betriebs oder einem nativen GitOps-Ansatz nähern, stellen wir fest, dass die Trennung des Source Code der Anwendung von der Config Vorteile hat. Beispielsweise empfiehlt dies die Doku von Argo CD7.

Diese Aufteilung der Repos hat unter anderem den Vorteil, dass die gesamte Config (beispielsweise einer Microservices-Anwendung mit mehreren Services, eines Teams, das mehrere Anwendungen betreibt, oder eines ganzen Clusters) an einer zentralen Stelle ist und man diese dort besser auditieren und durchsuchen kann.

Dies ermöglicht außerdem direkte Änderungen an bestimmten Environments ohne vorherige Promotion durch die anderen Environments.

Auch für das Config Update, die Automatisierung des Aktualisierens neu gebauter Image-Versionen mit dem CI-Server (siehe Abschnitt 6.5.5 auf Seite 163) hat diese Trennung Vorteile, da sie Endlosschleifen von Build-Jobs und Git-Pushes vermeidet und Config-Änderungen ohne Image-Build möglich und daher schneller durchführbar sind.

Andererseits ist genau das auch ein Nachteil: Es ist nicht automatisch ein CI-Job vorhanden, um beispielsweise statische Codeanalysen durchzuführen. Der Kasten »Beispiele für Tools zur statischen Codeanalyse der Config« zeigt eine Auswahl von Tools, die unserer Erfahrung nach in diesem Kontext sinnvoll sein können.

Beispiele für Tools zur statischen Codeanalyse der Config

Je nach Präferenz bieten sich unterschiedliche Patterns zur Umsetzung des Repo per Application-Patterns an:

Repo Separation
image

Abb. 6–5
Implementierung von Repo per Application durch Repo Separation

Bei Repo Separation landen Code und Config in getrennten Repos. Abb. 6–5 zeigt dies schematisch. Der oben genannte Nachteil der fehlenden statischen Codeanalyse lässt sich entweder darüber beheben, dass der CI-Job des App-Repos diese durchführt oder man legt einen eigenen Job für das Config-Repo an. Diese Jobs kann man auch verwenden, um automatisch oder manuell Config Update und Promotion (siehe Abschnitt 6.5.5 auf Seite 163) durchzuführen. Abschnitt 2.3.2 auf Seite 31 zeigt dafür ein Beispiel.

Dabei ist das Bewusstsein wichtig, dass die Ergebnisse des Jobs des Config-Repos und das Deployment durch den GitOps-Operator zunächst unabhängig voneinander sind. Ist beispielsweise der GitOps-Operator so konfiguriert, alles von main Branch zu deployen, führt er dies aus, auch wenn der Build scheitert. Dies lässt sich durch Prozesse, beispielsweise über PRs, einschränken.

Config Replication
image

Abb. 6–6
Implementierung von Repo per Application per Config Replication

Bei der Implementierung des Repo per Application-Patterns durch Config Replication bleibt die Config im App-Repo und der CI-Server pusht diese dann in das Config-Repo, wie Abb. 6–6 zeigt.

Zusätzliche Möglichkeiten durch CI-Server

Generell kann man an dieser Stelle den CI-Server zur Umsetzung von Shift Left nutzen: Er kann statische Codeanalysen durchführen und scheitert früher, als wenn man Fehler erst beim Deployment findet.

Außerdem bietet es sich bei Config Replication an, auch gleich das Rendered Manifest-Pattern umzusetzen. Auch dieses führt oft zu früherem Scheitern und erlaubt den Einsatz beliebiger CM-Tools. In Abschnitt 6.5.4 auf Seite 160 beschreiben wir dieses Pattern im Detail.

Zudem kann der CI-Server für weitere Automatisierung sorgen, beispielsweise für die Promotion (dazu mehr in Abschnitt 6.5 auf Seite 150) oder um Commits und PRs mit weiteren Informationen zu versehen. Abb. 6–7 zeigt einen beispielhaften PR.

Darin enthält der Commit ein Präfix für das Environment (in diesem Fall production), einen Link zum Issue Tracker (erzeugt Links zwischen der Config und der User Story) und einen Link zum zugehörigen Commit im App-Repo. Um klarzustellen, dass es sich um einen generierten Commit handelt, trägt sich der CI-Server als Committer ein. Um die Herkunft des Commits klarzustellen, übernimmt der CI-Server den Autor aus dem Commit im App-Repo.

image

Abb. 6–7
Automatisch erstellter Commit mit weiteren Informationen (Screenshot SCM-Manager)

Herausforderungen von Config Replication

Natürlich hat auch Config Replication nicht nur Vorteile. Ein Nachteil ist die Komplexität der entstehenden Pipelines. Um die Logik nicht für jede Anwendung duplizieren zu müssen, macht es Sinn, etwas Wiederverwendbares zu verwenden. Beispiele dafür sind eine GitHub Action oder eine Jenkins Shared Library. Unserer Erfahrung nach fällt einiges an Aufwand an, bis das alles resilient funktioniert. Beispielsweise kann die Concurrency im Zusammenspiel mit Git und automatischen Merges riskant sein durch die Notwendigkeit von Retry-Strategien und die Gefahr von Inkonsistenz. Die Implementierung der angesprochenen fortgeschrittenen Features wie statische Codeanalyse und Informationen in Commits verursachen zusätzlichen Aufwand. Unsere Empfehlung ist also, etwas Bestehendes zu verwenden, statt selbst zu bauen. Voraussetzung ist natürlich, dass etwas Passendes für die eingesetzten Tools verfügbar ist. Dies gilt zunächst für den CI-Server, der das Ganze ausführt. Wenn außerdem die Erstellung von PRs gewünscht ist, gilt dies auch für das SCM, was wir in Abschnitt 6.5.5 auf Seite 164 im Detail betrachten. Ein Beispiel ist die GitOps-Build-Lib16 für Jenkins. Mit ihr wurde auch der in Abb. 6–7 gezeigte Commit erstellt.

Ein weiterer Nachteil von Config Replication ist die Redundanz der Config: Die Config existiert am Ende zwei Mal, einmal im App-Repo und einmal im Config-Repo.

Repo Pointer

Wen die Redundanz stört, der kann das Repo per Application-Pattern alternativ durch einen Repo Pointer implementieren, wie ihn Abb. 6–8 schematisch zeigt. Statt Replizierung des Codes kommt dabei ein Verweis vom Config-Repo auf das App-Repo zum Einsatz. Dies kann man beispielsweise mit den Custom Resources Application in Argo CD oder Kustomization in Flux realisieren. Der GitOps-Operator zieht sich dann die Config direkt aus dem App-Repo. Denkbar ist auch der Git-native Weg über Submodules. Die Verwendung von CRs ist unserer Erfahrung nach einfacher realisierbar.

image

Abb. 6–8
Implementierung von Repo per Application per Repo Pointer

Auch die Nutzung von Repo Pointers hat Nachteile. Beispielsweise muss man den GitOps-Operator auf viele Repos autorisieren, und das Config-Repos ist nicht mehr die zentrale Stelle für Config, sondern enthält nur noch Links.

Config Split

Ein Kompromiss kann die Umsetzung mittels Config Split sein: Hier teilt man die Config zwischen den Repos auf. Dabei kommt ein Config-Management-Tool wie Helm oder Kustomize zum Einsatz (siehe auch Abschnitt 6.5.4 auf Seite 160). Die Aufteilung ist dann wie folgt:

Konkret am Beispiel vom Helm:

Diese Aufteilung vereinfacht beispielsweise die lokale Entwicklung, weil das Chart im App-Repo liegt, und bietet trotzdem die Vorteile einer getrennten Config.

image

Abb. 6–9
Implementierungen von Repo per Application per Config Split am Beispiel eines Helm-Charts mit Speicherung in Git, Helm-Repo, OCI

Abb. 6–9 zeigt mehrere Möglichkeiten, das Pattern umzusetzen, am Beispiel von Helm. Der Ablauf beim Deployment ist in allen gleich:

  1. Der GitOps-Operator holt sich die Custom Resource aus dem Config-Repo. Die Custom Resource verweist auf das Helm-Chart und Values oder enthält die Values selbst.
  2. Der GitOps-Operator holt das Helm-Chart aus der angegebenen Quelle (Git-Repo, Helm-Repo oder OCI-Registry).
  3. Der GitOps-Operator erzeugt aus Helm-Chart und Values die eigentlichen Manifeste und deployt sie.

Die Abbildung zeigt die Umsetzung mit drei unterschiedlichen Speicherorten für das Helm-Chart (von links nach rechts):

Bleibt nur noch die Frage des Config Update, also in unserem Beispiel, wie die neue Chart-Version ins Config-Repo kommt. Dies kann man

Dies beschreiben wir in Abschnitt 6.5.5 auf Seite 163 im Detail und thematisieren dabei auch die Rolle von PRs.

OCI-Artifacts

Statt das Rendering dem GitOps-Operator zu überlassen, besteht zumindest bei Flux auch die Möglichkeit, dies auf dem CI-Server durchzuführen und als OCI-Artifacts in die Registry zu schreiben. Dabei kann man auch auf CM-Tools zurückgreifen, die nicht vom GitOps-Operator unterstützt werden. Abschnitt 4.13 auf Seite 90 beschreibt dies im Detail. Dieses Vorgehen ist ähnlich einer Kombination der Repo Pointer- und Config Replication-Patterns.

Unterschied zum Repo Pointer-Pattern

Generell ist Config Split technisch ähnlich zum Repo Pointer-Pattern. Bei Repo Pointer liegt der Fokus auf der zentralen Speicherung der Config im App Repo bei Vermeidung von Redundanz (im Gegensatz zur Config Replication). Bei Config Split ist die Idee, die Environment-spezifische Config im Config-Repo abzulegen.

Gleiches Pattern bei Third-Party-Apps

Tatsächlich ist es auch das Config Split-Pattern, das man beim Betrieb von Third-Party-Apps einsetzt. Oft verwendet man Helm-Charts oder Kustomize-Bases, die von anderen bereitgestellt werden, und speichert nur die selbst gesetzten Values oder Overlays im Config-Repo. Die Idee des Config Split-Patterns ist es, dass dieses Vorgehen auch bei selbst entwickelten Apps umsetzbar ist.

6.4.4Repo per Environment

Begriffe und Synonyme

Repo per Environment ist auch bekannt als

Ein letztes Pattern, das sich in Bezug auf Repos zeigt, ist Repo per Environment. Bei diesem Pattern gibt es ein Repo für jedes Environment (beispielsweise QA, Staging und Production). Unserer Erfahrung nach findet dieses Pattern selten Anwendung, da es zu einer großen Zahl an Repos und zu einem weniger automatisierten, umständlicheren GitOps-Prozess führt. Gründe, sich dafür zu entscheiden, sind meist organisatorische Anforderungen. Beispielsweise wenn

6.5Promotion Patterns

Begriffe und Synonyme

Den Prozess des Übergangs von einem Environment in das nächste bezeichnen wir als Promotion. Die Promotion-Patterns beschreiben verschiedene Möglichkeiten, Environments und Übergänge zwischen ihnen mit GitOps umzusetzen.

Im Bereich der Promotions sehen wir folgende Patterns, die wir in den nächsten Abschnitten genauer beschreiben:

Zur Nutzung von PRs bei der Promotion

Bei GitOps ist es gängig, die Promotion per PR zu realisieren. Dies folgt der ursprünglichen GitOps-Idee Alexis Richardsons (»Operations by Pull Request«, siehe Abschnitt 1.2.5 auf Seite 14).

Einschränkend sei hier gesagt, dass uns keiner zur Nutzung von PRs zwingt. Auch in den GitOps-Prinzipien findet sich kein Hinweis auf die Nutzung von PRs, und in einigen Fällen kann es pragmatisch sein, davon abzusehen. Anbei einige Beispiele:

Unser Fazit zur Verwendung von PRs bei GitOps ist, dass wir dies nicht als stumpfen Cargo Cult befolgen sollten, sondern sie nur dann einsetzen, wenn sie einen Mehrwert für unseren individuellen GitOps-Prozess bringen.

6.5.1Branch oder Folder per Environment

Begriffe und Synonyme

Bei der Konzeption von Promotions lautet eine zentrale Frage: Wie bildet man Environments ab, in Ordnern oder Branches? Tabelle 6–1 gibt eine Überblick über diese unterschiedlichen Vorgehen, die wir als Branch per Environment- und Folder per Environment-Patterns bezeichnen.

Tab. 6–1
Gegenüberstellung von Branch per Environment und Folder per Environment

 

Branch per Environment

Folder per Environment

Environments

Permanente Branches

Ordner (trunk-based)

Mapping-Beispiel

Branch develop → Staging

Ordner staging → Staging

 

Branch main → Production

Ordner production → Production

Promotion

Merge

Kopieren
(+Merge von kurzlebigen Branches)

Ablauf-Beispiel

image

image

Vorteile

Erzwingt PRs

Einfacher Schutz für Production

Fühlt sich natürlich für Entwickelnde an

Vermeidet Konflikte/Drift

Kopieren einfacher als Cherry Pick

Skaliert mit Anzahl Environments

Passt besser zu CM-Tools

Branch per Environment

Unserer Erfahrung nach denken viele Teams (vor allem mit Hintergrund in der Softwareentwicklung) zunächst daran, die Environments durch permanente Branches zu trennen, also per Branch per Environment-Pattern. Dieses Vorgehen kommt vielen aus der Softwareentwicklung bekannt vor. Ein Beispiel ist der weit verbreitete (aber mittlerweile oft als zu kompliziert kritisierte) Git-Flow17:

Analog kann man auch bei der Promotion vorgehen: Man führt das Bootstrapping seines GitOps-Operators so aus, dass er beispielsweise

Natürlich kann man die Branches im Config-Repo auch gleich nennen wie die Environments, was sich gerade bei mehr als zwei Environments anbietet.

Die Promotion findet dann durch einen Merge im Git statt.

Operations by Pull Request

Diesen Merge macht man typischerweise per PR, was gut passt zu Alexis Richardsons ursprünglicher GitOps-Idee (»Operations by Pull Request«, siehe Abschnitt 1.2.5 auf Seite 14). Dieses Vorgehen hat damit das Vier-Augen-Prinzip fest eingebaut, was für ein Deployment in Produktion empfehlenswert ist. Zudem haben viele SCMs die Option, bestimmte Branches vor direktem Commit zu schützen. Dadurch werden PRs und damit ein Review erzwungen. Dieses Vorgehen entspricht oft auch Vorgaben, die in großen Organisationen verpflichtend sind.

Konzept Branches nicht ganz passend für Environments

Ein wichtiger Unterschied zwischen Code und Config ist allerdings, dass beim Code nach dem Merge der Stand auf main gleich dem Stand auf develop ist. Bei der Config gibt es typischerweise Unterschiede zwischen Environments. Insofern wäre hier der Zielzustand, die beiden Branches in gewissen Punkten unterschiedlich zu halten. Daher sind Branches für die Abbildung von Environment semantisch nicht ganz passend.

Umgang mit CM-Tools

Dazu kommt, dass CM-Tools nicht auf Ebene von Branches arbeiten. Environments unterscheiden sich nur in wenigen Bereichen. Der Einsatz von CM-Tools wie Helm und Kustomize kann Redundanzen vermeiden. Beispielsweise hat Kustomize hat seine typische Ordnerstruktur mit base und overlays, wie sie unter anderem Tabelle 2–1 auf Seite 30 zeigt. Damit lassen sich Environments auf Ordner abbilden, aber eben nicht auf Branches. Tabelle 6–1 zeigt eine fürs erste Verständnis vereinfachte, naive Herangehensweise, in der main und develop jeweils nur den Stand eines Environments beinhalten. In der Realität würde man hier zusätzlich mit Ordnern arbeiten und könnte dann auch beispielsweise Kustomize einsetzen. Allerdings hätte hat man es dadurch mit Redundanz zu tun: Die Config jedes Environments besteht auf jedem Branch.

Synchronisierung der Branches

Die Branches synchron zu halten ist eine der Herausforderungen dieses Patterns. Dies gilt vor allem, wenn eine größere Anzahl Environments im Spiel ist. Damit steigt die Komplexität, denn die Promotion eines Changes durch mehrere Environments erfordert dann Änderungen mehrerer Dateien auf mehreren Branches. Wenn dann noch Hotfixes (also direkte Änderungen an beispielsweise main) ins Spiel kommen, sollten wir dringend darauf achten, dass wir die Änderungen auch wieder zurück auf die vorhergehenden Branches mergen. Sonst besteht bei der nächsten regulären Promotion die Gefahr, die Änderungen des Hotfixes wieder zu überschreiben. Durch Auto-merge können sich sogar inkonsistente Zustände ergeben, im schlechtesten Fall in Produktion.

Promotion einzelner Features

Auch wenn von einer Menge an Änderungen oder Features nur einzelne in Produktion gebracht werden sollen, ist dies mit dem Branching-Modell aufwendig. Im besten Fall entspricht ein Feature einem Commit, und dieser kann per Cherry Pick ausgewählt werden. Diese sind jedoch nicht immer trivial.

Folder per Environment

Viele dieser Herausforderungen können durch das Folder per Environment-Pattern umgangen werden. Dieses stellt die Environments als Ordner auf einem Branch (trunk-based) dar.

Für die Promotion erzeugt man kurzlebige Branches, auf denen man die Änderungen von einem Environment-Ordner in den nächsten kopiert. Diese kurzlebigen Branches enthalten jeweils nur ein Feature. Nach dem Merge löscht man sie wieder.

Die Verwendung von Ordnern hat Vorteile gegenüber Branches:

Nachteile bei Folder per Environment

Natürlich hat auch Folder per Environment Nachteile. Der Prozess der Promotion ist weniger intuitiv. Das Vorgehen: Datei kopieren, kurzlebigen Branch anlegen und PR erzeugen muss man erklären. Dass man einen PR anlegt, um von develop auf main zu mergen, sollte hingegen jedem klar sein. Zudem lassen sich Branches einfacher schützen: In den meisten SCMs kann man PRs per Einstellung erzwingen. Auch aus politischer Sicht ist dies oft für Folder per Environment nachteilig: In vielen Organisationen (besonders in regulierten Branchen) ist es undenkbar, dass ein Deployment in Produktion prinzipiell durch ein einfaches Kopieren ausgelöst werden kann.

Ordner, Branches oder Repos als Environments?

Nach dieser Diskussion stellt sich die Frage: Welches ist nun das richtige Pattern für meinen Anwendungsfall: Branch per Environment, Folder per Environment oder vielleicht sogar das in Abschnitt 6.4.4 auf Seite 149 beschriebene Repo per Environment?

image

Abb. 6–10
Vereinfachte Darstellung der Kompromisse zwischen den Promotion Patterns

Wie immer in der Softwarearchitektur müssen wir hier Kompromisse eingehen. Eine grobe Daumenregel zeigt uns Abb. 6–10: Grundsätzlich scheint uns Folder per Environment technisch am wenigsten komplex zu sein, weil man sich die Redundanz der Branches spart. Dafür ist der Zugriffsschutz auf Ordnern am schwersten umzusetzen. Wir sollten uns also zuerst fragen: Müssen wir in unserer Organisation bestimmte Environments (beispielsweise Produktion) besonders schützen? Gerade in großen Organisationen ist dies oft notwendig und kann daher den Einsatz von Folder per Environment erschweren.

Wer diesen Fall hat oder wem der erzwungene Prozess mit PRs wichtiger ist als Vermeidung von Redundanzen, greift zum Branch per Environment-Pattern. In komplexeren Umgebungen kann auch eine Mischung beider Patterns ein Kompromiss sein: Ordner in den entwicklungsnahen Environments, um dort schnell zu iterieren. Dazu einen separaten Branch für die Produktion, um den Zugriffsschutz zu gewährleisten.

Zum Repo per Environment raten wir hingegen nur, wenn organisatorische Richtlinien vorliegen, die den Zugriffsschutz durch Branches als nicht ausreichend ansehen. Abschnitt 6.4.4 auf Seite 149 beschreibt hierfür einige Beispiele.

Nachteile beider Patterns verhindern

Abschließend sei noch erwähnt, dass einige der beschriebenen Nachteile beider Patterns sich durch Einstellungen im SCM verhindern lassen:

Je nach Umgebung können also manche Argumente nicht zutreffen, was die Entscheidung zwischen Ordnern oder Branches beeinflussen kann.

Erfahrungsberichte zu den Patterns

Für die Entscheidung hilft außerdem ein Blick auf konkrete Erfahrungsberichte.

Zum Branch per Environment-Pattern kennen wir nur wenige Erfolgsgeschichten. Ein Beispiel ist »Monitoring and Hardening the GitOps Delivery Pipeline with Flux«20 von Florian Heubeck. Er nennt den Vorteil, dass das Erzwingen der Nutzung von PRs, als gute Praxis für die Promotion, auf Branches einfacher ist als auf Ordnern.

Dem gegenüber stehen viele Berichte, die von diesem Pattern abraten und stattdessen zu Folder per Environment raten. Beispielsweise:

Auch die uns bekannten öffentlichen Beispiele für GitOps-Repo-Strukturen, von denen wir einige in Abschnitt 6.7 auf Seite 169 vorstellen, verwenden alle das Folder per Environment-Pattern.

Verbreitung der Patterns

Diese subjektive Repräsentation in den Medien muss nicht zwangsweise der echten Verbreitung entsprechen. Durch den Austausch mit Kunden und auf Konferenzen haben wir Autoren den Eindruck, dass beide Patterns verbreitet sind. Es lohnt sich also ein unvoreingenommenes Abwägen der jeweiligen Vor- und Nachteile mit Hinblick auf den eigenen Anwendungsfall.

6.5.2Preview Environments

Begriffe und Synonyme

Statt Preview Environments ist an anderer Stelle oft die Rede von

Andere Synonyme sind Deploy Previews und Preview Deployments.

Eine andere Art, mit Environments umzugehen sind Preview Environments. Dabei stößt die Erstellung eines PRs ein Deployment an, das auf der Config dieses PRs basiert. Das Mergen oder Ablehnen des PRs führt dann zu einer automatischen Löschung des Deployments. Dieses Feature ist bei serverless Platforms as a Service wie Netlify oder Vercel schon seit längerem gängig. Diese verwenden die Begriffe Preview Deployments25 oder Deploy Previews26.

Bei einem Deployment mittels CI-Server ist vor allem das automatische Löschen schwer umsetzbar. Mit GitOps, konkret beispielsweise bei Argo CD, ist dieser ganze Prozess nun mittels eines eigenen Controllers und zugehöriger CRD ApplicationSet (siehe Abschnitt 3.3 auf Seite 51) und dessen »Pull Request Generator«27 automatisierbar. Dieser erzeugt mittels Templating pro PR eine Application. Dabei folgt er einer im ApplicationSet definierten Vorlage, beispielsweise myapp-{{branch}}-{{number}}.

Bei Flux (oder zumindest dem darauf basierenden Weave GitOps) sieht dies in Form von GitOpsSets so ähnlich aus28. Eine spannende Erkenntnis daraus ist, dass GitOps-Tools Features und Vorteile bieten können, die über die eigentlichen Prinzipien von GitOps hinausgehen. Wir dürfen gespannt sein, welche weiteren Vorteile sich in diesem innovativen Umfeld in der Zukunft noch zeigen werden.

Herausforderungen der Preview Environments

Die Nutzung von Preview Environments bringt auch ihre eigenen Herausforderungen mit sich. Ein zentraler Punkt ist der Ressourcenverbrauch: Mit der Anzahl der offenen PRs steigt auch die Anzahl der laufenden Systeme und damit der Bedarf an CPU, Memory und Storage. Zumindest in der public Cloud kann Kubernetes mittels Cluster Autoscaler automatisch mitwachsen. Allerdings wachsen damit auch die Kosten mit, was ein Risiko darstellen kann. An dieser Stelle macht es also Sinn, entweder über Prozesse oder Automatismen die Anzahl der Instanzen zu beschränken. Beispielsweise kann das Deployment per Tags am PR gesteuert werden.

Eine weitere Herausforderungen können Datenbanken darstellen. Bei der Nutzung vom managed Service außerhalb von Kubernetes kann man diese nicht ohne Weiteres Tooling per GitOps deployen. Darüber hinaus stellt sich die Frage, wie Testdaten in diese Datenbanken kommen. Hier wäre die Entwicklung eines Automatismus, der Testdaten einspielt, eine Möglichkeit. Dies kann direkt über die Anwendung geschehen, per Sidecar-Container oder Kubernetes-Job.

Eine pragmatische Alternative für beide Herausforderungen ist die Verwendung einer stehenden Datenbank, die von allen Preview Environments verwendet wird. Dieses Vorgehen ist allerdings bei Schemaänderungen riskant.

Auch die dynamisch generierten Namespaces durch die Preview Environments bringen Herausforderungen mit sich: Zugriffsberechtigungen in Argo CD und gegebenenfalls an Benutzer für den Zugriff per kubectl müssen entsprechend vergeben werden.

Wer Sealed Secrets (siehe Abschnitt 5.1.1 auf Seite 98) benutzt, muss diese clusterweit verschlüsseln, damit sie auch in den dynamisch generierten Namespaces entschlüsselt werden können. Dadurch können gegebenenfalls Unbefugte Zugriff auf die Secrets erlangen, was ein Sicherheitsrisiko darstellen kann.

6.5.3Global Environments oder Environment per Application

image

Abb. 6–11
Global Environments vs. Environment per Application

Eine entscheidende Frage bei der Modellierung von Environments ist, ob jede Anwendung selbst über ihre Environments entscheidet (Environment per Application-Pattern) oder ob alle Anwendungen die gleichen Environments nutzen (Global Environments-Pattern). Abb. 6–11 zeigt beispielhafte Ordnerstrukturen für beide Patterns. Wie die Abbildung zeigt, ist beides einfach umsetzbar. Der einzige Unterschied ist, dass Anwendungen oder Environments auf oberster Ebene im Ordnerbaum liegen.

Bei Environment per Application ist es möglich, pro Anwendung über Anzahl und Namen der Environments zu entscheiden. Bei Global Environments haben prinzipiell alle Anwendungen die gleichen Environments. Das macht es zwar einheitlicher, jedoch haben unserer Erfahrung nach nicht alle Anwendungen die gleichen Environments. Hat eine Anwendung also weniger oder andere Environments als andere Anwendungen, findet man sie bei Global Environments nicht in allen Ordnern wieder. Dies kann zu Verwirrung führen, die bei Environment per Application nicht auftritt. Gerade in der Verwendung in Kombination mit Preview Environments (siehe Abschnitt 6.5.2 auf Seite 157) kann Environment per Application seine Vorteile ausspielen. Durch den Einsatz von Preview Environments wird es wahrscheinlicher, dass einzelne Anwendungen nur noch ein permanentes Environment haben und man Änderungen jeweils auf Preview Environments testet und reviewt. Erstaunlicherweise zeigen viele öffentliche Beispiele oder Literatur Strukturen mit dem Global Environments-Pattern. Dies könnte damit zusammenhängen, dass Preview Environments noch recht neu sind.

6.5.4Configuration Management

Begriffe und Synonyme

Mehr oder weniger synonym zu Configuration Management (CM) finden oft die Begriffe Templating, Patching, Overlay oder Rendering Verwendung.

Zur Implementierung der Promotion kommen oft Tools wie Kustomize oder Helm zum Einsatz. Wir fassen diese unter dem Begriff CM-Tools zusammen.

Rendered Manifest vs. GitOps Operator Rendering

Es gibt zwei Möglichkeiten CM-Tools im GitOps-Prozess einzusetzen:

Rendered Manifests bieten mehr Transparenz, weil der endgültige Stand, den der GitOps-Operator auf den Cluster anwendet, vorher in Git reviewt werden kann. Fehler beim Rendering fallen damit außerdem früher auf. Neben den Änderungen durch Chart oder Config zeigen sich auch Änderungen durch veränderte Helm- oder Kubernetes-Versionen, die sonst nicht transparent wären. Das Rendered Manifest-Pattern kann sich zudem positiv auf die Performance des GitOps-Operators auswirken, da dieser nicht mehr bei jeder Angleichung von verschiedenen Quellen Daten beziehen und kombinieren muss. Außerdem erlaubt es die Verwendung beliebiger CM-Tools, auch wenn der GitOps-Operator keine native Unterstützung für sie enthält.

Nachteile des Rendered Manifest-Patterns

Ein Nachteil des Rendered Manifest-Patterns ist die erhöhte Komplexität der CI-Pipelines.

Ein weiterer Nachteil ist, dass CM-Tools oftmals große Mengen an YAML erzeugen. Beispielsweise ergibt das Rendern des Helm-Charts »kube-prometheus-stack«29 (Version 45.8.1) fast 45.000 Zeilen YAML. Das lässt sich jedoch etwas relativieren, da zwar der erste Commit unübersichtlich sein mag, aber zukünftige Commits im Diff ein eindeutiges und überschaubares Bild der Änderungen zeigen.

Theoretisch ist ein weiterer Nachteil, dass helm lookup auf dem CI-Server nicht funktioniert. Allerdings trifft dies generell für Argo CD30 zu, da Argo CD auch bei der Verwendung von Applications oder Umbrella Charts helm template verwendet. Dafür verliert man durch das Rendered Manifest-Pattern bei Argo CD die Möglichkeit Build-Environment-Variablen31 (beispielsweise den Namen der aktuellen Application oder des aktuellen Namespace) in Ressourcen in Argo CD zu injizieren. Für beide Herausforderungen bietet uns die imperative Natur des CI-Servers Lösungen. Beispielsweise ist der Namespace oft vom Environment (Ordner oder Branch) ableitbar. helm lookups können wir meist über das explizite Setzen von Helm-Values umgehen.

Ein kleinerer Nachteil ist, dass in manchen Anwendungsfällen das Anlegen eines CI-Jobs als unverhältnismäßig aufwendig angesehen wird. Ein Beispiel sind Third-Party-Applications, bei denen kein Bauen der Anwendung, sondern nur das Deployen nötig ist.

Alternative mittels UI von Argo CD

Abschließend möchten wir noch erwähnen, dass die UI von Argo CD auch eine eingebaute Diff-Funktionalität hat. Wer nicht das Rendered Manifest-Pattern verwenden will, kann vor einem Upgrade .spec.syncPolicy.automated.selfHeal auf false setzen. Dann kann man über die UI von Argo CD ein Review des Diff durchführen, einen manuellen Sync starten und danach die syncPolicy wieder zurücksetzen. Dieser dreischrittige Prozess ist allerdings umständlich. Auch besteht die Gefahr, das Rücksetzen der syncPolicy zu vergessen. Außerdem wird keine Historie des Diff erhalten.

Konkrete CM-Tools

Kustomize

Unter den CM-Tools tut sich Kustomize als Operator-agnostisch hervor: Sowohl Argo CD als auch Flux sind in der Lage, eine standardisierte kustomization.yaml zu interpretieren. Verwirrung stiftet oft die Flux CRD Kustomization, auf die Abschnitt 6.6 auf Seite 168 eingeht. Diese ist spezifisch für Flux und bietet mehr Optionen als die kustomization.yaml, beispielsweise die Verbindung zu einem Git-Repo. Trotzdem sind sowohl Kustomization als auch kustomization.yaml bei Flux im Einsatz.

Helm

Externe Packages sind oft als Helm-Charts verfügbar. Unserer Erfahrung nach stellt sich uns oft nicht die Frage, ob wir Kustomize oder Helm einsetzen. Häufig sieht man auch eine Kombination von beiden. Bei Flux lässt sich ein Helm-Release sogar mittels Kustomize nachbearbeiten32.

Die Nutzung von Helm mit GitOps ist komplizierter. Es stehen drei Optionen zur Verfügung:

Sowohl Flux (HelmRelease) als auch Argo CD (Application) bieten CRDs für die Durchführung von Helm-Releases. Bei Argo CD übernimmt die Application neben Helm noch andere Aufgaben. Vielleicht liegt es daran, dass die Developer Experience in Bezug auf Helm-Charts vor der Einführung von Multi-Source Applications (siehe Abschnitt 4.8 auf Seite 80) eingeschränkt war33.

Umbrella Charts

Daher sind Umbrella Charts ein gängiges Pattern bei Argo CD. Dabei handelt es sich um ein Pattern, das unabhängig von GitOps bei Helm einsetzbar ist34, um verschiedene Helm-Charts zu orchestrieren.

Bei Argo CD findet das Pattern häufig Anwendung beim Deployment von Third-Party-Applications (Prometheus, Ingress-Controller etc.). Dabei erzeugt man ein Umbrella-Helm-Chart in einem Repo, das Verweise auf externe Helm-Charts enthält, die in anderen Repos liegen. Den Begriff Umbrella (engl. Regenschirm) kann man hier bildlich verstehen: Das Chart umfasst die anderen wie ein Regenschirm.

Das Umbrella Chart erzeugt man mittels helm create und gibt die Third-Party-Application als Dependency in der Chart.yaml an. Die Konfiguration der Anwendung erfolgt dann in der values.yaml des Umbrella Charts. Weitere Ressourcen kann man Helms Konventionen entsprechend mittels des Ordners templates deployen. Abschnitt 6.7.2 auf Seite 175 beschreibt dieses Pattern in der Praxis. In Abb. 6–16 auf Seite 176 sehen wir die Ordnerstruktur eines Umbrella Charts (unter argocd/argocd) sowie eine beispielhafte Chart.yaml in Listing 6–5 auf Seite 177.

Diese Kombination von zwei Repos war mit der Argo CD Application CRD vor der Einführung von Multi-Source Applications nicht möglich. Allerdings haben Umbrella Charts bei Argo CD auch Einschränkungen: Anders als beim Deployment von Helm-Charts mittels Application kann man bei Umbrella Charts keine Charts aus Git-Repos verwenden und nicht auf die in Argo CD konfigurierten Secrets zur Authentifizierung zugreifen. Daher ist es wahrscheinlich, dass Multi-Source Applications das Umbrella Charts-Pattern bei Argo CD langfristig ersetzen.

Andere CM-Tools im CI-Server

Generell könnten wir auch CM-Tools auf dem CI-Server ausführen und das Ergebnis in Git oder einer OCI-Registry speichern (Rendered Manifest-Pattern). Analog zu helm template können wir also auch kustomize build oder kubectl kustomize im CI-Server ausführen. Dieses Vorgehen erlaubt es uns außerdem, beliebige CM-Tools einzusetzen, auch wenn der GitOps-Operator diese nicht unterstützt. Beispiele sind CM-Tools wie JSonnet, Cuelang oder Timoni, die mit Ausnahme von JSonnet in Argo CD nicht in GitOps-Operatoren integriert sind. Wie wir in Abschnitt 4.8 auf Seite 80 erfahren, sieht Flux den Weg über den CI-Server sogar als offizielle Lösung zur Anbindung weiterer CM-Tools vor.

6.5.5Config Update

Unserer Erfahrung nach sind für den Prozess der Promotion folgende drei Fragen essenziell:

Je nach Prozess und Environment sind die letzten beiden Schritte optional. Die dafür notwendigen Aktivitäten fassen wir unter dem Begriff Config Update zusammen.

Je nach Prozess und Environment sind die letzten beiden Schritte optional, wie wir eingangs in Abschnitt 6.5 auf Seite 150 diskutieren.

Wir sehen vier Ansätze für das Config Update:

Die technisch einfachste Lösung ist manuell. Dabei übernimmt alle Aufgaben ein Mensch. Dies ist zunächst einfach umzusetzen. Bei großer Anzahl von Environments, hoher Deployment-Frequenz und generell auf lange Sicht ist es aufwendig, fehleranfällig und ermüdend. Daher bietet sich Automatisierung an. Die folgenden Absätze stellen die oben genannten drei Ansätze zur Automatisierung vor.

Kapitel 7 auf Seite 197 beschreibt die drei Ansätze mit höherer Detailtiefe und beleuchtet dabei zusätzlich das Thema Prüfen.

Config Update mittels CI-Server
image

Abb. 6–12
Config Update und Config Replication mittels CI-Server

Die für viele Applikationsteams offensichtliche Möglichkeit ist, den CI-Server das gesamte Config Update übernehmen zu lassen. Gerade für Teams, die zu GitOps umsteigen, fühlt sich dies oft wie der natürliche nächste Schritt an. Je nach Repo-Patterns und Aufteilung in App- und Config-Repo (siehe Abschnitt 2.3.1 auf Seite 29) kommt dafür ein CI-Job in einem der Repos in Frage. Besonders wenn der CI-Server ohnehin schon für die Umsetzung von Config Replication (siehe Abschnitt Config Replication) und/oder des Rendered Manifest-Patterns (siehe Abschnitt 6.5.4 auf Seite 160) im Einsatz ist, bietet es sich an auch das Config Update mittels CI-Server zu realisieren.

Wenn der CI-Server bereits die Config ins SCM pusht, was liegt da näher, als auch noch PRs anzulegen? Abb. 6–12 zeigt dies schematisch. Im Gegensatz zum Image Update Controller kann der CI-Server den gesamten Prozess automatisieren. Allerdings muss man diesen Prozess im Zweifel auch selbst implementieren, inklusive dem Anlegen der PRs. Was auf der Abbildung einfach aussieht, enthält tatsächlich einiges an Komplexität: PRs sind nicht über das Git-Protokoll erstellbar. Jedes SCM hat dafür eine eigene API, die der Controller implementieren muss. PRs sind nicht Teil des Git-Protokolls, sie sind über die API des SCM ansprechbar. Daher entwickelt man eine Lösung, die nur für die jeweilige Kombination von CI-Server und SCM funktioniert. Das macht die Portierung zu anderen CI-Servern und SCMs schwieriger. Außerdem sinken die Chancen, eine bereits implementierte Lösung für die jeweils gesuchte Kombination zu finden.

Ein Beispiel ist die GitOps-Build-Lib für Jenkins und SCM-Manager, die wir bereits aus Abschnitt Config Replication kennen. Diese ist so designt, dass zumindest der SCM-Provider einfach auf andere SCMs erweiterbar ist35.

Auch OCI statt Git möglich

Statt eines Push in das Config-Repo ist es auch denkbar, dass der CI-Server die Config in eine OCI-Registry pusht. Voraussetzung ist, dass der GitOps-Operator das Lesen der Config aus OCI-Registries unterstützt, siehe beispielsweise Abschnitt 4.13 auf Seite 90. Da es im OCI-Repo keine Branches und PRs gibt, muss man hier neue Wege für den GitOps-Prozess finden.

Config Update mittels Image Update Controller
image

Abb. 6–13
Config Update mittels Image Update Controller

Eine Alternative stellt die Verwendung eines Image Update Controllers dar, den viele GitOps-Operatoren zur Verfügung stellen. Wie Abschnitt 4.10 auf Seite 85 darstellt, bieten sowohl Argo CD (»Image Updater«) als auch Flux (»Image Automation Controllers«) dafür Lösungen an. Abschnitt 7.3 auf Seite 215 erklärt im Detail, wie man diese verwendet. Abb. 6–13 zeigt schematisch, wie Image Update Controllers funktionieren:

Prinzipiell kann der Image Update Controller das Image auch direkt im Cluster aktualisieren. Dies würde allerdings gegen die GitOps-Prinzipien 1 und 2 verstoßen, mit entsprechenden Nachteilen.

Sowohl Flux36 als auch Argo CD37 können die Änderungen auf neue Branches pushen. Bei Flux ist der Name des Branches fix, bei Argo CD kann man ihn durch Variablen beeinflussen. Beide können keine PRs erzeugen. Der Grund liegt wahrscheinlich auch in der bereits im vorherigen Abschnitt erwähnten Komplexität: Der Controller müsste die API jedes unterstützten SCM implementieren. Ausgeschlossen ist dies nicht, wie beim Thema Preview Environments ersichtlich: Der ApplicationSet-Controller von Argo CD kann mit PRs verschiedener SCMs interagieren. In der Zukunft ist es also nicht ausgeschlossen, dass die Erstellung von PRs in den Image Update Controllers landet. Bis dahin kann ein Workaround sein, mittels CI-Server für bestimmte Branches bei jedem Commit einen PR zu erzeugen, wie in der Flux-Dokumentation38 vorgeschlagen.

Config Update mittels Dependency-Bot

Am besten wäre es, eine Option zu haben, die das Config Update ohne Implementierungsaufwand auf dem CI-Server durchführt und automatisch PRs erstellt. Dies verspricht die Nutzung von Dependency-Bots wie Renovate Bot39 oder Dependabot40. Wie der Name schon erahnen lässt, ist die Aufgabe dieser Tools das Aktualisieren von Dependencies, beispielsweise um bekannte Schwachstellen zu verhindern. Es handelt sich also nicht um spezielle GitOps-Tools. Ein Dependency-Bot ist eine Software, die Dependencies in Git-Repos erkennen und gegen deren Quellen auf neuere Versionen prüfen kann. Je nach Konfiguration erstellt der Dependency-Bot dann PRs oder aktualisiert die Versionen direkt im Git. Klassisch geht es dabei um Dependencies aus der Programmierung wie beispielsweise

image

Abb. 6–14
Config Update mittels Dependency-Bot

Dependency-Bots können aber auch Kubernetes- oder Helm-YAML-Dateien verarbeiten. Dies ermöglicht den Einsatz im Kontext von GitOps. Die YAML-Dateien enthalten Dependencies auf OCI-Images, die auf OCI-Registries verweisen. Diese kann der Dependency-Bot prüfen. Wie Abb. 6–14 zeigt, kann der Dependency-Bot beim Finden neuer Versionen automatisch PRs erstellen. Die Verfügbarkeit eines zum eingesetzten SCM kompatiblen Dependency-Bots vorausgesetzt ist die Automatisierung der Config Updates also reine Konfigurationssache. In Abschnitt 7.3.3 auf Seite 217 erfahren wir mehr über die Vor- und Nachteile der Verwendung von Dependency-Bots mit GitOps.

6.6Verdrahtungs-Patterns

Die letzte Kategorie von Patterns beschäftigt sich damit, wie man den GitOps-Operator mit den Repos, Ordnern, Environments etc. verdrahtet. In diesem Bereich benennen wir die beiden Patterns Bootstrapping und Linking. Bei Letzterem beschreiben wir zudem Nesting (als eine Verallgemeinerung von App of Apps) und Templating.

6.6.1Bootstrapping

Der erste Schritt beim Verdrahten ist das Bootstrapping des GitOps-Operators. Dabei installiert man den GitOps-Operator in den Cluster und richtet ihn so ein, dass er auf ein erstes Config-Repo zeigt. Das Bootstrapping ist per kubectl realisierbar, meist gibt es aber spezielle CLIs für die jeweiligen GitOps-Operatoren, die einfacher zu benutzen sind. Abschnitt 4.2 auf Seite 71 beschreibt die Optionen, die uns Argo CD und Flux bieten.

GitOps-Operator per GitOps betreiben

Idealerweise bildet man dabei auch die Konfiguration des GitOps-Operators in Git ab, sodass zukünftig auch Updates und Änderungen am GitOps-Operators per GitOps erfolgen können. Dabei ist es wichtig, sich bewusst zu sein, dass besonders an dieser Stelle Situationen entstehen können, die ein imperatives Eingreifen erfordern können (siehe auch Kapitel 9 auf Seite 247).

Ein Beispiel ist die Änderung der Referenz für das SSH-Secret, das der Operator benutzt. Wenn diese Änderung einen Zugriff auf das Repo unmöglich macht, ist es auch unmöglich, dies per GitOps zu reparieren.

Dennoch ist der Betrieb des GitOps-Operators per Git empfehlenswert, da es in den meisten Fällen ein einheitliches Vorgehen sowie die Vorteile von GitOps bietet.

6.6.2Linking

Grouping

Nach dem Bootstrapping erfolgt das Linking, also das Anbinden weiterer Repos, Ordner oder Branches. Damit einhergehend ist oft das Grouping, die Gruppierung bestimmter Ressourcen, beispielsweise alles, was einer Anwendung oder einem Environment zugehörig ist. Sowohl Linking als auch Grouping erfolgen typischerweise mittels spezifischer CRDs, beispielsweise der Kustomization bei Flux und der Application bei Argo CD.

Nesting, beispielsweise App of Apps

Das Linking erfolgt oft geschachtelt (Nesting). Bei Argo CD gibt es für dieses Pattern eine eigene Bezeichnung: App of Apps. Mit der ersten Application, die man beim Bootstrapping angelegt hat, deployt man weitere Applications, die jeweils noch weitere Applications beinhalten können.

Bei Flux ist das gleiche Vorgehen mit Kustomizations gängig, allerdings gibt es dafür keine eigene Bezeichnung.

Templating beim Linking und Grouping

Ein neueres Feature von GitOps-Operatoren ist die Verwendung von Templating beim Linking und Grouping. Dabei handelt es sich um dasselbe Prinzip wie bei Preview Environments. Bei Argo CD zum Beispiel gibt es neben dem PR-Generator noch weitere Generatoren für ApplicationSets.

Dadurch kann man beispielsweise Environments in Ordnern modellieren, und der Git-Generator41 erzeugt dann für jeden Ordner eine Application. Auch Konfigurationsdateien kann dieser aus dem Config-Repo lesen. Abschnitt 6.7.1 zeigt ein Beispiel für umfangreiche Nutzung dieser Features. Eine weitere Option ist die Generierung auf Basis von Listen im ApplicationSet42. Damit stehen umfassende Möglichkeiten bereit, das Linking mit geringer Redundanz durchzuführen. Dies skaliert auch sehr gut, denn ob man mit diesen Generatoren eine oder einhundert Applications generiert, macht zumindest für die Config keinen großen Unterschied.

6.7Beispiele für Config-Repos

Bis hierher beschreibt und kategorisiert dieses Kapitel recht theoretisch verschiedene GitOps-Patterns. Zum Abschluss illustrieren wir die beschriebenen Patterns an sechs echten Beispielen öffentlicher Config-Repos. Darunter sind jeweils zwei mit Fokus auf Argo CD oder Flux, eines, das für beide denkbar ist, und eines, das mit den anderen kombinierbar ist. Außerdem werden wir feststellen, dass die Unterschiede zwischen Argo CD und Flux im Design gering sind. Auch hier stellen wir wiederkehrende Themen fest, die teils unterschiedlich benannt und in Repo-Strukturen abgebildet werden.

6.7.1Beispiel 1: Argo CD Autopilot

image

Abb. 6–15
Repo-Struktur bei argocd-autopilot

Argo CD Autopilot ist eine CLI, die 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. Mehr Details zu Autopilot sowie zu den grundlegenden Bausteinen von Argo CD, nämlich Application, ApplicationSet, und AppProject gibt Kapitel 3 auf Seite 47.

Bootstrapping

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 (Befehl project create) und eine Application (Befehl app create) notwendig. Abb. 6–15 auf der vorherigen Seite zeigt die Repo-Struktur nach Ausführung von Listing 6–1.

Listing 6–1
Bootstrapping mittels Argo CD Autopilot

1

# Basic setup

2

argocd-autopilot repo bootstrap

3

 

4

# Add first app

5

argocd-autopilot project create staging

6

argocd-autopilot app create my-app -p staging --app ...

7

 

8

# Add additional stage with app

9

argocd-autopilot project create production

10

argocd-autopilot app create my-app -p production --app ...

Listing 6–2
in-cluster.json

1

{"name":"in-cluster",

2

"server":"https://kubernetes.default.svc"}

Listing 6–3
staging.yaml

1

apiVersion: argoproj.io/v1alpha1

2

kind: ApplicationSet

3

metadata:

4

name: staging

5

spec:

6

generators:

7

- git:

8

files:

9

- path: apps/**/staging/config.json

10

repoURL: https://gitlab.com/gitops-book/

11

image argocd-autopilot-example.git

12

template:

13

metadata:

14

name: staging-{{ userGivenName }}

15

namespace: argocd

16

spec:

17

destination:

18

namespace: '{{ destNamespace }}'

19

server: '{{ destServer }}'

20

project: staging

21

source:

22

path: '{{ srcPath }}'

23

repoURL: '{{ srcRepoURL }}'

24

targetRevision: '{{ srcTargetRevision }}'

Listing 6–4
Von Autopilot generierte config.json für das Staging Environment

1

{

2

"appName": "my-app",

3

"userGivenName": "my-app",

4

"destNamespace": "default",

5

"destServer": "https://kubernetes.default.svc",

6

"srcPath": "apps/my-app/overlays/staging",

7

"srcRepoURL": "https://gitlab.com/gitops-book/

8

image argocd-autopilot-example.git",

9

"srcTargetRevision": "",

10

"labels": null,

11

"annotations": null

12

}

Die Struktur kannst du im Repo »gitops-book/argocd-autopilot-example«44 interaktiv einsehen und ausprobieren.

Zusammenhänge der Repo-Struktur

Der folgende Abschnitt beschreibt die Zusammenhänge der Repo-Struktur anhand der Nummern in Abb. 6–15:

  1. Die Application autopilot-bootstrap verwaltet den Ordner bootstrap und bindet damit alle anderen Applications in dieser Aufzählung ein. autopilot-bootstrap selbst steht nicht unter Versionsverwaltung. Stattdessen übermittelt die CLI sie einmalig imperativ während des Bootstrappings an den Cluster.
  2. Die Application argo-cd verwaltet Argo CD selbst per GitOps.

    Installation von Argo CD über stable-Branch

  3. Dazu enthält sie eine kustomization.yaml, die weitere Ressourcen aus dem Internet einbezieht. Sie verweist direkt auf eine Kustomization im Repo von Autopilot45, die wiederum alle zur Installation von Argo CD notwendigen Ressourcen aus dem Repo von Argo CD selbst46 holt. Dabei verweist sie auf den Branch stable von Argo CD.

    Clusterweite Ressourcen

  4. Das ApplicationSet cluster-resources referenziert mittels Git-Generator für Dateien (siehe Abschnitt 6.6.2 auf Seite 168) alle JSON-Dateien unter dem Pfad bootstrap/cluster-resources.
    Damit kann man clusterweite Ressourcen verwalten, die von mehreren Applications genutzt werden, wie beispielsweise Namespaces.
  5. Die Application root ist dafür zuständig, alle AppProjects und Applications einzubinden, die im Ordner projects/ liegen. Nach dem Ausführen des Befehls bootstrap ist dieser Ordner noch leer.

    Mehrere Environments

  6. Jede Ausführung des Befehls project create führt zur Generierung einer Datei, die ein AppProject und ein zugehöriges ApplicationSet enthält. Listing 6–3 auf Seite 171 zeigt einen Ausschnitt aus dieser Datei. Diese sind für die Umsetzung mehrerer Environments gedacht.
  7. Der Befehl app create befüllt den Ordner apps mit der Struktur für eine Application in einem Environment. Dazu gehört 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 (siehe Listing 6–4 auf der vorherigen Seite). Die config.json enthält dabei die Werte, die das ApplicationSet in die Application einträgt, vergleiche dazu Listing 6–3 auf Seite 171.
    Der Zweck dieses Ordners ist die Ablage der Config, die spezifisch für ein Environment ist.
  8. Zusätzlich erzeugt der Befehl app eine kustomization.yaml, die auf den Ordner base zeigt. Der Zweck dieses Ordners ist die Ablage der Config, die in allen Environments gleich ist. Diese Aufteilung vermeidet redundante Config.

Analog zu 6. bis 8. kann man weitere Environments und Anwendungen hinzufügen. Abb. 6–15 auf Seite 170 zeigt hier beispielhaft eine Anwendung my-app mit zwei Environments staging und production.

Autopilot in Produktion

Abschließend wollen wir betonen, dass wir einige Gründe sehen, 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 in der Organisation »argoproj-labs«. Die Commits kommen hauptsächlich von einem Unternehmen: Codefresh. Dadurch besteht ein höheres Risiko für das Auftreten von Breaking Changes oder sogar für das Einstellen des Projekts. Aufgrund dieser Punkte halten wir eine Verwendung von Autopilot in Produktion im aktuellen Status nicht für empfehlenswert.

Standardmäßig ist außerdem die Version von Argo CD nicht gepinnt. Stattdessen verweist die kustomization.yaml (3. in Abb. 6–15 auf Seite 170) schlussendlich auf den stable-Branch des Argo-CD-Repos. Wir empfehlen, per Kustomize eine deterministische Version zu referenzieren (siehe Abschnitt 1.3.2 auf Seite 18). Eine derart laxe Versionsangabe schreit nach Problemen:

Außerdem ist die Repo-Struktur, die Autopilot erzeugt, kompliziert, also schwierig zu verstehen und zu warten. Das Ausmaß an Konzentration, das zum Verständnis von Abb. 6–15 auf Seite 170 und der zugehörigen Beschreibung nötig ist, spricht dabei schon eine deutliche Sprache. Dazu kommen weniger offensichtliche Themen:

Mandantentrennung

Autopilot modelliert Environments über Argo CD Projects (6. und 7. in Abb. 6–15). Wie wäre bei dieser Monorepo-Struktur eine Trennung unterschiedlicher Applikationsteams, also eine Mandantentrennung, 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 Plattformteams aus und implementieren ein Repo per Team-Pattern. Dies ist mit dem Autopilot nicht intuitiv.

Das nächste Beispiel zeigt hierfür eine Alternative auf.

6.7.2Beispiel 2: GitOps Playground

Produktionsreife Repo-Struktur für Argo CD

Der GitOps Playground stellt ein OCI-Image bereit, das einen Kubernetes-Cluster mit allem provisionieren kann, was für den Betrieb mittels GitOps nötig ist, und veranschaulicht dies mittels Beispielapplikationen. Zu den installierten Tools gehören GitOps-Operator, SCM, CI-Server, Monitoring und Secrets Management. Der gesamte Stack lässt sich außerdem mittels eines einzigen Befehls auf einem lokalen Kubernetes-Cluster starten. Durch den Einsatz von k3d49 wird dafür nur Docker benötigt.

image

Abb. 6–16
Zusammenhang der Config-Repos im GitOps Playground (Argo CD)

Die Repo-Struktur des GitOps Playground ermöglicht einen Betrieb von Argo CD selbst per GitOps.

Abb. 6–16 zeigt, wie die Config-Repos verdrahtet sind.

Bootstrapping

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

Die Installation von Argo CD erfolgt beim Bootstrapping mittels des Helm-Charts von Argo CD.

Um das Bootstrapping abzuschließen, wendet der GitOps Playground außerdem zwei Ressourcen imperativ auf den Cluster an:

Diese sind ebenfalls im argocd-Repo enthalten.

Nach Abschluss des Bootstrappings können wir alles, auch Argo CD selbst, per GitOps verwalten.

Listing 6–5
Chart.yaml des Umbrella Charts für Argo CD

1

apiVersion: v2

2

name: argocd

3

version: 1.0.0

4

description: Wraps the upstream argo-cd helm chart

5

dependencies:

6

- name: argo-cd

7

version: 5.23.5

8

repository: https://argoproj.github.io/argo-helm

Listing 6–6
Chart.lock des Umbrella Charts für Argo CD

1

dependencies:

2

- name: argo-cd

3

repository: https://argoproj.github.io/argo-helm

4

version: 5.23.5

5

digest: sha256:64chars

6

generated: "2023-05-35T13:00:00.444549403+02:00"

Zusammenhänge der Repo-Struktur

Der folgende Abschnitt beschreibt die Zusammenhänge der Repo-Struktur anhand der Nummern in Abb. 6–16:

  1. Die Application bootstrap verwaltet den Ordner applications, und damit sich selbst (bootstrap.yaml).
    Dadurch kann man Änderungen an bootstrap per GitOps vornehmen. bootstrap enthält außerdem noch andere Anwendungen (App-of-Apps-Pattern).

    Installation von Argo CD

  2. Die Application argocd verwaltet den Ordner argocd, der die Ressourcen von Argo CD als Umbrella Chart enthält.
    Dabei enthält die values.yaml die eigentliche Config der Argo-CD-Instanz. Zusätzliche Ressourcen (beispielsweise Secrets und Ingresses) kann man über den Ordner template deployen. Das eigentliche Helm-Chart deklariert man in der Chart.yaml (siehe Listing 6–5).
  3. Die Chart.yaml enthält das Helm-Chart von Argo CD als Dependency. Die Datei verweist auf eine deterministische Version des Charts, die aus dem Chart-Repo im Internet bezogen wird. Die Version ist gepinnt und per Digest abgesichert durch die Chart.lock, siehe Listing 6–6.
    Argo CD kann man per GitOps updaten, indem man helm dep update ausführt und das Ergebnis ins Git pusht.
  4. Die Application projects verwaltet den Ordner projects, der wiederum die folgenden AppProjects enthält:
    1. argocd, das beim Bootstrapping zum Einsatz kommt
    2. das in Argo CD fest verbaute default, dessen Rechte gegenüber dem Standardverhalten beschränkt sind, um die Angriffsfläche zu reduzieren (siehe »Argo CD End User Threat Model«50)
    3. ein AppProject pro Team (zur Implementierung von Least Privilege und Notifications pro Team):
      1. cluster-resources (für Plattform-Admins, benötigt mehr Rechte auf dem Cluster)
      2. example-apps (für Entwickelnde, benötigt weniger Rechte auf dem Cluster)

        Clusterweite Ressourcen

  5. Die Application cluster-resources verweist auf das Repo cluster-resources und darin auf den Ordner argocd. Dieses Repo hat die typische Ordnerstruktur eines Config-Repos (mehr Details siehe nächster Schritt).
    Auf diese Weise nutzen Plattform-Admins GitOps auf die gleiche Weise wie ihre »Kunden« (die Entwickelnden) und können so besseren Support leisten (dogfooding).

    Typische Ordnerstruktur eines Config-Repos

  6. Die Application example-apps verweist auf das Repo example-apps und darin auf den Ordner argocd. Wie die cluster-resources hat es auch die typische Ordnerstruktur eines Config-Repos:
  7. Die Application misc zeigt auf den Ordner misc.

    Mehrere Environments

  8. Die Application my-app-staging verweist auf den Ordner apps/ my-app/staging innerhalb desselben Repos. Dies bietet eine Ordnerstruktur für die Promotion, also für mehrere Environments.
  9. Die Application my-app-production realisiert die zugehörige Produktionsumgebung, indem sie auf den Ordner apps/my-app/production innerhalb desselben Repos verweist.
    Generell raten wir zur Vorsicht bei direkten Änderungen am production-Ordner und diesen zu schützen, entweder durch Prozesse wie Reviews und PRs oder Einstellungen im SCM, wie Abschnitt 6.5.1 auf Seite 151 beschreibt.

Alternativen zur Erstellung der Applications pro Environment

Anstelle der in Abb. 6–16 auf Seite 176 gezeigten einzelnen YAML-Dateien pro Application (my-app-staging und my-app-production) sind folgende Ansätze denkbar:

Von oben nach unten sinkt die Redundanz und dafür steigt das Wissen, das nötig ist, um die Lösung zu verstehen. Das Treffen dieses Kompromisses ist unserer Erfahrung nach eine sehr individuelle Entscheidung.

Mehrere Cluster

Der GitOps Playground selbst verwendet standardmäßig einen einzelnen Kubernetes-Cluster und implementiert damit das Instance per Cluster-Pattern. Die gezeigte Repo-Struktur ist jedoch auch für mehrere Cluster nach dem Hub and Spoke-Pattern verwendbar: Zusätzliche Cluster kann man entweder in der values.yaml oder als Secrets mittels des templates-Ordners definieren.

Alternativen zum Umbrella Chart

Wie Abschnitt 6.5.4 auf Seite 160 beschreibt, hat das Umbrella Chart-Pattern den Nachteil, dass man keinen Zugriff auf die in Argo CD definierten Secrets hat. Wer in Umgebungen mit Anspruch an hohe Sicherheit arbeitet, kann dadurch Probleme bekommen, weil man dem Umbrella Chart-Pattern keine Credentials mitgeben kann. Alternativen sind Multi-Source Applications (sobald sie in Argo CD stabil sind) oder die Verwendung des Argo CD Operators, den wir aus Abschnitt 4.2 auf Seite 71 kennen. Dieser ist zwar auch noch nicht stabil, allerdings über den OperatorHub gut integriert in beispielsweise OpenShift.

6.7.3Beispiel 3: Flux Monorepo

image

Abb. 6–17
Repo-Struktur von flux2-kustomize-helm-example (erweitert)

Nach diesen nicht trivialen Beispielen im Kontext von Argo CD beginnt unser praktischer Einblick in die Welt von Flux mit einer positiven Überraschung: Es sind keine Vorüberlegungen oder externe Tools zur Installation notwendig. Das flux CLI bringt den Befehl bootstrap 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 und als grobe Vorlage für eigene Repo-Strukturen dienen können. Wir beginnen mit dem Monorepo.

Zusammenhänge der Repo-Struktur

Der folgende Abschnitt beschreibt die Zusammenhänge der Repo-Struktur anhand der Nummern in Abb. 6–17:

Bootstrapping

  1. Im Ordner clusters/production/flux-system generiert der Befehl flux bootstrap alle zur Installation von Flux notwendigen Ressourcen sowie ein GitRepository und eine Kustomization. Zum Bootstrapping wendet flux diese einmalig imperativ auf den Cluster an. Die Kustomization referenziert dann ihren eigenen Überordner production. Ab hier wird alles über GitOps verwaltet.

    Clusterweite Ressourcen

  2. Die flux-system Kustomization deployt zudem eine weitere Kustomization infrastructure, die auf den gleichnamigen Ordner zeigt. Mittels dieser kann man clusterweite Ressourcen wie Ingress-Controller und Network Policies deployen.

    Verschiedene Environments

  3. Außerdem deployt flux-system die Kustomization apps. Diese zeigt auf den Unterordner des jeweiligen Environments unter apps, beispielsweise apps/production.
  4. Im Ordner apps/production liegt eine kustomization.yaml, welche die Ordner aller Anwendungen einbindet.
  5. Im Ordner jeder Anwendung liegt eine weitere kustomization.yaml, welche die 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.

Mehrere Cluster

Analog zum Ordner apps gibt es im Ordner clusters ebenfalls je einen Ordner pro Environment. Flux implementiert also das Instance per Cluster-Pattern: eine Flux-Instanz pro Environment auf einem eigenen Cluster. Die Strukturen, die oben am Beispiel von production beschrieben sind, existieren analog noch einmal für staging, was in der Abbildung nur angedeutet ist. Für diese führt man gegen den staging-Cluster erneut den Befehl bootstrap aus. Nach diesem Muster ist dies für zusätzliche Environments erweiterbar.

Verwaltung mehrerer Anwendungen

Das öffentliche Beispiel selbst zeigt nur die Verwaltung einer einzelnen Anwendung, und es ist nicht offensichtlich, wie man weitere hinzufügt. Unsere Erfahrung aus der Praxis ist, dass eine Flux-Instanz meist mehrere Anwendungen verwaltet. Daher zeigt Abb. 6–17 eine um die Erkenntnisse aus einem Issue53 erweiterte Variante, die mehrere Anwendungen unterstützt. Diese Struktur kannst du im Repo »gitops-book/flux2-kustomize-helm-example« bei GitLab54 einsehen und ausprobieren.

Herausforderungen

Der Nachteil dieser Struktur ist, dass eine einzige Kustomization pro Environment alle Anwendungen unter apps deployt. Hier wird also das Grouping nicht pro Anwendung durchgeführt. Beispielsweise zeigt eine grafische Oberfläche wie Weave GitOps dann alle in der Kustomization enthaltenen Ressourcen als eine »App« auf der Oberfläche an (siehe Abb. 6–18). Dies wird schnell unübersichtlich. Analog zur Verwendung von Applications bei Argo CD (siehe vorherige Beispiele) ist es auch bei Flux denkbar, eine Kustomization pro Anwendung anzulegen statt einer einzigen Kustomization in der Datei apps.yaml. Dies bedarf zwar mehr Wartung, sorgt aber für übersichtlichere Strukturen.

Verwendung mit Argo CD

Die beschriebene Repo-Struktur würde mit wenigen Änderungen auch für Argo CD funktionieren. Statt der Kustomizations würde man Applications zum Linking einsetzen. Beide Tools können mit den kustomization.yamls umgehen.

image

Abb. 6–18
Mehrere Anwendungen in einer Kustomization (Screenshot Weave GitOps)

6.7.4Beispiel 4: Flux Repo per Team

image

Abb. 6–19
Zusammenhang der Config-Repos bei flux2-multi-tenancy

Wem für seine Organisation ein Repo per Team sinnvoller erscheint, findet im Flux-Projekt auch dafür ein offizielles Beispiel. Flux verwendet dafür den Begriff Multi-Tenancy. Statt des allgemeineren Begriffs Tenant (engl. Mandant) verwenden wir den zum Pattern passenden Begriff Team.

Mehrere Cluster, clusterweite Ressourcen

Einige Punkte sind bereits aus dem vorherigen Beispiel bekannt:

Zusammenhänge der Repo-Struktur

Der folgende Abschnitt beschreibt die Zusammenhänge der Repo-Struktur anhand der Nummern in Abb. 6–19:

Bootstrapping

  1. Wie in Abschnitt 6.7.3 auf Seite 180 beschrieben befinden sich im Ordner clusters/production/flux-system die von der CLI generierten Ressourcen zum Bootstrapping von Flux. Zusätzlich ist in der kustomization.yaml in diesem Ordner die Mandantentrennung (»Multi-Tenancy Lockdown«) realisiert, die Abschnitt 4.11 auf Seite 88 genauer beschreibt.
  2. Die Kustomization flux-system deployt eine Kustomization tenants, die auf den Ordner tenants/production zeigt.
  3. In diesem Ordner liegt eine kustomization.yaml, welche die Ordner aller Teams in einem Environment einbindet, beispielsweise tenants/production/team1.
  4. Im Ordner jedes Teams liegt eine weitere kustomization.yaml, welche 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 tenants/production/team1) über die base gelegt.
  5. Konkret können sich im Ordner tenants/base/team1 mehrere Dateien befinden, die eine weitere kustomization.yaml zusammenfügt. Die Aufteilung in mehrere Dateien erleichtert die Übersicht und die Promotion. Dazu mehr in Abschnitt 6.7.6 auf Seite 187.
    Die Datei rbac.yaml enthält Namespace, ServiceAccount und Role-Binding, die spezifisch für das Team sind und sich mit dem Befehl flux create tenant erstellen lassen, den wir in Abschnitt 4.11 auf Seite 88 näher beschreiben.
  6. Das Team-Repo wird dabei über die Datei sync.yaml eingebunden, in der sich ein GitRepository und eine weitere Kustomization befinden. Letztere verwendet wegen des Multi-Tenancy Lockdown einen teamspezifischen ServiceAccount, der die Rechte des Teams auf seine eigenen Namespaces einschränkt.

    Verschiedene Environments

    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übergelegt wird, beispielsweise tenants/ production/team1/path.yaml. In unserem Beispiel ist dieser Pfad production, analoge Strukturen lassen sich auch für weitere Environments aufbauen.

Team-Repo mit mehreren Anwendungen

Der Aufbau des Team-Repos entspricht dann genau dem des Ordners app aus Abschnitt 6.7.3 auf Seite 180. Auch hier haben wir dieses so angepasst, dass es mehrere Anwendungen unterstützt. Diese Struktur kannst du im Repo »gitops-book/flux2-multi-tenancy« bei GitLab56 einsehen und ausprobieren.

Herausforderungen

Daher gilt auch hier der Nachteil, dass eine einzige Kustomization pro Environment alle Anwendungen im Team-Repo deployt und dies unübersichtlich wird. Auch hier könnten wir dies mit einer Kustomization pro Anwendung umgehen (siehe Abschnitt 6.7.3 auf Seite 180). Und auch in diesem Repo zeigen Diskussionen in den Issues57, dass die Flux-Beispiele eher Inspiration als direkt Vorlage für die Praxis sind.

Außerdem kann man an dieser Stelle darüber streiten, ob es sinnvoller ist, eine Flux-Instanz pro Environment oder pro Team zu betreiben. Beides möchte man eigentlich voneinander trennen. Der am stärksten isolierte Ansatz wäre eine Flux-Instanz pro Team und pro Environment. Ob man sich diesen hohen Wartungsaufwand leisten will, hängt vom jeweiligen Einsatzszenario ab.

6.7.5Beispiel 5: The Path to GitOps

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. Abb. 6–20 zeigt sein Beispiel für ein Monorepo.

Dieses findest du bei GitHub, und zwar sowohl für Argo CD (das wir hier beschreiben) als auch für Flux59.

Unterschiedliche Bezeichnungen

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.

image

Abb. 6–20
Die Repo-Struktur bei example-kubernetes-go-repo

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

Verschiedene Environments

Clusterweite Ressourcen

Um Wiederholungen zu den vorherigen Beispielen zu vermeiden, beschreibt dieses Kapitel Zusammenhänge der Repo-Struktur nicht im Detail. Interessant sind jedoch die folgenden Punkte:

Mehrere Cluster

Beispiel für Flux

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 empfehlen wir allerdings, die Struktur zu verwenden, die der Befehl flux bootstrap erzeugt. 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.

6.7.6Beispiel 6: Environment-Varianten

Viele Environments

Kompatibilität mit Flux

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 ist daher mit den anderen Beispielen kombinierbar. Dieses Beispiel fokussiert sich auf die Umsetzung einer großen Anzahl von Environments. Es zeigt, wie man verschiedene Environments (integration, load, prod, qa und staging) in unterschiedlichen Regionen (asia, eu, us) ausrollen kann, ohne dass viel redundante Config entsteht. Insgesamt entstehen so elf Environments. In jedem Environment soll zudem zwischen prod und non-prod unterschieden werden. Dabei 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 man es ohne Änderungen auch mit Flux einsetzen, da es das Linking ausschließlich per kustomization.yaml realisiert.

image

Abb. 6–21
Die Ordnerstruktur bei gitops-Environment-promotion

Abb. 6–21 zeigt die Struktur vereinfacht auf fünf Environments. Ausgangspunkt sind die Unterordner eines Environments im Ordner envs, beispielsweise envs/prod-eu. Diese Unterordner würden, wie in anderen Beispielen gezeigt, per Argo CD Application oder Flux Kustomization eingebunden. 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 von variants, 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.

Umsetzung mit Helm

Grundsätzlich kann man dieses Beispiel auch per Helm umsetzen, dies wäre aber umständlicher und es würde die Verwendung von speziellen CRDs wie Argo CD Application oder Flux HelmRelease statt universeller kustomization.yaml erfordern.

Einfache Promotion

Dieses Beispiel liefert zudem 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 durchführbar ist. Es ist nicht notwendig, Text auszuschneiden und einzufügen, was den Prozess vereinfach und das Risiko von Fehlern verringert. Außerdem sind die Diffs einfacher zu lesen.

6.8Mandantentrennung

Aus den vorherigen Abschnitten und Kapiteln haben wir viel Rüstzeug gesammelt, um umfassender auf das Thema Mandantentrennung einzugehen.

Typischerweise trennen wir Teams, Environments oder beides voneinander.

Zur Umsetzung stehen uns folgende technische Möglichkeiten zur Verfügung:

Im Folgenden betrachten diese im Einzelnen, bevor wir uns konkret der Frage widmen, wie wir damit Teams und/oder Environments trennen.

Anforderungen abhängig von der eigenen Organisation

Wie so oft ist zu Beginn ein Blick auf die Anforderungen sinnvoll. Was unter Mandanten verstanden wird und wie stark die Trennung ausgeprägt ist, hängt von den Rahmenbedingungen der jeweiligen Organisation ab. Hier können beispielsweise die Teamtopologie, Organisationsstruktur (Plattformteam mit Applikationsteams, oder technisch autonome Teams, die alles selbst betreiben) und Betriebsmodell des GitOps-Operators (SaaS/PaaS oder eigener Betrieb) und externe Faktoren wie Regulierung und Standardisierung und nicht zuletzt persönliche Präferenzen, Erfahrungen und Meinungen unterschiedlich sein.

6.8.1Rolle der GitOps-Operatoren

Die Rahmenbedingungen einer Organisation nehmen direkt Einfluss auf die Entscheidung zur Anzahl von Instanzen der GitOps-Operatoren: Soll jedem Mandant eine dedizierte Instanz zur Verfügung stehen oder ist es denkbar, dass mehrere Mandanten sich eine Instanz teilen? Diese Mandantentrennung innerhalb einer Instanz muss natürlich der GitOps-Operator unterstützen. Aus Abschnitt 4.11 auf Seite 88 wissen wir, dass dies für Argo CD und Flux zutrifft.

Geteilte Instanzen bieten dabei immer weniger Isolation, also auch weniger Sicherheit als dedizierte Instanzen. So empfiehlt beispielsweise Argo CD trotz der umfassenden Optionen zur Mandantentrennung, beim Umgang mit weniger vertrauenswürdigen Aktoren Mandanten durch separate Instanzen zu trennen61. Dieser Meinung schließen wir uns an. Aus unserer Erfahrung ist eine saubere Mandantentrennung innerhalb einer Argo-CD-Instanz nicht trivial. Abschnitt 6.7.2 auf Seite 175 beschreibt beispielsweise die Gefahr durch das default-Projekt, und Abschnitt 4.11 auf Seite 88 zeigt auf, dass ohne »Applications in any namespace«, entweder Self-Service oder Mandantentrennung eingeschränkt ist.

Dedizierte Instanz pro Mandant

Eine dedizierte Instanz pro Mandant, die allein in einem eigenen Cluster läuft, ist also die stärkste Form der Isolation. Die Kehrseite der Medaille ist erhöhter Betriebs- und Ressourcenaufwand.

Mehrere Instanzen im selben Cluster

Um den Aufwand zu reduzieren, haben wir die Möglichkeit, mehrere Instanzen innerhalb eines Clusters, getrennt durch Namespaces, zu betreiben, also das Instance per Namespace-Pattern zu implementieren (siehe Abschnitt 6.3 auf Seite 137). In Abschnitt 4.11 auf Seite 88 erfahren wir, dass dies zumindest bei Argo CD möglich ist. Allerdings ist die Isolation durch Kubernetes-Namespaces schwächer als die Trennung in eigene Cluster. Dazu kommt die Tatsache, dass die CRDs Cluster-Ressourcen sind, also clusterweit gelten. Dies zwingt uns dazu, im Betrieb darauf zu achten, dass die im selben Cluster laufenden Instanzen alle kompatibel zu den CRDs sind.

Unserer Erfahrung nach wird dieses Vorgehen selten angewandt. In On-Premises-Umgebungen kann es ein pragmatischer Kompromiss sein zwischen dort oft höheren Kosten für den Betrieb von Kubernetes-Clustern (im Vergleich zur Public Cloud). Ob es höhere Isolation bietet als die Trennung von Mandanten innerhalb einer Operator-Instanz, kann man nicht pauschal beantworten.

Geteilte Instanz

Bei der Trennung von Mandanten innerhalb einer Instanz eines GitOps-Operators ist die Konfiguration besonders kritisch. Unterlaufen einem hier Fehler, kann das eine Aufweichung der Trennung zur Folge haben. Diese Konfiguration erfolgt typischerweise durch die RBAC-Methoden des jeweiligen Operators. Abschnitt 4.11 auf Seite 88 zeigt uns, dass bei Argo CD eine Kombination seiner RBAC-Notation und der CRD AppProject zum Einsatz kommt, bei Flux das native RBAC von Kubernetes.

6.8.2Rolle der Repo-Struktur

Neben der Anzahl der Operatoren ist die Repo-Struktur entscheidend für die Mandantentrennung. Wie Abschnitt 6.5.1 auf Seite 155 ausführt, können wir Environment mittels Branch per Environment, Folder per Environment und Repo per Environment trennen. Dabei nehmen Isolation und Aufwand in der Reihenfolge der Nennung zu. Auch bei den Repo-Strukturen spielen Rahmenbedingungen wie die Teamtopologie wieder eine Rolle, hier kommen Patterns wie Monorepo und Repo per Team zum Einsatz.

Repo-Strukturen für technisch autonome Teams

Ein technisch autonomes Team, das alles, inklusive GitOps-Operator selbst betreibt, kann die Komplexität mit einem Monorepo geringer halten. Dazu passende Beispiele für Repo-Strukturen besprechen wir in Abschnitt 6.7.1 auf Seite 170 (Argo CD), Abschnitt 6.7.3 auf Seite 180 (Flux) und Abschnitt 6.7.5 auf Seite 185 (Argo CD und Flux).

Repo-Strukturen für Platform Teams

Für Plattformteams macht es unserer Erfahrung nach Sinn, mit getrennten Repos, in diesem Fall Repo per Team zu arbeiten. Es erhöht durch Umsetzung von Least Privilege die Sicherheit. Außerdem reduziert es die Mental Load der Applikationsteams, weil sie nur das sehen, was sie betrifft. Dazu passende Beispiele für Repo-Strukturen besprechen wir in Abschnitt 6.7.2 auf Seite 175 (Argo CD) und Abschnitt 6.7.4 auf Seite 183 (Flux). In diesen Beispielen zeigt sich auch die Aufteilung der Zuständigkeiten: Das Plattformteam verwaltet den GitOps-Prozess und die Kubernetes-Cluster. Dazu gehören:

Die Applikationsteams sind Anwender dieser Dienste.

Anwendungen in mehrere Cluster deployen mittels Templating

Gerade wenn das Plattformteam eine große Anzahl an Clustern verwaltet, zeigt sich einer der Vorteile von GitOps: Durch die Möglichkeiten des Templating, beispielsweise durch ApplicationSets, hält sich der Aufwand auch bei einer großen Anzahl an Clustern in Grenzen. Für clusterweite Ressourcen kann man mittels Cluster-Generator62 eine Application pro Cluster auf Basis einer zentralen Config erzeugen. Wird die Config angepasst (beispielsweise eine neue Version gesetzt), werden automatisch alle Instanzen aktualisiert.

6.8.3Rolle der Cluster

Natürlich spielt auch die Anzahl der Kubernetes-Cluster eine Rolle für die Mandantentrennung.

Eine kostengünstige Möglichkeit ist die Trennung in unterschiedliche Namespaces eines Clusters. Wie schon erwähnt bietet diese geringere Isolation. Eine Möglichkeit, die Isolation weiter zu erhöhen, sind Lösungen wie vCluster63 oder capsule64.

Cluster bieten großes Potenzial der Isolation, können aber auch großen Aufwand verursachen. Dabei ist die Spanne groß, von einem Cluster, der verschiedene Teams mit all ihren Environments beinhaltet, bis hin zu Clustern, die nur ein Team und Environment beinhalten.

6.8.4Teams und Environments trennen

Damit haben wir die technischen Grundlagen abgeschlossen und betrachten nun, welche Optionen sich uns bieten, um Teams oder Environments zu trennen. Was wollen wir eigentlich trennen, Team, Environment oder beides?

Ausprägungen der Mandantentrennung

Die Mandantentrennung ist denkbar in den extremen Ausprägungen

Zwischen diesen extremen Ausprägungen gibt es viele Zwischenstufen. Diese konkretisieren wir anhand eines Beispiels. Um nicht zu kompliziert zu werden, lassen wir dabei folgende Dimensionen außer Acht:

Dabei setzen wir voraus, dass der verwendete GitOps-Operator Multi-Cluster-Management (also das Hub And Spoke-Pattern) unterstützt. Abschnitt 4.12 auf Seite 90 zeigt, dass dies generell sowohl bei Argo CD als auch Flux möglich ist. Bei Flux scheint es aber weniger üblich zu sein, was auch durch die offiziellen Beispiele in Abschnitt 6.7.3 auf Seite 180 und Abschnitt 6.7.4 auf Seite 183 gestützt wird. Insofern kann die Wahl der GitOps-Operators auch Einfluss haben auf die Umsetzung der Mandantentrennung.

Tab. 6–2
Ausprägungen der Mandantentrennung am Beispiel

image

Die Beispiele in Abschnitt 6.7 auf Seite 169 können teilweise als Startpunkt für die Umsetzung einzelner Ausprägungen gewählt werden. So kann für die Ausprägungen Alles geteilt und zentraler Operator Abschnitt 6.7.2 auf Seite 175 und für Operator pro Environment Abschnitt 6.7.4 auf Seite 183 als Beispiel dienen.

Nehmen wir an, wir würden 5 Teams mit jeweils 3 Environments verwalten. Dabei ergäben sich die in Tabelle 6–2 dargestellten Ausprägungen der Mandantentrennung, mit von oben nach unten ansteigender Isolation.

Teams oder Environments?

Die hohe Anzahl an Clustern bei manchen Ausprägungen lässt erahnen, mit welchem Aufwand diese verbunden sein können. In der Praxis ist meist ein Kompromiss zwischen Aufwand und Sicherheit notwendig. Daher können wir uns fragen, was bei der Mandantentrennung wichtiger ist: Teams oder Environments?

Generell ist meist der Aufwand für Cluster-Betrieb größer als für den Operator-Betrieb. Zudem empfiehlt sich, wie eingangs erwähnt, gerade beim Umgang mit weniger vertrauenswürdigen Aktoren die Trennung in separate Operator-Instanzen.

Insofern besteht beispielsweise bei Zentraler Operator mit Cluster-Environments aus Sicht der Sicherheit ein ungünstiges Kosten-Nutzen-Verhältnis: Der hohe Betriebsaufwand für die 15 Cluster bringt wenig, weil die Isolation der Mandanten in einer geteilten Operator-Instanz gering ist.

Daher sollte man sich fragen: Wenn man schon den Aufwand für den Betrieb von Mandanten-Clustern betreibt, liegt es da nicht nahe, dedizierte Operator zu betreiben?

Repos stärker isolieren?

Wenn dann Operatoren und Cluster getrennt sind, können jedoch die Repos zum entscheidenden Angriffsvektor werden. Hier kann man mit Repo per Environment-Pattern oder gar mit getrennte SCM-Instanzen gegensteuern, mit entsprechend weiter steigendem Aufwand.

Den passenden Kompromiss zwischen Aufwand und Sicherheit finden

Dies führt uns zu der Frage, wo der Grenznutzen liegt oder der jeweils passende Kompromiss zwischen Aufwand und Sicherheit. Diese kann man nicht pauschal beantworten. Liegen Regularien vor, gelten natürlich diese. Darüber hinaus kann ein risikogetriebes Vorgehen mehr Klarheit schaffen. Welche individuellen Risiken liegen bei meiner Organisation vor und wie bewerte ich diese? Ein Threat Modelling kann bei der Identifikation der Risiken helfen. Als Starthilfe kann dabei das in Abschnitt 6.7.2 auf Seite 175 erwähnte Threat Model von Argo CD oder das von Flux65 dienen.

Am Ende steht der klassischer Kompromiss zwischen Aufwand und Sicherheit. Welches der jeweils passende für eine Organisation ist, wird auf der Spanne von Startup bis reguliertem Unternehmen mit Sicherheit unterschiedlich entschieden (Wortspiel nicht ganz unbeabsichtigt).

Abschließend möchten wir erwähnen, dass die Sicherheit nicht die einzige nichtfunktionale Anforderung ist. So kann eine Aufteilung in mehrere Cluster auch aus Gründen der Performance durchaus Sinn ergeben.

Auch die Aufteilung auf mehrere GitOps-Operator kann auch Perfomancengründen erforderlich sein: In Abschnitt 4.14 auf Seite 93 besprechen wir beispielsweise empirische Leistungsgrenzen einzelner Argo-CD- oder Flux-Instanzen.

6.9Fazit

Anhand wiederkehrender Elemente bestehender GitOps-Tools beschreibt dieses Kapitel GitOps-Patterns und ordnet sie in die vier Kategorien Operator Deployment, Repository, Promotion und Verdrahtung ein. Die Patterns geben einerseits einen Überblick über die Möglichkeiten bei Design-Entscheidungen für den GitOps-Prozess sowie für die Struktur der zugehörigen Repos und was bei diesen zu beachten ist. Andererseits können die Patterns dazu beitragen, einheitliche Begriffe zu finden und damit die Kommunikation zu erleichtern.

Patterns sind Grundbausteine.

Die Diskussion dieser Patterns mit Experten von Unternehmen unterschiedlichster Größe und Branche zeigen jedoch auch, dass die Patterns eher als grundlegende Bausteine zu sehen sind und die Realität oft komplexer und komplizierter ist. Die Kontexte, Herausforderungen, Rahmenbedingungen und Begrenzungen sind für jede Organisation unterschiedlich. So kann beispielsweise eine Lösung für eine Organisation zwar perfekt, aber aufgrund von Auflagen unmöglich sein. Gleichzeitig sind der Kreativität keine Grenzen gesetzt: Repo-Strukturen können in der Praxis noch umfangreicher, verschachtelter oder hierarchischer werden, um bestimmte Anforderungen zu erfüllen. An dieser Stelle sei noch einmal darauf hingewiesen, dass die gezeigten Patterns in der Praxis oftmals auch gemischt im Einsatz sind. Oftmals ist es also nicht zwangsweise eine Entweder-oder-Entscheidung zwischen Patterns derselben Kategorie. Vielmehr sind sie eine Sammlung von Lösungen aus der Praxis, die als Inspiration dienen sollen.

Erkenntnisse aus Beispiel-Repos

Außerdem betrachtet dieses Kapitel Beispiele aus der Praxis und zeigt darin die Verwendung der Patterns auf. Diese Beispiele liefern Vorlagen, Ideen und Tipps für eigene Projekte und zeigen Herausforderungen und Kompromisse aus der Praxis auf. Dabei zeigen sich einige wiederkehrende Themen, die teils unterschiedlich benannt werden. Dabei handelt es sich um Repo-Strukturen für

Geringe Unterschiede zwischen Strukturen für Argo CD und Flux

Die Beispiele zeigen auch, dass grundsätzlich kaum Unterschiede zwischen Argo CD und Flux bei den Repo-Strukturen notwendig sind. Diese beschränken sich auf Bootstrapping und Linking (Operatorspezifische CRDs).

Mandantentrennung

Das Thema Mandantentrennung bezieht sich meist auf die Trennung von Teams und/oder Environments. Zur Umsetzung kommen typischerweise Instanzen der GitOps-Operatoren, Repo-Strukturen sowie Kubernetes-Cluster und Namespaces zum Einsatz.