| FOTO | AUTO | EDV | AUDIO |

11 — GitOps

Teil D — Wie man betreibt. Zurück zur Übersicht.

Lernziel

Nach diesem Kapitel kennst du die vier GitOps-Prinzipien, kannst Push- von Pull-Verfahren unterscheiden und weißt, wie Geheimnisse in einem öffentlich lesbaren Repository sicher abgelegt werden. Du hast einen einfachen Pull-Abgleich für den KFZ-Rechner gebaut.

11.1 Die Idee

GitOps ist die konsequente Fortsetzung von Infrastructure as Code: Git ist nicht die Dokumentation des Systems, Git ist das System.

Vier Prinzipien:

  1. Deklarativ. Der Sollzustand ist vollständig beschrieben, nicht als Abfolge von Schritten.
  2. Versioniert und unveränderlich. Git ist die einzige Quelle der Wahrheit, jede Änderung hat Autor, Zeitpunkt und Begründung.
  3. Automatisch gezogen. Ein Automatismus holt sich den Sollzustand — statt dass eine Pipeline ihn hineinschiebt.
  4. Fortlaufend abgeglichen. Weicht der Ist-Zustand ab, wird korrigiert. Dauerhaft, nicht nur beim Ausrollen.

Der vierte Punkt ist der eigentliche Unterschied zu allem Vorherigen. Eine Pipeline stellt einen Zustand einmal her. Ein GitOps-Automatismus hält ihn aufrecht.

11.2 Push oder Pull

  PUSH (klassische Pipeline)

  Git ──► Pipeline ──► Zugangsdaten ──► Zielsystem
                       ▲
             Die Pipeline braucht Schreibrechte
             auf der Produktion.


  PULL (GitOps)

  Git ◄────── Agent im Zielsystem ──► wendet an
              zieht regelmäßig
              ▲
     Keine Zugangsdaten außerhalb des Zielsystems.
     Die Pipeline endet an der Registry.
Push Pull
Wer verbindet sich zu wem Pipeline zum Ziel Ziel zum Repository
Zugangsdaten für Produktion in der Pipeline im Zielsystem
Firewall eingehend zum Ziel nur ausgehend
Drift-Korrektur nein ja, laufend
Einrichtung einfach Agent nötig

Der sicherheitstechnische Vorteil des Pull-Verfahrens ist erheblich: Die Pipeline braucht keine Produktionszugangsdaten mehr. Sie baut, prüft und legt in der Registry ab — mehr nicht. Wer die Pipeline übernimmt, hat damit noch keinen Zugriff auf die Produktion. Angesichts der Angriffsfläche aus Kapitel 7 ist das ein starkes Argument.

11.3 Zwei Repositories

Bewährt hat sich die Trennung:

  kfz-rechner/                  (Anwendungsrepository)
  ├── src/
  ├── tests/
  ├── Containerfile
  └── .gitlab-ci.yml            baut das Image, legt es in der Registry ab

  kfz-betrieb/                  (Konfigurationsrepository)
  ├── test/
  │   └── kfz-rechner.yaml      Image-Tag: 2.5.1
  ├── abnahme/
  │   └── kfz-rechner.yaml      Image-Tag: 2.5.0
  └── produktion/
      └── kfz-rechner.yaml      Image-Tag: 2.4.8

Warum getrennt? Der Ist-Zustand jeder Umgebung ist damit eine Zeile in einer Datei, und ein Deployment ist ein Commit. Der Git-Verlauf des Konfigurationsrepositories beantwortet lückenlos, welche Version wann in welcher Umgebung lief und wer sie freigegeben hat. Ein Rollback ist ein git revert.

Für regulierte Umgebungen ist das der entscheidende Punkt. Die Freigabe einer Produktivänderung wird zu einem Pull Request im Konfigurationsrepository — mit Prüfer, Zeitstempel und Begründung. Das Vier-Augen-Prinzip ist damit technisch erzwungen statt organisatorisch vereinbart, und der Nachweis fällt als Nebenprodukt an. Mehr dazu in Kapitel 17.

11.4 Werkzeuge

Werkzeug Umfeld Anmerkung
Argo CD Kubernetes, OpenShift Weboberfläche, sehr verbreitet
Flux Kubernetes schlanker, stärker CLI-orientiert
OpenShift GitOps OpenShift Argo CD als Operator, integriert
systemd-Timer plus Skript einzelner Server reicht überraschend weit

Die letzte Zeile ist ernst gemeint. GitOps ist ein Prinzip, kein Produkt — und auf einem einzelnen Server lässt es sich mit einem Zeitgeber und dreißig Zeilen Shell umsetzen.

11.5 Geheimnisse in einem Git-Repository

Das offensichtliche Problem: Wenn alles in Git steht, stehen dort auch die Passwörter. Das darf nicht sein.

Verfahren Funktionsweise Eignung
SOPS Werte einzeln verschlüsselt, Struktur bleibt lesbar gut, auch außerhalb von Kubernetes
Sealed Secrets verschlüsselt für genau einen Cluster Kubernetes
External Secrets Operator verweist auf einen externen Tresor Unternehmensumgebungen
Podman Secrets Geheimnis lokal, Unit verweist darauf einzelner Server
# SOPS: Struktur lesbar, Werte verschlüsselt - gute Diffs im Git
db:
    host: db-prod.intern
    benutzer: kfz_app
    passwort: ENC[AES256_GCM,data:8Kj2mQ==,iv:...,tag:...,type:str]

Ein häufiger Denkfehler: „Das Repository ist ja privat, da kann das Passwort ruhig im Klartext stehen.„

Ein privates Repository wird geklont — auf Notebooks, in CI-Runner, in Sicherungen. Es hat Leseberechtigte, die man nicht mehr überblickt, und es überlebt Personalwechsel. Der Klartext bleibt zudem für immer in der Historie, auch nach dem Löschen. Verschlüsselung kostet einen Nachmittag Einrichtung; der Alternativpfad kostet im Ernstfall den Wechsel sämtlicher Zugangsdaten.

11.6 Lab: GitOps für einen einzelnen Server

/usr/local/bin/gitops-abgleich.sh
#!/usr/bin/env bash
#
# Minimaler GitOps-Abgleich: holt den Sollzustand und stellt ihn her.
#
set -euo pipefail
 
REPO="${HOME}/kfz-betrieb"
UMGEBUNG="produktion"
LOG_TAG="gitops"
 
log() { logger -t "$LOG_TAG" "$*"; echo "$*"; }
 
cd "$REPO"
 
# --- 1. Sollzustand holen ---
ALT="$(git rev-parse HEAD)"
git fetch --quiet origin
git reset --quiet --hard origin/main
NEU="$(git rev-parse HEAD)"
 
# --- 2. Gewünschtes Image aus der Konfiguration lesen ---
SOLL_IMAGE="$(grep '^image:' "${UMGEBUNG}/kfz-rechner.yaml" | awk '{print $2}')"
 
# --- 3. Ist-Zustand ermitteln ---
IST_IMAGE="$(podman inspect kfz-rechner --format '{{.ImageName}}' 2>/dev/null || echo "keiner")"
 
if [[ "$SOLL_IMAGE" == "$IST_IMAGE" ]]; then
    exit 0                       # Soll = Ist, nichts zu tun
fi
 
log "Abweichung erkannt: ist=${IST_IMAGE} soll=${SOLL_IMAGE} (commit ${NEU:0:8})"
 
# --- 4. Sollzustand herstellen ---
podman pull "$SOLL_IMAGE"
sed -i "s|^Image=.*|Image=${SOLL_IMAGE}|" \
    "${HOME}/.config/containers/systemd/kfz-rechner.container"
 
systemctl --user daemon-reload
systemctl --user restart kfz-rechner.service
 
# --- 5. Ergebnis prüfen, sonst zurückrollen ---
sleep 5
if ! curl -fsS --max-time 5 http://127.0.0.1:8081/health > /dev/null; then
    log "FEHLER: Dienst antwortet nicht. Rolle zurück auf ${IST_IMAGE}."
    sed -i "s|^Image=.*|Image=${IST_IMAGE}|" \
        "${HOME}/.config/containers/systemd/kfz-rechner.container"
    systemctl --user daemon-reload
    systemctl --user restart kfz-rechner.service
    exit 1
fi
 
log "Abgleich erfolgreich: ${SOLL_IMAGE} (commit ${NEU:0:8})"
~/.config/systemd/user/gitops-abgleich.timer
[Unit]
Description=GitOps-Abgleich alle zwei Minuten

[Timer]
OnBootSec=2min
OnUnitActiveSec=2min
AccuracySec=10s

[Install]
WantedBy=timers.target
~/.config/systemd/user/gitops-abgleich.service
[Unit]
Description=GitOps-Abgleich

[Service]
Type=oneshot
ExecStart=/usr/local/bin/gitops-abgleich.sh
systemctl --user enable --now gitops-abgleich.timer
systemctl --user list-timers
journalctl --user -u gitops-abgleich.service -f

Aufgaben:

  1. Ändere im Konfigurationsrepository den Image-Tag und beobachte, wie der Server binnen zwei Minuten nachzieht — ohne dass jemand etwas ausrollt.
  2. Ändere den laufenden Container von Hand. Was passiert beim nächsten Abgleich?
  3. Trage einen nicht existierenden Tag ein. Prüfe, dass der Dienst weiterläuft.
  4. Führe das Rollback über git revert im Konfigurationsrepository durch und stoppe die Zeit.
  5. Überlege: Welche Zugangsdaten braucht die Pipeline in diesem Aufbau noch für die Produktion?

Die letzte Frage ist die Pointe des Kapitels. Die Antwort lautet: keine. Die Pipeline endet an der Registry. Wer sie kompromittiert, kann ein Image ablegen — aber nichts ausrollen, solange niemand den entsprechenden Commit im Konfigurationsrepository freigibt.

11.7 Anti-Pattern

Anti-Pattern Folge Abhilfe
Änderungen von Hand am Zielsystem werden beim nächsten Abgleich überschrieben — oder verhindern ihn ausschließlich über Git
Geheimnisse im Klartext Kompromittierung SOPS, Sealed Secrets, externer Tresor
Anwendungs- und Konfigurationsrepository vermischt Bauvorgang löst Deployment aus, keine saubere Freigabe trennen
Abgleich ohne Prüfung fehlerhafter Zustand wird stur hergestellt Gesundheitsprüfung mit Rückfall
Kein Rückweg Fehler wird endlos neu ausgerollt Rollback im Skript vorsehen
latest im Konfigurationsrepository Git-Verlauf sagt nichts mehr aus feste Version oder Digest

11.8 Reflexionsfrage

Wenn jemand heute Nacht auf einem eurer Produktivsysteme von Hand eine Konfigurationsdatei ändert — wann und wie würdet ihr das erfahren?


Weiter mit Kapitel 12 — Observability