CI/CD-Mastery Serie | Teil 10 von 12

Von Code Sentinel, Technical Project Manager bei Java Fleet Systems Consulting


🗺️ Wo du in der Serie stehst

ModulTeileStatus
Modul 1: FoundationsTeil 1-4✅ Abgeschlossen
Modul 2: Docker & ContainerTeil 5-7✅ Abgeschlossen
Modul 3: Deployment-StrategienTeil 8-10🔄 Du bist hier!
Modul 4: Enterprise & AdvancedTeil 11-12📅 Coming Soon

Level: 🟡 PROFESSIONALS | Lesezeit: ~15 Minuten | Voraussetzungen: Teil 1-9


📚 Was du bisher gelernt hast

In den letzten Wochen hast du:

  • ✅ Pipelines gebaut (Teil 1)
  • Security Gates implementiert (Teil 2-4)
  • ✅ Container optimiert und abgesichert (Teil 5-7)
  • ✅ Blue-Green Deployments gemeistert (Teil 8)
  • ✅ Canary Releases mit Kubernetes aufgesetzt (Teil 9)

Heute: Wir machen Git zur Single Source of Truth für deine gesamte Infrastruktur.


⚡ 30-Sekunden-Überblick

Was du heute lernst:

  • 🌱 Einsteiger: Was GitOps ist und warum es dein Leben einfacher macht
  • 🌿 Erfahrene: ArgoCD Setup und praktische Workflows
  • 🌳 Profis: Multi-Environment Management und Drift Detection

Nach diesem Teil kannst du:

  • ✅ GitOps-Prinzipien verstehen und anwenden
  • ✅ ArgoCD in deinem Cluster einrichten
  • ✅ Environments sauber trennen (dev/staging/prod)
  • ✅ Config-Drift automatisch erkennen und korrigieren

👋 Code Sentinel: „Git als Wahrheit – nicht als Hoffnung“

Hey! 👋

Code Sentinel hier. Schön, dass du wieder da bist!

Letzte Woche haben wir Canary Releases mit Kubernetes aufgesetzt. Du kannst jetzt Traffic schrittweise umleiten und bei Problemen automatisch zurückrollen. Solide Basis.

Aber ich hab eine Frage an dich: Weißt du gerade, was WIRKLICH in deinem Cluster läuft?

Nicht was laufen SOLLTE. Was TATSÄCHLICH deployed ist.


Sarah aus Hamburg schrieb mir letzte Woche:

„Code, wir hatten einen Incident. Jemand hat per kubectl einen Hotfix direkt im Cluster gemacht. Drei Wochen später wusste niemand mehr, dass das passiert ist. Deployment aus Git hat den Fix überschrieben. Production down.“

Das ist kein Edge Case. Das ist Alltag.

Das Problem: Deine Pipeline deployed aus Git. Aber zwischen den Deployments passieren Dinge:

  • Manuelle kubectl-Änderungen („nur kurz fixen“)
  • Secrets, die jemand direkt anlegt
  • ConfigMaps, die „temporär“ angepasst werden

Nach drei Wochen: Git sagt A, Cluster hat B. Niemand weiß warum.

GitOps löst das. Nicht durch Hoffnung, sondern durch Automatisierung.

Lass uns das zusammen angehen! 🚀


🟢 GRUNDLAGEN

Was ist GitOps?

GitOps in einem Satz: Git ist die einzige Wahrheit. Der Cluster synchronisiert sich automatisch mit Git – nicht umgekehrt.

Das Prinzip:

GitOps vs Tradionell
Traditionell vs. GitOps Workflow

Abbildung 1: Der fundamentale Unterschied zwischen traditionellem Deployment und GitOps

Die vier GitOps-Prinzipien:

  1. Declarative: Alles als YAML/Code definiert
  2. Versioned: Alles in Git versioniert
  3. Automated: Änderungen werden automatisch angewendet
  4. Continuously Reconciled: Agent prüft und korrigiert Drift

💡 Neu bei GitOps? Stell dir vor, du hast einen Roboter, der alle 3 Minuten prüft: „Stimmt der Cluster noch mit Git überein?“ Wenn nicht, korrigiert er automatisch. Das ist GitOps.


Warum GitOps statt kubectl?

Das kubectl-Problem:

# Montag: Du deployest
kubectl apply -f deployment.yaml

# Mittwoch: Kollege "fixt" was
kubectl set image deployment/app app=myapp:hotfix

# Freitag: Du deployest wieder
kubectl apply -f deployment.yaml
# Hotfix ist weg. Keiner weiß warum.

Mit GitOps:

# Jede Änderung geht durch Git
git commit -m "Hotfix: Update image to myapp:hotfix"
git push

# ArgoCD deployed automatisch
# Und: Der Hotfix ist dokumentiert, reviewed, versioniert

Die Vorteile:

AspektOhne GitOpsMit GitOps
Audit Trailkubectl history (lokal)Git History (permanent)
Rollbackkubectl rollout undo (hoffentlich)git revert (garantiert)
Review„Vertrau mir“Pull Request
Drift DetectionManuellAutomatisch
Disaster Recovery😰git clone + sync

🟡 PROFESSIONALS

ArgoCD Setup

ArgoCD ist der De-facto-Standard für GitOps. Lass uns das aufsetzen.

Installation:

# Namespace erstellen
kubectl create namespace argocd

# ArgoCD installieren
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Warten bis alles läuft
kubectl wait --for=condition=available deployment/argocd-server -n argocd --timeout=300s

# Admin-Passwort holen
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

CLI installieren:

# macOS
brew install argocd

# Linux
curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x argocd
sudo mv argocd /usr/local/bin/

Login:

# Port-Forward für lokalen Zugriff
kubectl port-forward svc/argocd-server -n argocd 8080:443 &

# Login (admin + Passwort von oben)
argocd login localhost:8080

Repository-Struktur für GitOps

Empfohlene Struktur:

gitops-repo/
├── apps/                    # Application Definitions
│   ├── my-java-app/
│   │   ├── base/           # Basis-Konfiguration
│   │   │   ├── deployment.yaml
│   │   │   ├── service.yaml
│   │   │   └── kustomization.yaml
│   │   └── overlays/       # Environment-spezifisch
│   │       ├── dev/
│   │       │   └── kustomization.yaml
│   │       ├── staging/
│   │       │   └── kustomization.yaml
│   │       └── prod/
│   │           └── kustomization.yaml
│   └── another-app/
│       └── ...
├── infrastructure/          # Cluster-weite Ressourcen
│   ├── namespaces.yaml
│   ├── network-policies.yaml
│   └── resource-quotas.yaml
└── argocd/                  # ArgoCD Application Manifests
    ├── my-java-app-dev.yaml
    ├── my-java-app-staging.yaml
    └── my-java-app-prod.yaml

Warum diese Struktur?

  • apps/: Jede App hat ihre eigene Konfiguration
  • base/: Gemeinsame Basis für alle Environments
  • overlays/: Environment-spezifische Anpassungen (Replicas, Ressourcen, etc.)
  • infrastructure/: Cluster-weite Konfigurationen
  • argocd/: ArgoCD Application Definitions

Erste Application anlegen

Base Deployment (apps/my-java-app/base/deployment.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-java-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-java-app
  template:
    metadata:
      labels:
        app: my-java-app
    spec:
      containers:
      - name: app
        image: myregistry/my-java-app:latest
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "256Mi"
            cpu: "100m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 30
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 10

Base Kustomization (apps/my-java-app/base/kustomization.yaml):

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml

Production Overlay (apps/my-java-app/overlays/prod/kustomization.yaml):

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: production

resources:
  - ../../base

# Production-spezifische Anpassungen
replicas:
  - name: my-java-app
    count: 3

images:
  - name: myregistry/my-java-app
    newTag: v1.2.3  # Feste Version in Prod!

patches:
  - patch: |-
      - op: replace
        path: /spec/template/spec/containers/0/resources/requests/memory
        value: "512Mi"
      - op: replace
        path: /spec/template/spec/containers/0/resources/limits/memory
        value: "1Gi"
    target:
      kind: Deployment
      name: my-java-app

ArgoCD Application Definition

argocd/my-java-app-prod.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-java-app-prod
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  
  source:
    repoURL: https://github.com/your-org/gitops-repo.git
    targetRevision: main
    path: apps/my-java-app/overlays/prod
  
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  
  syncPolicy:
    automated:
      prune: true      # Entfernt Ressourcen die nicht mehr in Git sind
      selfHeal: true   # Korrigiert manuelle Änderungen automatisch
      allowEmpty: false
    syncOptions:
      - CreateNamespace=true
      - PruneLast=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

Was passiert hier?

  • automated.prune: Löscht Ressourcen, die aus Git entfernt wurden
  • automated.selfHeal: Korrigiert Drift automatisch (jemand ändert was per kubectl → wird zurückgesetzt)
  • retry: Bei Fehlern automatisch wiederholen

Application deployen:

kubectl apply -f argocd/my-java-app-prod.yaml

Sync und Rollback

Status prüfen:

# Alle Applications anzeigen
argocd app list

# Details einer App
argocd app get my-java-app-prod

# Sync-Status
argocd app sync my-java-app-prod --dry-run

Manueller Sync (wenn automated deaktiviert):

argocd app sync my-java-app-prod

Rollback:

# History anzeigen
argocd app history my-java-app-prod

# Zu bestimmter Revision zurück
argocd app rollback my-java-app-prod 2

# Oder besser: Git revert und pushen
git revert HEAD
git push
# ArgoCD synct automatisch

🔵 BONUS

Drift Detection in der Praxis

ArgoCD Sync-Zyklus

Abbildung 2: So funktioniert der kontinuierliche Sync-Zyklus in ArgoCD

Was ist Drift?

Drift = Unterschied zwischen Git (gewünschter State) und Cluster (tatsächlicher State).

ArgoCD zeigt Drift automatisch:

# Drift anzeigen
argocd app diff my-java-app-prod

Beispiel-Output:

===== apps/Deployment my-java-app ======
--- live
+++ git
@@ -15,7 +15,7 @@
     spec:
       containers:
       - name: app
-        image: myregistry/my-java-app:hotfix-xyz  # Jemand hat manuell geändert
+        image: myregistry/my-java-app:v1.2.3      # Git sagt was anderes

Self-Healing in Action:

Mit selfHeal: true korrigiert ArgoCD das automatisch innerhalb von 3 Minuten (Standard-Sync-Interval).

Notification bei Drift (Slack-Integration):

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  service.slack: |
    token: $slack-token
  template.app-out-of-sync: |
    message: |
      Application {{.app.metadata.name}} is out of sync!
      Sync Status: {{.app.status.sync.status}}
      Health: {{.app.status.health.status}}
  trigger.on-sync-status-unknown: |
    - when: app.status.sync.status == 'OutOfSync'
      send: [app-out-of-sync]

Multi-Environment Workflow

Der sichere Weg: Environment Promotion

┌─────────┐    PR     ┌─────────┐    PR     ┌─────────┐
│   dev   │ ───────► │ staging │ ───────► │  prod   │
└─────────┘  merge    └─────────┘  merge    └─────────┘
     │                     │                     │
     ▼                     ▼                     ▼
 dev-cluster         staging-cluster        prod-cluster

Workflow:

  1. Entwicklung: Änderungen in overlays/dev/ → Auto-Sync
  2. Testing: PR von dev → staging Branch → Review → Merge
  3. Production: PR von staging → main Branch → Review → Approval → Merge

Branch-Strategie für GitOps:

# Dev: Immer auf develop Branch
spec:
  source:
    targetRevision: develop
    
# Staging: Auf staging Branch
spec:
  source:
    targetRevision: staging
    
# Prod: Nur main/master, mit Tag
spec:
  source:
    targetRevision: v1.2.3  # Oder main mit commit SHA

Secrets in GitOps

Das Problem: Secrets in Git = Sicherheitsrisiko

Lösung 1: Sealed Secrets

# Sealed Secrets Controller installieren
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml

# Secret verschlüsseln
kubeseal --format yaml < secret.yaml > sealed-secret.yaml

# Sealed Secret in Git committen (sicher!)
git add sealed-secret.yaml
git commit -m "Add sealed secret"

Lösung 2: External Secrets Operator

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-secret
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: my-secret
  data:
    - secretKey: password
      remoteRef:
        key: secret/data/myapp
        property: password

Code Sentinel’s Empfehlung:

„Sealed Secrets für den Anfang. External Secrets mit Vault für Enterprise. Niemals Plain-Text Secrets in Git – auch nicht in privaten Repos.“

🔗 Externe Links – Teil 10: GitOps & ArgoCD

Für Einsteiger 🌱

RessourceBeschreibung
ArgoCD Getting StartedOffizielle Schnellstart-Anleitung
Kustomize TutorialInteraktives Kustomize-Tutorial
GitOps PrinciplesWas ist GitOps? Der offizielle Standard

Offizielle Dokumentation 📚

RessourceBeschreibung
ArgoCD DocsKomplette ArgoCD-Referenz
ArgoCD Best PracticesEmpfehlungen für Production
Flux CD DocsAlternative zu ArgoCD
Kustomize DocsOffizielle Kustomize-Dokumentation

Tools & Repositories 🛠️

ToolBeschreibung
ArgoCD GitHubSource Code & Issues
Kustomize GitHubKustomize Repository
Sealed SecretsSecrets sicher in Git speichern
External Secrets OperatorVault/AWS Secrets Manager Integration
kubeseal CLIInstallation & Verwendung

Fortgeschritten 🌳

RessourceBeschreibung
ArgoCD ApplicationSetsMulti-Cluster GitOps
ArgoCD NotificationsSlack/Teams Integration
Argo RolloutsProgressive Delivery mit ArgoCD

✅ Checkpoint

Hast du heute gelernt:

  • [ ] Was GitOps ist und warum es wichtig ist?
  • [ ] Wie ArgoCD installiert und konfiguriert wird?
  • [ ] Wie du Applications mit Kustomize strukturierst?
  • [ ] Wie Drift Detection funktioniert?
  • [ ] Wie du Secrets sicher in GitOps handhabst?

Quick-Test:

  1. Was macht selfHeal: true in ArgoCD?
  2. Warum sollte Production ein festes Image-Tag haben?
  3. Wie rollst du eine fehlerhafte Änderung zurück?

🎨 Challenge für diese Woche

Deine Mission:

  1. Installiere ArgoCD in deinem Cluster (oder Minikube)
  2. Erstelle ein GitOps-Repository mit der empfohlenen Struktur
  3. Deploye eine einfache Java-App (oder das AutoTech-Projekt aus Teil 9)
  4. Teste Drift Detection: Ändere etwas per kubectl und beobachte ArgoCD

Bonus: Richte Slack-Notifications für Sync-Failures ein.


❓ FAQ

Frage 1: ArgoCD oder Flux – was soll ich nehmen?

Beide sind solide. ArgoCD hat die bessere UI und ist einsteigerfreundlicher. Flux ist leichtgewichtiger und besser für Multi-Tenancy. Für die meisten Teams: ArgoCD.

Frage 2: Muss ich Kustomize verwenden?

Nein. ArgoCD unterstützt auch Helm, plain YAML und Jsonnet. Kustomize ist aber der sauberste Ansatz für Environment-Overlays ohne Template-Komplexität.

Frage 3: Was wenn ArgoCD selbst ausfällt?

Dein Cluster läuft weiter – ArgoCD ist nur für Sync zuständig. Bei Ausfall: Kein Auto-Sync, kein Self-Heal. Nach Recovery synct ArgoCD automatisch nach.

Frage 4: Wie oft synct ArgoCD?

Standard: Alle 3 Minuten. Konfigurierbar in der ArgoCD ConfigMap. Für kritische Apps: Webhook-Trigger bei Git Push.

Frage 5: Kann ich ArgoCD auch für Helm Charts nutzen?

Ja, nativ unterstützt. Du kannst Helm Charts direkt referenzieren oder Helm-generierte Manifests in Git committen (empfohlen für Audit-Trail).

Frage 6: Wie verhindere ich, dass jemand kubectl benutzt?

RBAC einschränken. Entwickler bekommen nur Read-Access auf Cluster. Alle Änderungen gehen durch Git. ArgoCD macht den Rest.

Frage 7: Was macht ihr bei Java Fleet, wenn jemand trotzdem kubectl benutzt?

lacht Das gehört eher zu private logs. Aber sagen wir mal so: Kofi hat einmal „nur kurz was gefixed“ und ArgoCD hat es 3 Minuten später zurückgesetzt. Die Slack-Notification kam schneller als sein „Done!“-Post. Seitdem respektiert er den Prozess. 🔒


📖 CI/CD-Mastery Serie – Alle Teile

TeilThemaStatus
1Erste Pipeline
2Security Gates (OWASP & Trivy)
3Coverage Gates (JaCoCo)
4Quality Gates (SonarQube)
5Multi-Stage Docker Builds
6Container Security (SBOM)
7Registry Integration
8Blue-Green Deployments
9Canary & Kubernetes
→ 10GitOps & Environments📍 Du bist hier
11Jenkins Enterprise📅 Nächste Woche
12Multi-Platform & Finale📅 Coming Soon

📦 Downloads

RessourceBeschreibung
gitops-starter-kit.zipRepository-Struktur, ArgoCD Configs, Beispiel-App
argocd-setup.shInstallation Script für lokales Testing
kustomize-cheatsheet.pdfSchnellreferenz für Overlays

Quick Start:

# 1. ZIP entpacken
unzip gitops-starter-kit.zip
cd gitops-starter-kit

# 2. ArgoCD installieren (Minikube vorausgesetzt)
./argocd-setup.sh

# 3. Beispiel-App deployen
kubectl apply -f argocd/example-app.yaml

🔮 Nächste Woche: Jenkins Enterprise

Teil 11: Jenkins für große Teams – Shared Libraries & Distributed Builds

Du lernst:

  • Shared Libraries schreiben (DRY für Pipelines)
  • Pipeline-Templates für 100+ Projekte
  • Configuration as Code
  • Jenkins skalieren

Warum Jenkins nach GitHub Actions?

Viele Enterprises haben Jenkins-Infrastruktur. Shared Libraries sind das Feature, das Jenkins für große Teams unverzichtbar macht.


👋 Bis nächste Woche!

GitOps ist ein Mindset-Shift. Am Anfang fühlt es sich langsamer an – jede Änderung durch Git, Reviews, PRs.

Aber nach dem ersten Incident, den du in 30 Sekunden per git revert gelöst hast, wirst du es nicht mehr missen wollen.

Fragen? Schreib mir: code.sentinel@java-developer.online

Keep deploying, keep learning! 🛡️


Bernd’s Corner:

„GitOps? In meiner Zeit haben wir FTP benutzt und gehofft, dass es klappt. Heute beschweren sich Leute, dass ein PR 5 Minuten dauert. Weißt du, wie lange ein FTP-Upload über ISDN gedauert hat? Ich auch nicht mehr. Ich hab’s verdrängt.“

— Bernd, der sich weigert, seine kubectl-Aliase aufzugeben


Tags: #GitOps #ArgoCD #Kubernetes #DevOps #CI/CD

© 2025 Java Fleet Systems Consulting | java-developer.online

Autor

  • Code Sentinel

    32 Jahre alt, Technical Project Manager und Security-Experte bei Java Fleet Systems Consulting. Code ist ein erfahrener Entwickler, der in die Projektleitung aufgestiegen ist, aber immer noch tief in der Technik verwurzelt bleibt. Seine Mission: Sicherstellen, dass Projekte termingerecht, sicher und wartbar geliefert werden.