GitOps steht sinnbildlich für einen hohen Automatisierungsgrad, wie er im Software-as-a-Service-, kurz SaaS-Kontext angestrebt wird. Dieser Beitrag zeigt, wie Flux dabei hilft, einen GitOps-Prozess für Änderungen im laufenden Betrieb aufzusetzen.
GitOps soll in diesem Beispiel einen verbesserten Team-Workflow bei der Bereitstellung einer SaaS-App gewährleisten.
(Bild: AWS Deutschland)
Dies ist der dritte Teil einer Blog-Post-Reihe zum Thema Software-as-a-Service (SaaS) in Amazon Elastic Kubernetes Service (EKS). In den vorigen Teilen haben wir …
das Kubernetes Cluster provisioniert,
ein erstes Container Repository in AWS Elastic Container Registry (ECR) angelegt,
die SaaS Identität eingeführt, und
mit Amazon Cognito einen Identity Provider mit OIDC und JWT Unterstützung eingerichtet.
In der Einführung dieser Reihe haben wir festgestellt, dass ein hoher Automatisierungsgrad und eine Standardisierung der Anwendung über alle Mandanten hinweg für einen effizienten SaaS-Betrieb notwendig ist. GitOps ist in den letzten Jahren im Umfeld von Cloud Native zu einem beliebten Prozess zur Automatisierung von Deployments in Container-Plattformen geworden.
Der Vorteil von GitOps ist dabei, die Stabilität und Verlässlichkeit im Betrieb zu maximieren. Änderungen werden ausschließlich über die Versionskontrolle durchgeführt. Dadurch erhalten wir nicht nur einen Audit-Trail aller Änderungen, wir erhalten auch eine sehr einfache Roll-Back-Funktionalität zur Minimierung der Mean-Time-to-Recovery (MTTR). Wir werden in diesem Beispiel GitOps nur für Änderungen der Anwendung innerhalb eines produktiven Kubernetes-Clusters verwenden.
Weaveworks hat den Begriff GitOps 2017 geprägt. Dabei hat Weaveworks auf der Art aufgebaut, wie Änderungen in Kubernetes durchgeführt werden. Der Zustand des Clusters wird durch die Control-Plane verwaltet. Gewünschte Zustandsänderungen (entweder von außerhalb des Clusters durch Anweisungen über die Kubernetes API oder von innerhalb wie zum Beispiel bei Skalierungs-Events) werden im Key-Value Store etcd persistiert; Controller im Cluster führen kontinuierlich eine Reconciliation durch.
GitOps für Kubernetes führt eine zusätzliche Reconciliation-Schleife ein.
(Bild: AWS Deutschland / GitHub)
Reconcilation bedeutet, dass der Controller den aktuellen Zustand der von ihm überwachten Objekte mit dem gewünschten Zustand in etcd vergleicht und die nötigen Schritte einleitet, um diesen zu erreichen. Dabei ist die Zustandsbeschreibung deklarativ. Im Gegensatz zu einer imperativen Beschreibung wird nicht beschrieben, wie dieser Zustand zu erreichen ist. GitOps für Kubernetes führt eine weitere Reconciliation-Schleife ein: zwischen der Versionskontrolle (in der Regel Git) und dem Key-Value-Store etcd.
Flux v2
Um diese Reconciliation-Schleife zu implementieren, werden wir im Folgenden Flux v2 von Weaveworks verwenden. Flux ist hierbei kein alleinstehendes Werkzeug, das von außen mit der Kubernetes API kommuniziert. Es nutzt viel mehr das Kubernetes-Konzept von Custom Resource Definitions (CRDs).
Eine CRD erweitert die Kubernetes API und ermöglicht es so, den Cluster um Funktionalitäten zu erweitern. Typische Beispiele hierfür sind beispielweise die AWS Controllers for Kubernetes, die AWS Services wie Datenbanken oder Message Broker als native Objekte in den Kubernetes Templates verwalten können. Flux definiert vier Arten von Controllern als CRD:
Source Controller überwachen externe Quellen (beispielweise Git oder Helm-Repositories) und erzeugen von anderen Controllern konsumierbare Artefakte im Kubernetes Cluster.
Reconciler prüfen die von Source Controllern erzeugten Artefakte und erzeugen bei Bedarf Änderungen im Desired State in etcd, auf den dann wiederum die Runtime Controller von Kubernetes selbst reagieren und den Zustand der Anwendung entsprechend anpassen.
Image Automation Controller überwachen den Zustand von Image Repositories, schreiben bei Bedarf neue Image Versionen in die Kubernetes Templates, committen diese an Git Repositories und lösen somit wieder Reconciliation-Schleifen über Source Controllers und Reconcilers aus.
Notification Controller interagieren mit externen Systemen via Events und sind beispielweise das Bindeglied zu Slack und somit ChatOps.
Neben den Controllern in Kubernetes benötigen wir noch eine Möglichkeit, den Cluster-Zustand unter Versionskontrolle zu halten. Wie bereits erwähnt, verwenden wir Git – und Flux gibt hier ein paar Konventionen vor. Diese Konventionen sind jedoch nicht starr und erlauben es die Struktur der Repositories auf den jeweiligen Workflow zwischen Development und Betriebsteam anzupassen. Eine Übersicht findet sich in der offiziellen Doku.
Der zu realisierende Team-Workflow.
(Bild: AWS Deutschland / GitHub)
Ein häufiges Team-Setup von SaaS Anbietern ist, eine Plattform-Mannschaft für die Entwicklung und Verwaltung des Clusters zu haben, welches das Produktteams dazu befähigt, ihre Anwendung in dieser Plattform zu betreiben. Um die Trennung der Verantwortlichkeiten auch technisch zu gewährleisten, besitzen beide Teams ihre eigenen Git-Repositories. Wir wollen daran angelehnt in diesem Beispiel den vorne zu sehenden Workflow abbilden.
Das Plattformteam verwaltet einen oder mehrere Cluster in seinem Infrastruktur-Repository. Es führt die notwendigen Konfigurationen durch, wie beispielweise die Installation von Flux. Dieser Schritt wird Bootstrapping genannt. Anschließend ist das Team für das Onboarding neuer Mandanten zuständig. Hierzu werden die notwendigen Kubernetes-Templates im Infrastruktur-Repository angelegt.
Im Onboarding-Prozess werden ebenfalls Kubernetes Namespaces und Service Accounts zur Isolierung der Mandanten erzeugt. Außerdem bestimmt der Onboarding-Prozess, welche SaaS-Anwendungen für einen Mandanten provisioniert werden sollen.
Stand: 08.12.2025
Es ist für uns eine Selbstverständlichkeit, dass wir verantwortungsvoll mit Ihren personenbezogenen Daten umgehen. Sofern wir personenbezogene Daten von Ihnen erheben, verarbeiten wir diese unter Beachtung der geltenden Datenschutzvorschriften. Detaillierte Informationen finden Sie in unserer Datenschutzerklärung.
Einwilligung in die Verwendung von Daten zu Werbezwecken
Ich bin damit einverstanden, dass die Vogel IT-Medien GmbH, Max-Josef-Metzger-Straße 21, 86157 Augsburg, einschließlich aller mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen (im weiteren: Vogel Communications Group) meine E-Mail-Adresse für die Zusendung von Newslettern und Werbung nutzt. Auflistungen der jeweils zugehörigen Unternehmen können hier abgerufen werden.
Der Newsletterinhalt erstreckt sich dabei auf Produkte und Dienstleistungen aller zuvor genannten Unternehmen, darunter beispielsweise Fachzeitschriften und Fachbücher, Veranstaltungen und Messen sowie veranstaltungsbezogene Produkte und Dienstleistungen, Print- und Digital-Mediaangebote und Services wie weitere (redaktionelle) Newsletter, Gewinnspiele, Lead-Kampagnen, Marktforschung im Online- und Offline-Bereich, fachspezifische Webportale und E-Learning-Angebote. Wenn auch meine persönliche Telefonnummer erhoben wurde, darf diese für die Unterbreitung von Angeboten der vorgenannten Produkte und Dienstleistungen der vorgenannten Unternehmen und Marktforschung genutzt werden.
Meine Einwilligung umfasst zudem die Verarbeitung meiner E-Mail-Adresse und Telefonnummer für den Datenabgleich zu Marketingzwecken mit ausgewählten Werbepartnern wie z.B. LinkedIN, Google und Meta. Hierfür darf die Vogel Communications Group die genannten Daten gehasht an Werbepartner übermitteln, die diese Daten dann nutzen, um feststellen zu können, ob ich ebenfalls Mitglied auf den besagten Werbepartnerportalen bin. Die Vogel Communications Group nutzt diese Funktion zu Zwecken des Retargeting (Upselling, Crossselling und Kundenbindung), der Generierung von sog. Lookalike Audiences zur Neukundengewinnung und als Ausschlussgrundlage für laufende Werbekampagnen. Weitere Informationen kann ich dem Abschnitt „Datenabgleich zu Marketingzwecken“ in der Datenschutzerklärung entnehmen.
Falls ich im Internet auf Portalen der Vogel Communications Group einschließlich deren mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen geschützte Inhalte abrufe, muss ich mich mit weiteren Daten für den Zugang zu diesen Inhalten registrieren. Im Gegenzug für diesen gebührenlosen Zugang zu redaktionellen Inhalten dürfen meine Daten im Sinne dieser Einwilligung für die hier genannten Zwecke verwendet werden. Dies gilt nicht für den Datenabgleich zu Marketingzwecken.
Recht auf Widerruf
Mir ist bewusst, dass ich diese Einwilligung jederzeit für die Zukunft widerrufen kann. Durch meinen Widerruf wird die Rechtmäßigkeit der aufgrund meiner Einwilligung bis zum Widerruf erfolgten Verarbeitung nicht berührt. Um meinen Widerruf zu erklären, kann ich als eine Möglichkeit das unter https://contact.vogel.de abrufbare Kontaktformular nutzen. Sofern ich einzelne von mir abonnierte Newsletter nicht mehr erhalten möchte, kann ich darüber hinaus auch den am Ende eines Newsletters eingebundenen Abmeldelink anklicken. Weitere Informationen zu meinem Widerrufsrecht und dessen Ausübung sowie zu den Folgen meines Widerrufs finde ich in der Datenschutzerklärung.
Die eigentliche Provisionierung erfolgt dann aber durch Links in die Produkt-Repositories der Produktteams. Hier liegen die Deployment-Manifeste, wie beispielweise Kubernetes Templates oder Helm-Charts. Die Mandanten- oder umgebungsspezifische Konfiguration liegt in unserem Beispiel ebenfalls im Produkt-Repository.
Die Repository Struktur
Für diese Artikelreihe werden wir mit zwei Repositories arbeiten. Das Plattform-Team verwaltet das Infrastruktur-Repository, das den Infrastrukturcode und die Konfiguration des Clusters enthält. Es beinhaltet darüber hinaus noch Konfiguration für die Tenant-Namespaces, in welche die SaaS-Mandanten deployed werden. Hier findet sich der Link zum Produkt Repository des Produkt-Teams. Es enthält das Deployment-Manifest und die App-Konfiguration, auf die im Infrastruktur-Repository verwiesen wird.
Die für diesen Blog Post relevanten Komponenten im Infrastruktur Repository sind:
└─ clusters └─ prod-eu ├─ flux-system │ ├─ # YAML-Dateien für die Flux-Komponenten ├─ tenant-001 │ ├─ # 1 Ordner pro Mandant mit YAML-Dateien └─ tenant-002 └─ # für die SaaS im Mandanten-Namespace
Das Produkt-Repository hingegen enthält das Deployment Manifest, das im Infrastruktur Repository innerhalb des Tenant Unterordners verlinkt ist:
└─ kustomize └─ # YAML-Files mit dem desired state der SaaS
Scope-Auswahl für das GitHub Token.
(Bild: AWS Deutschland / GitHub)
Wir verwalten beide Repositories in GitHub und erzeugen uns ein GitHub Token für den Account. Das Token ist eine Alternative zur Passwort-Authentifizierung gegenüber der GitHub API. Wir werden das Token verwenden, um Flux Zugriff auf die Repositories zu geben. Eine detaillierte Anleitung beinhaltet die GitHub-Dokumentation. Das Token muss – wie in der Abbildung zu sehen – alle Berechtigungen des Scope repo enthalten.
Eine Demo App
Unser Beispiel für ein Produkt Repository ist das in der offiziellen Flux Dokumentation verwendete podinfo-Repository von Stefan Prodan von Weaveworks. Im kustomize-Ordner des Repositories ist ein Manifest bestehend aus einem Deployment, einem Service und Konfigurationen zum Skalieren der Pods enthalten:
Wir benötigen zwei Kommandozeilen-Tools in unserer lokalen Entwicklungsumgebung, um den oben beschriebenen Workflow mit Flux aufzusetzen. Zunächst muss Flux in Version 2 selbst installiert werden. Das zweite Tool werden wir für das Konfigurationsmanagement einsetzen. Es erzeugt Kubernetes Templates und heißt Kustomize, eine Installationsanleitung findet sich hier.
Nach der Installation exportieren wir unsere GitHub Zugangsdaten in der Kommandozeilen-Session, die wir für das Bootstrapping mit Flux verwenden wollen:
Flux verwendet die kubeconfig zur Authentifizierung am Kubernetes Cluster. Damit dort aktuelle Zugangsinformationen für den EKS Cluster gefunden werden, müssen wir die kubeconfig mit der AWS CLI aktualisieren:
$ aws eks update-kubeconfig --name prod-euAdded new context arn:aws:eks:eu-west-1:<account-id>:cluster/prod-eu to /Users/mkokott/.kube/config
Wir können nun prüfen, ob alle Voraussetzungen erfüllt sind, den Boostrap-Prozess für das Cluster zu starten:
Der Bootstrap-Prozess führt eine Reihe von Schritten im Kubernetes-Cluster und im Infrastruktur-Repository durch:
Das Repository wird in GitHub angelegt, falls es nicht bereits existiert.
Ein Manifest für die Komponenten von Flux wird im Infrastruktur Repository angelegt.
Das Manifest wird verwendet, um die Flux-Komponenten in den Kubernetes Cluster zu deployen.
Flux im Kubernetes Cluster wird so konfiguriert, dass der Cluster-Ordner im Infrastruktur Repository auf Änderungen überwacht wird.
Wir starten den Bootstrap-Prozess über die Kommandozeile, in der wir die GitHub Zugangsdaten exportiert haben:
flux bootstrap github \ --owner=$GITHUB_USER \ --repository=saas-in-eks-infra \ --branch=main \ --path=./clusters/prod-eu \ --personal► connecting to github.com ► cloning branch "main" from Git repository "https://github.com/mkokott-aws/saas-in-eks-infra.git" ✔ cloned repository ► generating component manifests ✔ generated component manifests ✔ component manifests are up to date ► installing toolkit.fluxcd.io CRDs ◎ waiting for CRDs to be reconciled ✔ CRDs reconciled successfully ► installing components in "flux-system" namespace ✔ installed components ✔ reconciled components ► determining if source secret "flux-system/flux-system" exists ► generating source secret ✔ configured deploy key "flux-system-main-flux-system-./clusters/prod-eu" for "https://github.com/mkokott-aws/saas-in-eks-infra" ► applying source secret "flux-system/flux-system" ✔ reconciled source secret ► generating sync manifests ✔ generated sync manifests ✔ sync manifests are up to date► applying sync manifests ✔ reconciled sync configuration ◎ waiting for Kustomization "flux-system/flux-system" to be reconciled ✔ Kustomization reconciled successfully ► confirming components are healthy ✔ source-controller: deployment ready ✔ kustomize-controller: deployment ready ✔ helm-controller: deployment ready ✔ notification-controller: deployment ready ✔ all components are healthy
Im Kubernetes-Cluster existiert nun ein neuer Namespace flux-system, in dem die Controller laufen. Die erste Reconciliation-Schleife wurde ebenfalls eingerichtet. Im neuen Unterordner clusters/prod-eu/flux-system ist in der gotk-sync.yaml-Datei der Cluster-Unterordner im Infrastruktur-Repository registriert:
Diese erste Reconciliation-Schleife ermöglicht es uns nun, über einen Git-Commit neue Mandanten im Cluster zu erzeugen. Dazu arbeiten wir im lokal ausgecheckten Infrastruktur-Repository:
# Erzeugen eines Unterordners für den neuen Tenant cd clusters/prod-eu/ mkdir my-first-tenant# jeder Tenant wird in einem eigenen # Namespace angelegt, für den ein # eigener Service Account für den # Reconciliation-Prozess erzeugt wird flux create tenant my-first-tenant \ --with-namespace=my-first-tenant \ --export > rbac.yaml# eine Reconciliation-Schleife für das # Produkt Repository wird angelegt flux create source git my-first-tenant \ --namespace=my-first-tenant \ --url="https://github.com/stefanprodan/podinfo.git" \ --branch=master --export > sync.yaml# eine Kustomization als Referenz zum # Deployment-Manifest und die Konfiguration # der SaaS-App werden hinzugefügt flux create kustomization my-first-tenant \ --namespace=my-first-tenant \ --service-account=my-first-tenant \ --target-namespace=my-first-tenant \ --source=GitRepository/my-first-tenant \ --path=kustomize \ --export >> sync.yaml# alle erzeugten YAML-Dateien werden # in einem Manifest als Kubernetes # Kustomization Objekt definiert kustomize create --autodetect# alle Dateien für den neuen Tenant # werden ins Infrastruktur-Repository gepusht git add -A git commit -m "provisioning my first tenant" git push
Damit weisen wir Flux an, eine Reconciliation-Schleife für das Produkt-Repository im Namespace des neuen Mandanten anzulegen und das Deployment-Manifest aus dem Unterordner kustomize anzuwenden:
Es werden keine Änderungen im Produkt Repository durchgeführt. Der Reconciler benötigt hier lediglich lesenden Zugriff, um die referenzierten Manifestdateien in den Kubernetes Cluster zu übertragen.
Die Reconciliation-Schleife für das Infrastruktur Repository zeigt nach kurzer Zeit die neue Revision. Die mit Flux und Kustomize angelegten Manifeste enthalten die Anweisungen, die App für den neuen Mandanten in einem eigenen Namespace anzulegen:
$ kubectl get namespacesNAME STATUS AGE default Active 5h2m flux-system Active 3h27m kube-node-lease Active 5h2m kube-public Active 5h2m kube-system Active 5h2m my-first-tenant Active 2m35s$ kubectl get pods -n my-first-tenantNAME READY STATUS RESTARTS AGE podinfo-96c5c65f6-hg8lc 1/1 Running 0 25s podinfo-96c5c65f6-nckjk 1/1 Running 0 40s
In diesem Namespace wurde ein zusätzlicher Service Account angelegt, der ein cluster-admin RoleBinding nur in diesem Namespace hat:
$ kubectl get serviceaccounts -n my-first-tenantNAME SECRETS AGE default 1 2m55s my-first-tenant 1 2m55s
Der Reconciler verwendet diesen Service-Account, um Änderungen im Deployment-Manifest ausrollen und sämtliche Kubernetes-Objekte in diesem Namespace manipulieren zu können. Außerhalb des eigenen Namensbereichs hat der Reconciler keine Zugriffsrechte. Service-Accounts für Pods zur Laufzeit sind davon nicht betroffen. Sie können weiterhin beliebige Service-Accounts wie zum Beispiel default verwenden.
In diesem Teil der Serie haben wir gesehen, wie ein GitOps-Prozess mit Flux aufgesetzt werden kann. SaaS-Apps für Mandanten werden in dedizierte Namespaces deployed. Die Definition der Deployment-Manifeste liegt in der Hand der Produktteams, das autark vom Plattformteam eine Wartung über Git-Commits betreiben kann.
Das Plattform-Team ist weiterhin für die Kubernetes-Infrastruktur zuständig und hat alle Möglichkeiten zur Isolation der Mandanten. So können zum Beispiel Network Policies verwendet werden, um den Datentransfer zu kontrollieren. Durch die Integration von AWS Identity and Access Management (IAM) in AWS und die Role Based Access Control (RBAC) in Kubernetes kann das Plattform Team auch zentral Zugriffsrechte auf Ressourcen wie zum Beispiel Datenbanken außerhalb des Clusters verwalten.
Markus Kokott
(Bild: AWS Deutschland)
Im nächsten Teil der Reihe zeigen wir, wie sich das Onboarding und damit die Provisionierung neuer Mandanten automatisieren lässt.
* Markus Kokott arbeitet als Solutions Architect bei Amazon Web Services. Er hilft Softwareherstellern dabei ihre Prozesse und Produkte zu modernisieren und damit fit für die Cloud zu machen. Technologisch interessiert sich Markus insbesondere für die Bereiche DevOps und Container.