⇒ In der DOAG gibt es seit kurzem eine Cloud Native Community. Diese beschäftigt sich nativ mit Cloud-Themen. Eins davon ist "GitOps". Im ersten Teil geht es um Methodik & Tooling und die wesentlichen GitOps-Prinzipien.
--
Einleitung: Dieser mehrteilige Beitrag soll einen Eindruck zu GitOps vermitteln – wo es herkommt, wo es hingeht und was auf dem Weg liegt. Eins vorweg: "Die Cloud", respektive Kubernetes, verzeichnet zwar das erste Auftreten von GitOps, jedoch sind seine Prinzipien und die Einsatzmöglichkeiten universell; sie können – und sollen – damit natürlich nach Möglichkeit überall angewandt werden.
Außerdem sollte GitOps nicht isoliert betrachtet werden. Vielmehr verändern seine Prinzipien die Art und Weise, wie wir professionelle IT betreiben. Es ist das Puzzlestück, das viele 'gehypte' Themen der letzten Jahre erst sinnvoll ermöglicht: autarke Teams, echtes DevOps, ganzheitliches (Software) Engineering inklusive Betrieb aus einer Hand.
Nette Nebeneffekte, die sich einstellen, wenn man GitOps lebt, sind bessere Nachhaltigkeit bei der Arbeit durch die Vermeidung manueller Tätigkeiten und höhere Qualität unserer Systeme.
Methodik & Tooling
Der Begriff GitOps stammt von Alexis Richardson, dem Chef von WeaveWorks (Quelle: https://www.weave.works/blog/gitops-operations-by-pull-request). Seine Firma ist vorrangig im Kubernetes-Umfeld tätig und hat nach ein paar Schlüsselerlebnissen mit ihrer Plattform entdeckt, dass sie da was haben, über das man Reden sollte. Damit man über etwas reden kann, braucht es einen Namen. Der Begriff "GitOps" hat sich durchgesetzt, da er sich als sehr einprägsam herausgestellt hat.
Ähnlich wie in der Bezeichnung "DevOps" ist die Aussagekraft jedoch alles und nichts. Es geht um Versionsverwaltung. Das kann Git sein, muss aber nicht. Es geht um Ops, also Betrieb. Aber es geht auch weit darüber hinaus. Genau wie bei DevOps hat GitOps nichts mit konkretem Tooling zu tun. Es gibt ein paar Tool-Stacks, die GitOps nach seiner Definition umsetzen, man kann allerdings auch auf anderen Wegen den GitOps-Himmel erreichen.
Die Erfinder des Begriffs, Alexis und sein Team bei WeaveWorks, haben jedoch aus der DevOps-Bewegung gelernt. Als DevOps aufkam, hatte jeder eine andere Erklärung dafür, jeder hat es anders umgesetzt und inzwischen kann man DevOps-Engineers einstellen, die ganz sicher wieder ein anderes Verständnis mitbringen als der Auftraggeber. Damit das alles mit GitOps nicht passiert, wurden klare Prinzipien dazu definiert, was erfüllt sein muss, damit sich ein System als "durch GitOps verwaltet" nennen darf. Natürlich interessiert das keinen und jeder, der irgendetwas mit DevOps macht und Git kennt, schreibt GitOps auf sein Produkt.
Für mich und viele meiner Kollegen ist GitOps aber tatsächlich zur Lebenseinstellung geworden. Wir versuchen nicht nur, entsprechend den Prinzipien zu arbeiten, wir richten unsere gesamte Arbeit so aus, dass die Prinzipien pauschal erfüllt werden. Das heißt im Extremfall: Wir machen gewisse Dinge einfach nicht mehr, wenn wir sie nicht entsprechend der GitOps-Prinzipien tun können.
Schauen wir uns nun die Prinzipien an.
GitOps-Prinzipien
WeaveWorks steht der Cloud-Native-Computing-Foundation nahe und inzwischen wurde eine GitOps Working Group innerhalb der Cloud-Native-Computing-Foundation gegründet, der auch Größen wie Amazon, Microsoft und Red Hat angehören (Quelle: https://opengitops.dev/). Diese Arbeitsgruppe hat die originär von WeaveWorks formulierten Prinzipen inhaltlich wie auch sprachlich überarbeitet und in einer Version 1 veröffentlicht. Diese englische Version 1 wurde durch die Community in andere Sprachen übersetzt und der GitOps-Arbeitsgruppe übergeben, wir schauen uns die deutsche Auslegung an (Quelle: https://github.com/open-gitops/documents/tree/main/i18n).
Die folgenden Formulierungen können als offiziell betrachtet werden.
→ 1. Prinzip: Deklarativ
Der Soll-Zustand eines durch GitOps verwalteten Systems muss deklarativ beschrieben sein.
Das 1. Prinzip besagt, dass der Soll-Zustand des Systems deklarativ zu beschreiben ist. Imperative Skripte, die einen Soll-Zustand herstellen, in Git zu verwalten, ist also kein GitOps. Kubernetes-Manifeste sind deklarativ. Sie beschreiben keinen Vorgang, sondern einen Zustand.
Ich möchte, dass mein Deployment drei Pods hat. Ich möchte, dass eine ConfigMap gebunden wird. Ich möchte ein Volume einer gewissen Größe beanspruchen. Alles deklarativ. Kubernetes ist ein perfekter Lebensraum für GitOpsler, das passt ganz natürlich zusammen. Trotzdem ist das 1. Prinzip sehr allgemeingültig; sofern ein System deklarativ beschrieben werden kann, ist man auf dem richtigen Weg.
Wie siehts mit Anwendungskonfiguration aus? Häufig wird Consul, Vault oder irgendein anderes Werkzeug zur Pflege von Konfigurationen verwendet, das dann irgendwie zur Anwendung kommt. Da wird es schwieriger, GitOps-konform zu bleiben. Ein Key-Value-Paar ist so weit zwar auch deklarativ: Ich möchte, dass der Schlüssel abc den Wert xyz erhält. Jedoch bekommen wir damit noch Probleme mit den anderen Prinzipien.
Ein schönes Beispiel ist zum Beispiel auch Grafana. Man kann es vollständig über seine Oberfläche verwalten, also Datasources konfiguieren, Plug-ins hinzufügen, Dashboards bauen und vieles mehr.
Jedoch lässt es sich auch fast komplett automatisch provisionieren. Bis auf die Benutzerverwaltung kann man im Prinzip alles deklarativ definieren. Dashboards malt man natürlich trotzdem in Grafana selbst, dann exportiert man aber das JSON Model und checkt es im Falle von Kubernetes etwa als ConfigMap ein, damit es zukünftig automatisch bereitgestellt wird. So muss man es nur einmal malen und auch nicht ständig von Hand in andere Instanzen importieren.
→ 2. Prinzip: Versioniert und unveränderlich
Der Soll-Zustand wird in einer Weise gespeichert, die Unveränderlichkeit sowie Versionierung erzwingt und die vollständige Historie erhält.
Der Soll-Zustand – natürlich deklarativ beschrieben – soll also versioniert und unveränderlich gespeichert werden. Da ist Git dann raus, schließlich kann man mit der Git-Historie machen, was man will. Ja richtig, und weil mir Git alle Möglichkeiten der Manipulation und Verschleierung gibt, müssen diese unterbunden werden, wenn man ein guter GitOps-Jünger sein will.
Gehen wir nun davon aus, dass unsere Git-Verwaltungslösung jegliches Pfuschen untersagt, dann ist das 2. Prinzip auch schnell erfüllt. Ich möchte meinen Soll-Zustand ändern, ich ändere meine Deklaration und committe die Änderung. Das ist versioniert, wenn auch mit einem hässlichen Hash als Versionsbezeichnung. Unveränderlich ist es auch, wenn wir auf einem geschütztem Branch arbeiten, auf dem nichts überschrieben werden darf.
Zwar ist Git die häufigste Wahl für ein GitOps-System, da bereits damit gearbeitet wird, aber selbstverständlich funktioniert es identisch mit Subversion oder jeder anderen Code-Versionierung. Genauso möglich sind SharePoint sowie viele andere File-Share-Lösungen, da sie meist beim Überschreiben einer Datei eine neue Version anlegen. Sogar spezielle Dateisysteme wären denkbar – bleiben wir jedoch bei Git, das bietet uns über die pure Einhaltung der Prinzipien hinaus noch ein paar weitere Vorteile, wie wir später sehen werden.
Anwendungskonfiguration ist wieder spannend, Versionierung respektive Unveränderlichkeit ist nicht in jedem Key/Value-System möglich, aber es kommen noch ein paar Argumente, warum Anwendungskonfiguration auch ins Git sollte.
→ 3. Prinzip: Automatisch bezogen
Software-Agenten beziehen den beschriebenen Soll-Zustand automatisch.
Hier fängt der Spaß an. Das 3. Prinzip sagt uns, dass wir selbst nichts zu tun brauchen, beziehungswesie viel wichtiger: nichts tun dürfen. Die Arbeit muss von Software-Agenten durchgeführt werden. Und was nicht sofort ins Auge springt: Diese Software-Agenten „beziehen“ den Soll-Zustand, das heißt, niemand bringt die Konfiguration von außen ins System, wie das eine externe Delivery-Pipeline tun würde; das System selbst beschafft sich seine Konfiguration. Da steht noch nicht, was mit dem Soll-Zustand passieren soll, das kommt als Nächstes. Was allerdings enthalten ist, ist ein Security-Feature: Niemand muss auf das System zugreifen können.
Es braucht keine extern gehaltenen Zugangsdaten mit Gott-Rechten, die verloren gehen könnten, da das System Selbstversorger ist. Dieses Prinzip ist leider auch das, welches nicht für jedes System erfüllt werden kann. Unser Streben ist immer, so gut wie möglich GitOps zu machen, um alle Vorteile daraus zu gewinnen. Das automatische Beziehen ist aber oft eine Herausforderung, die anders viel einfacher bei kaum mehr Nachteilen gelöst werden muss.
Einfaches Beispiel sind voll verwaltete Dienste in Public-Clouds, wie beispielsweise Serverless-Functions. Dort kann man oftmals keine Software-Agenten innerhalb des Systems platzieren, da das System geschlossen ist. Eine Serverless-Function deklarativ zu beschreiben und diese Beschreibung einzuchecken ist jedoch meist recht einfach möglich. Das „automatisch“ lässt sich auch einfach machen, etwa über einen Commit-Webhook. Doch durch das System selbst beziehen zu lassen funktioniert oft nicht. Das heißt, wir platzieren den Software-Agenten zwischen Git und dem System, er checkt aus der Versionsverwaltung aus und konfiguriert den Serverless-Function-Service.
Nicht ganz prinzipientreu, aber dennoch eine gute Lösung.
→ 4. Prinzip: Kontinuierlich angeglichen
Software-Agenten beobachten den tatsächlichen Systemzustand und versuchen kontinuierlich, ihn dem Soll-Zustand anzugleichen.
Und zu guter Letzt die eigentlich härteste Aufgabe. Der tatsächliche Systemzustand soll vom Agenten überwacht und bei Bedarf angepasst werden. Das wortgetreu umzusetzen, dürfte ziemlich schwierig werden; die meisten Implementierungen machen es sich einfacher und prüfen in festen Zeitintervallen, ob es einen Unterschied zwischen dem Soll-Zustand und dem Ist-Zustand gibt – falls dem so ist, wird versucht, den Soll-Zustand herzustellen. Dies ist kein echtes Beobachten, aber der Effekt ist der Gleiche. Die wirklich harte Nummer ist allerdings, aus einer deklarativen Beschreibung eine Aktivität abzuleiten, die ausgeführt werden kann, um zum gewünschten Zustand zu kommen.
Infrastructure-as-Code-Systeme können das, mal besser, mal schlechter, und im Falle von Kubernetes ist es zum Glück aus Anwender-Sicht sehr einfach, weshalb GitOps hier auch so gut und harmonisch funktioniert. In Umgebungen, die üblicherweise nicht deklarativ beschrieben werden, gestaltet es sich schwieriger. Die bereits erwähnte Serverless-Function-Lösung etwa kann neben der Oberfläche auch mittels Command-Line-Client konfiguriert werden. Oberflächen sind natürlich generell keine nachhaltige Option, CLIs pauschal immer toll. Doch nichts davon ist deklarativ. Was wir hingegen wollen, ist etwas wie „Es möge die folgende Konfiguration in Kraft sein.“
Auch wenn hier kein prinzipientreues GitOps möglich ist, versuchen wir doch so viel wie möglich davon zu erfüllen. Eine Lösung kann also eine deklarative Beschreibung des Ziel-Zustandes als Konfigurations-Datei sein. Diese kann eingecheckt werden. Des Weiteren schreiben wir uns noch einen Software-Agenten, der diese Deklaration in CLI-Befehle übersetzt und die regelmäßig, mindestens aber bei Änderung des Soll-Zustandes, ausführt. Das klingt etwas wie eine klassische Continuous-Deployment-Lösung, mit dem Unterschied, dass wir explizit zwischen dem Bau der Fach-Anwendung, der Beschreibung und Konfiguration der Anwendung in der Laufzeitumgebung und dem Herbeiführen dieses Zustandes trennen. Ungewöhnlich ist ebenfalls, dass der Soll-Zustand den „Umweg“ über Git nimmt; dank der Git-Historie können wir so genau sagen, wie das System zu jedem Zeitpunkt konfiguriert war. Lückenlose Nachvollziehbarkeit, Git hält die einzige Wahrheit über unser System.
--
⇒ In der nächsten Folge von "GitOps: wieso, weshalb, warum - Teil 2 von 3" am 9. September 2022 geht es um GitOps und Kubernetes.
--
Bild: Gerd Altmann auf Pixabay


