Configuration as Code (CasC) ist weit verbreitet. Dabei wird CasC auf verschiedenen Ebenen eingesetzt.

Beispiele sind Puppet und Ansible auf Softwareebene, aber auch Infrastructure as Code (IaC) ist inzwischen bereits State-of-the-Art oder zumindest auf dem Weg dorthin.

Bei CasC wird der Soll-Zustand eines Software-Systems in Code beschrieben und in einem Repository abgelegt. Das jeweilige Tool gleicht dann den Ist-Zustand und den Soll-Zusstand ab und versucht, den Sollzustand herzustellen.

GitOps setzt dieses Prinzip auf Clusterebene um. Das Deployment und die Konfiguration eines Containers werden in Code beschrieben. Der beschriebene Soll-Zustand wird von einem Tool hergestellt.

Das Tool meiner Wahl ist an dieser Stelle Flux CD Version 2. Die Konfiguration in Form von yaml (Kubernetes CustomResourceDefinitions) wird in einem Git-Repository verwaltet. Im Kubernetes-Cluster wird das sogenannte GitOps Toolkit (GOTK) installiert. Es besteht aus mehreren Komponenten/Controllern, die Soll- und Ist-Zustand abgleichen und den Soll-Zustands herstellen.

Ich beschränke mich auf die Verwendung von Flux v2 in Verbindung mit Helm. Wenn ich im folgenden Text Flux schreibe ist damit immer Flux v2 gemeint.


Flux CD

Flux CD von WeaveWorks ist inzwischen ein Projekt der Cloud Native Computing Foundation. Weitere Details und Informationen zu Flux CD und GitOps gibt es unter https://fluxcd.io/


Um Flux zu verstehen, ist es wichtig, die verschiedenen Flux Kubernetes Resources und deren Aufgaben zu kennen.

Sources / Quellen

Flux unterstützt verschiedene Quellen. Relevant sind für uns GitRepositories und HelmRepositories.

Die Konfiguration (im Sinne von Configuration as Code) unseres Kubernetes-Clusters wird in GitRepositories verwaltet. Die wichtigste Aufgabe von GitRepository Resources ist es, das Git-Repository im Kubernetes-Cluster abzubilden. GitRepositories enthalten die Informationen, die notwendig sind, um vom Kubernetes-Cluster aus auf das Git-Repository und damit auf die Konfiguration zuzugreifen.

Neben GitRepositories verwende ich im folgenden auch HelmRepositories. HelmRepository-Ressourcen repräsentieren analog zu GitRepositories Helm-Repositories. Auf diese Weise ist es möglich, Helm-Repositories zu definieren, auf die vom Kubernetes-Cluster aus zugegriffen wird, um Helm-Charts im Cluster zu installieren.

HelmReleases

HelmReleases definieren Helm-Charts, die im Kubernetes-Cluster installiert werden sollen. Sie referenzieren ein Helm-Chart in einem HelmRepository. Es ist außerdem möglich, Werte des Helm-Charts zu überschreiben.

Kustomizations

Kustomizations werden im Wesentlichen genutzt, um die Konfiguration im Git-Repository zu strukturieren.


Ingress-Controller Kubernetes Ingresses sind eine flexible Möglichkeit, um den Datenverkehr von außerhalb eines Kubernetes-Clusters an interne Kubernetes Services weiterzuleiten. Weitere allgemeine Informationen sind unter folgender URL zu finden: https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/

Auf Details zum Setup des Ingress-Controllers werde ich im folgenden Blogpost eingehen.

Es ist möglich, Flux zu installieren und die Konfiguration ausschließlich per Git und kubectl zu verwalten. Die Flux CLI erleichtert den Umgang aber erheblich und automatisiert die Erstellung von Kubernetes-Manifesten. Kustomize automatisiert zudem die Erstellung von Kustomization Resources.

Um die Flux CLI zu installieren, nutzen wir folgenden Aufruf.

# Install Flux v2 cli to /usr/local/bin
curl -s https://toolkit.fluxcd.io/install.sh | sudo bash

Im Folgenden beschreibe ich exemplarisch am Beispiel eines Ingress-Controllers, wie die Konfiguration eines Deployments aussieht.

Ich beschränke mich in meiner Beschreibung auf den Ansatz mit einem generischen Git-Repository. Für Gitlab und Github stellt die Flux CLI zusätzlich das Kommando bootstrap zur Verfügung. Wir führen die Schritte stattdessen manuell aus.

Im ersten Schritt erstellen wir ein GitOps Git-Repository für die Konfiguration, klonen das Repository und wechseln in das Repository-Verzeichnis.

# Clone gitops-repository 
git clone <gitops-repository>
# Change directory 
cd <gitops-repository>

Nun installieren wir die Flux Komponenten im Kubernetes-Cluster.

# Create directories
mkdir -p clusters/<clustername>/flux-system
# Create flux-components manifest
flux install --version=latest --arch=amd64 --export > ./clusters/<clustername>/flux-system/flux-components.yaml
# Add to repository, commit and push
git add . && git commit -m "Add flux-components manifests" && git push
# Apply manifest
kubectl apply -f ./clusters/<clustername>/flux-system/flux-components.yaml 
# Check if controller have started
flux check

Wir konfigurieren nun die Verbindung zwischen unserem Git-Repository und dem Kubernetes-Cluster. Dazu erzeugen wir eine GitRepository-Ressource und eine Kustomization-Ressource.

# Create GitRepository resource
flux create source git flux-system \
  --url=<gitops-repository> \
  --username=<username> \
  --password=<password> \
  --branch=main \
  --interval=1m
# Create Kustomization resource
flux create kustomization flux-system \
  --source=flux-system \
  --path="./clusters/<clustername>/" \
  --prune=true \
  --interval=10m

Nun exportieren wir die GitRepository- und Kustomization-Ressource und fügen sie dem Git-Repository hinzu.

# Export GitRepository resource
flux export source git flux-system > clusters/<clustername>/flux-system/flux-sync.yaml

# Export Kustomization resource
flux export kustomization flux-system >> clusters/<clustername>/flux-system/flux-sync.yaml

# Change to directory and create Kustomization for flux-system
cd ./clusters/<clustername>/flux-system && kustomize create --autodetect

# Add to repository, commit and push
git add -A && git commit -m "Add sync manifests" && git push

Damit ist die Installation von Flux abgeschlossen. Als nächstes wird die Verzeichnisstruktur erzeugt, um Services des Infrastructure-Layers (siehe Blogpost Überblick) zu konfigurieren und in unser Kubernetes-Cluster zu deployen. In diesem Blogpost deployen wir auf diesem Weg den Ingress-Controller ingress-nginx

Als erstes erzeugen wir einige Verzeichnisse.

# Create infrastructure directory/{sources, ingress-nginx}
mkdir -p infrastructure/sources
mkdir infrastructure/ingress-nginx

Danach erzeugen wir im Verzeichnis infrastructure das Kustomization-Kubernetes-Manifest mit folgendem Inhalt.

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ./sources
  - ./ingress-nginx

Im sources-Verzeichnis legen wir die Datei ingress-nginx.yaml mit dem HelmRepository-Kubernetes-Manifest an. Die Datei sollte folgenden Inhalt enthalten.

apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: HelmRepository
metadata:
  name: ingress-nginx
spec:
  interval: 30m
  url: https://kubernetes.github.io/ingress-nginx

Im sources-Verzeichnis erzeugen wir das Kustomizations-Manifest mit Hilfe von Kustomize.

# Change to sources directory
cd sources
# Create Kustomization
kustomize create --autodetect

Nun wechseln wir ins Verzeichnis infrastructure/ingress-nginx und erzeugen dort das eigentliche HelmRelease. Die Datei release.yaml sollte folgenden Inhalt enthalten.

apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: ingress-nginx
spec:
  releaseName: ingress-nginx
  chart:
    spec:
      chart: ingress-nginx
      sourceRef:
        kind: HelmRepository
        name: ingress-nginx
        namespace: flux-system
      version: 3.15.2 
  interval: 1h0m0s
  install:
    remediation:
      retries: 3
  # Default values
  # https://github.com/bitnami/charts/blob/master/bitnami/nginx-ingress-controller/values.yaml
  values:
    controller:
      service:
        type: NodePort
        nodePorts:
          http: 31080
          https: 31443
      config:
        use-proxy-protocol: "true"
    defaultBackend:
      enabled: true

Damit Flux den Namespace erzeugt, benötigen wir im Verzeichnis infrastructure/ingress-nginx außerdem die Datei namespace.yaml mit dem Inhalt:

apiVersion: v1
kind: Namespace
metadata:
  name: ingress-nginx

Nun benötigen wir auch im Verzeichnis infrastructure/ingress-nginx eine entsprechende Kustomization-Datei.

# Create Kustomization file using kustomize
kustomize create --autodetect

Schließlich fügen wir alle Änderungen dem Repository hinzu und führen commit und push aus.

git add .
git commit -m "Add ingress-nginx configuration."
git push

Mit Hilfe der Flux CLI prüfen wir, ob das Deployment des HelmReleases korrekt ist.

flux get helmreleases --all-namespaces

Wir erwarten folgende Ausgabe:

NAMESPACE     NAME          READY MESSAGE                           REVISION SUSPENDED 
ingress-nginx ingress-nginx True  Release reconciliation succeeded  3.15.2   False    

Mit Abschluss dieses Blogposts sind wir nun in der Lage, auf Basis des GitOps-Ansatzes Helm-Charts in unser Kubernetes-Cluster zu deployen. Und mit dem Deployment des Ingress-Controllers haben wir nun einen Zugang zu unserem Cluster geschaffen. Der nächste Blogpost wird sich dem Thema Ingress noch einmal ausführlicher widmen.