Zum Inhalt springen

GitOps-Workflows: Infrastruktur über Pull Requests verwalten

GitOps-Workflows, die Kubernetes-Infrastruktur über Pull Requests mit ArgoCD und Flux verwalten: Repo-Struktur, Sync-Policies, Health Checks, Rollback.

4 Min. Lesezeit
GitOps-Workflow-Diagramm, das zeigt, wie ein Pull Request die automatisierte Synchronisation von einem Git-Repository zu einem Kubernetes-Cluster über ArgoCD auslöst

Traditionelle Deployment-Pipelines schieben Änderungen in die Produktion: ein CI-Server baut Artefakte und deployed sie auf die Ziele. GitOps kehrt das um: der gewünschte Zustand lebt in Git, und ein Controller innerhalb des Clusters gleicht kontinuierlich die Realität mit dem deklarierten Zustand ab. Keine manuellen kubectl-Befehle, keine imperativen Skripte, keine Abweichung zwischen dem, was du glaubst deployed zu haben, und dem, was tatsächlich läuft.

Der Pull Request wird zum Deployment-Mechanismus. Einen Manifest-Change reviewen, mergen, und der Cluster konvergiert. Ein Deployment zurückrollen, indem man einen Commit reverteert.

Das GitOps-Modell

GitOps hat vier Prinzipien: deklarative Konfiguration, versionskontrollierter Zielzustand, automatisierte Anwendung und kontinuierliche Abstimmung.

ymlyaml
# ❌ Imperatives Deployment — fragil, keine Audit-Trail
# kubectl set image deployment/api api=myapp:v2.3.1
# kubectl scale deployment/api --replicas=5
# kubectl apply -f hotfix-configmap.yaml
# Wer hat das ausgeführt? Wann? Wurde es geprüft?
# Was passiert, wenn der Cluster driftet?
ymlyaml
# ✅ Deklaratives GitOps — Git IST die Quelle der Wahrheit
# environments/production/api/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: production
  labels:
    app: api
    version: v2.3.1
spec:
  replicas: 5
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
        version: v2.3.1
    spec:
      containers:
        - name: api
          image: registry.example.com/api:v2.3.1
          ports:
            - containerPort: 3000
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: 500m
              memory: 512Mi
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 10
            periodSeconds: 15
          readinessProbe:
            httpGet:
              path: /ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10

Jede Änderung ist ein Commit. Jedes Deployment ist ein Merge. Jedes Rollback ist ein Revert.

Repository-Struktur

Trenne Anwendungscode von Deployment-Konfiguration. Anwendungs-Repos enthalten Quellcode und CI-Pipelines. Konfig-Repos enthalten Kubernetes-Manifeste, nach Umgebung organisiert.

# infrastructure-config/ (GitOps-Konfig-Repo)
├── base/                    # Gemeinsame Manifeste
│   ├── api/
│   │   ├── deployment.yaml
│   │   ├── service.yaml
│   │   ├── hpa.yaml
│   │   └── kustomization.yaml
│   └── worker/
│       ├── deployment.yaml
│       ├── service.yaml
│       └── kustomization.yaml
├── environments/
│   ├── staging/
│   │   ├── api/
│   │   │   ├── kustomization.yaml    # Patches für Staging
│   │   │   └── replica-patch.yaml
│   │   └── kustomization.yaml
│   └── production/
│       ├── api/
│       │   ├── kustomization.yaml    # Patches für Prod
│       │   ├── replica-patch.yaml
│       │   └── resource-patch.yaml
│       └── kustomization.yaml
└── argocd/                  # ArgoCD-Application-Definitionen
    ├── staging.yaml
    └── production.yaml

Mit Kustomize definieren Basismanifeste die gemeinsame Konfiguration. Umgebungs-Overlays patchen nur das, was abweicht — Replica-Anzahl, Ressourcenlimits, Umgebungsvariablen.

ymlyaml
# environments/production/api/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
 
resources:
  - ../../../base/api
 
namespace: production
 
patches:
  - path: replica-patch.yaml
  - path: resource-patch.yaml
 
images:
  - name: registry.example.com/api
    newTag: v2.3.1
ymlyaml
# environments/production/api/replica-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 5

ArgoCD Application-Konfiguration

ArgoCD beobachtet das Git-Repository und synchronisiert den Cluster-Zustand entsprechend. Die Application-Ressource definiert, was beobachtet und wie synchronisiert werden soll.

ymlyaml
# argocd/production.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: production-api
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: production
  source:
    repoURL: https://github.com/myorg/infrastructure-config
    targetRevision: main
    path: environments/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true          # Entfernt Ressourcen, die aus Git gelöscht wurden
      selfHeal: true       # Macht manuelle Cluster-Änderungen rückgängig
      allowEmpty: false    # Verhindert versehentliches Löschen aller Ressourcen
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - PruneLast=true
    retry:
      limit: 3
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 1m
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas   # Ignoriert HPA-verwaltete Replicas

Die Einstellung selfHeal: true ist kritisch: sie bedeutet, dass manuelle kubectl-Änderungen automatisch rückgängig gemacht werden. Die Quelle der Wahrheit ist Git, und der Cluster konvergiert kontinuierlich zu ihr.

Automatisierte Image-Updates

Wenn eine CI-Pipeline ein neues Image baut, muss sie das GitOps-Konfig-Repo aktualisieren. Das erzeugt einen Commit, den ArgoCD aufgreift.

ymlyaml
# .github/workflows/build-and-update.yml
name: Build and Update Config
 
on:
  push:
    branches: [main]
 
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      image-tag: ${{ steps.meta.outputs.version }}
    steps:
      - uses: actions/checkout@v4
 
      - name: Build and push image
        id: meta
        run: |
          TAG="v$(date +%Y%m%d)-${GITHUB_SHA::8}"
          echo "version=$TAG" >> $GITHUB_OUTPUT
          docker build -t registry.example.com/api:$TAG .
          docker push registry.example.com/api:$TAG
 
  update-config:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout config repo
        uses: actions/checkout@v4
        with:
          repository: myorg/infrastructure-config
          token: ${{ secrets.CONFIG_REPO_TOKEN }}
 
      - name: Update image tag
        run: |
          cd environments/staging/api
          kustomize edit set image \
            registry.example.com/api:${{ needs.build.outputs.image-tag }}
 
      - name: Commit and push
        run: |
          git config user.name "CI Bot"
          git config user.email "ci@example.com"
          git add .
          git commit -m "chore: update api image to ${{ needs.build.outputs.image-tag }}"
          git push

Health Checks und Rollback

ArgoCD überwacht die Ressourcen-Gesundheit nach der Synchronisation. Wenn ein Deployment die Health Checks nicht besteht, kannst du automatisches Rollback konfigurieren oder es im Sync-Status erkennen.

ymlyaml
# Custom health check für die API
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: production-api
spec:
  # ... source- und destination-Konfiguration
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
  # Ressourcen-Gesundheitsbewertung
  info:
    - name: deployment-strategy
      value: rolling-update
shbash
# Sync-Status prüfen
argocd app get production-api
 
# Sync-Verlauf anzeigen
argocd app history production-api
 
# Auf vorherige Sync-Version zurückrollen
argocd app rollback production-api 2
 
# Oder einfach in Git reverteeren (bevorzugt — Git bleibt Quelle der Wahrheit)
git revert HEAD
git push origin main
# ArgoCD erkennt die Änderung und synchronisiert zurück

Das git-basierte Rollback ist der korrekte GitOps-Ansatz. Anstatt ArgoCDs Rollback-Befehl zu nutzen, reverteere den Commit in Git. So bleibt das Git-Repository als einzige Quelle der Wahrheit erhalten, und die Revert-Commit-Message liefert eine klare Audit-Trail, die erklärt, warum das Rollback passiert ist.

Drift-Erkennung und Alerts

Auch wenn selfHeal aktiviert ist, solltest du Drift-Versuche überwachen: sie deuten oft auf betriebliche Probleme oder Team-Mitglieder hin, die den GitOps-Workflow umgehen.

ymlyaml
# Prometheus-Alert für ArgoCD-Sync-Fehler
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: argocd-alerts
  namespace: monitoring
spec:
  groups:
    - name: argocd
      rules:
        - alert: ArgoCDSyncFailed
          expr: |
            argocd_app_info{sync_status="OutOfSync"}
            * on(name) group_left()
            (time() - argocd_app_info{sync_status="OutOfSync"})
            > 300
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: >-
              Application {{ $labels.name }}
              has been out of sync for more than 5 minutes
            description: >-
              Check ArgoCD for sync errors.
              Run: argocd app get {{ $labels.name }}
 
        - alert: ArgoCDAppDegraded
          expr: |
            argocd_app_info{health_status="Degraded"} == 1
          for: 3m
          labels:
            severity: critical
          annotations:
            summary: >-
              Application {{ $labels.name }}
              health is degraded

Wichtige Erkenntnisse

GitOps macht Git zur einzigen Quelle der Wahrheit für den Infrastrukturzustand: jedes Deployment ist ein Merge, jedes Rollback ist ein Revert, und der Cluster gleicht kontinuierlich mit dem deklarierten Zustand ab, ohne dass imperative Befehle nötig sind. Trenne Anwendungs-Code-Repositories von Infrastruktur-Konfigurations-Repositories, damit Image-Builds Konfigurations-Updates über automatisierte Commits auslösen und eine klare Grenze zwischen dem, was die Anwendung ist, und dem, wie sie läuft, erhalten bleibt. Aktiviere selfHeal und prune in den ArgoCD-Sync-Richtlinien, damit manuelle Cluster-Änderungen automatisch rückgängig gemacht und gelöschte Ressourcen bereinigt werden, was verhindert, dass sich Configuration Drift ansammelt, wenn Teams den GitOps-Workflow umgehen. Überwache Out-of-Sync-Zustände und degraded Health mit Alerts, denn Drift-Versuche deuten oft auf Prozesslücken oder betriebliche Probleme hin: ihre schnelle Erkennung bewahrt die Integrität, die GitOps wertvoll macht.

Wilfredo Rujel

Wilfredo Rujel

Full-Stack-Softwareentwickler

Diesen Beitrag teilenX