Jenkins-Pipeline: Local → Test → QS → Prod
Zielgruppe: DevOps. Zusätzlich zur bestehenden GitHub-Actions-Pipeline (siehe infra/ci/README.md) - beide bewusst nebeneinander demonstriert, wie Eureka UND Kubernetes-native Discovery in ADR-0007. Setzt Podman voraus, sonst nichts weiter (kein echter Kubernetes-Cluster nötig, siehe "Stolperstein" unten).
Die vier Stufen
| Stufe | Womit | Auslöser | Ziel |
|---|---|---|---|
| Local | IntelliJ (oder jede andere IDE) + podman compose up -d für die Infrastruktur (Podman-Machine unter Windows) |
manuell, jederzeit | localhost, kein Namespace |
| Test | Jenkins, Stufen "Build & Unit-Tests" bis "Deploy nach Test" | automatisch bei jedem Pipeline-Lauf | Namespace library-test |
| QS | Jenkins, Stufe "Deploy nach QS" | automatisch, direkt nach Test | Namespace library-qs |
| Prod | Jenkins, Stufe "Deploy nach Prod" | manuell freigegeben (input-Step) |
Namespace library |
Local ist keine Jenkins-Stufe - es ist der Ausgangspunkt davor: eine Entwicklerin startet die
Infrastruktur (cd infra && podman compose up -d postgres kafka rabbitmq redis keycloak, siehe
docker-compose-guide.md) und lässt genau den Service, an dem sie
gerade arbeitet, aus der IDE heraus laufen (mvn spring-boot:run bzw. IntelliJs eigener
Spring-Boot-Run-Konfiguration, Default-Profil, zeigt auf localhost). Erst ein Push/PR stößt die
Pipeline an, die dann automatisch bis Test und QS durchläuft und vor Prod auf eine explizite
Freigabe wartet.
Jenkins lokal starten
Seit Phase 12 lebt Jenkins NICHT mehr in diesem Repository, sondern zentral, projekt-
übergreifend in enterprise-workspace/shared/enterprise-infrastructure/ - ein einziges
Jenkins für alle lokal angeschlossenen Lernprojekte statt eines pro Projekt (RAM-/Boot-Zeit-
schonender, entspricht dem, wie ein echtes Team auch nicht pro Repository einen eigenen
Jenkins-Server betreibt):
cd ../../shared/enterprise-infrastructure # relativ zu diesem Projekt
podman compose up -d
Läuft unabhängig davon, welches Lernprojekt gerade geöffnet ist - einmal starten reicht,
bleibt im Hintergrund verfügbar. Details (Aufbau, wie ein weiteres Projekt sich anschließt,
Grenzen) siehe shared/enterprise-infrastructure/README.md.
Anders als bei einem gehosteten Dienst wie Bitbucket (SaaS - Atlassian betreibt den Server,
man meldet sich nur an) ist Jenkins Software, die man selbst betreibt. Es gibt zwar auch
einen nativen Windows-Installer auf jenkins.io, der laeuft aber ohne die im geteilten
shared/enterprise-infrastructure/jenkins/Dockerfile vorinstallierten Werkzeuge (Maven, Node, Podman/Buildah, Helm,
kubectl, Chromium) - die muessten dann manuell auf Windows installiert und in Jenkins
konfiguriert werden. Der Container-Weg oben ist deshalb der in diesem Projekt vorgesehene. (Das
Agent-Image muss podman mitbringen - nachzuruesten in enterprise-infrastructure, dem Nachbar-Repo.)
Was mit Jenkins-Daten (Jobs, Build-Historie, Credentials) beim Stoppen, Neuinstallieren der
Container-Laufzeit oder beim Ziehen einer neueren Jenkins-Version passiert - inklusive des
Zusammenspiels zwischen dem jenkins-data-Volume und plugins.txt - siehe
docker-compose-guide.md
(die dortige Erklärung gilt unverändert, nur dass das Volume jetzt in enterprise-infrastructure statt in
diesem Projekt verwaltet wird).
Web-UI: http://jenkins.localhost (kein localhost:<port> mehr nötig - siehe
reverse-proxy.md für den vollen Kontext, warum und wie das funktioniert).
Anmeldedaten per Umgebungsvariable (JENKINS_ADMIN_USER/JENKINS_ADMIN_PASSWORD in
shared/enterprise-infrastructure/docker-compose.yml, Default admin/admin - für den lokalen Lernbetrieb, nicht
für einen Mehrnutzer-Betrieb gedacht). Der Pipeline-Job library-platform-pipeline existiert
nach dem Start automatisch (siehe shared/enterprise-infrastructure/jenkins/casc.yaml,
Jenkins Configuration-as-Code) - kein Klicken durch den Setup-Wizard nötig.
Voraussetzung für einen echten Build: ein Git-Credential. Dieses Repository ist privat -
ein realer Testlauf (per REST-API ausgelöst, siehe Verifikation unten) scheiterte prompt beim
git fetch mit Invalid username or token. Password authentication is not supported for Git operations. - GitHub verweigert anonymes Klonen. Unter "Manage Jenkins → Credentials" ein
Credential vom Typ "Username with password" anlegen (Username = GitHub-Login, Passwort = ein
GitHub Personal Access Token mit repo-Scope, KEIN echtes Passwort - GitHub akzeptiert seit
Langem nur noch Token), danach in der Job-Konfiguration unter "Source Code Management" als
Credentials auswählen. Bewusst nicht in casc.yaml vorbelegt - ein Token ist ein Geheimnis
(siehe PR-Checkliste, Security) und gehört nicht in eine versionierte Datei, exakt dieselbe
Begründung wie bei den fehlenden KUBECONFIG_*-Credentials weiter unten.
Alle Werkzeuge, die das Jenkinsfile braucht (Maven 3.9.9, Node 20,
Podman/Buildah, Helm, kubectl, Chromium), sind bereits im Image gebaut (siehe
shared/enterprise-infrastructure/jenkins/Dockerfile) - deshalb
kommt das Jenkinsfile ohne Jenkins' eigene, UI-gestützte Tool-Auto-Provisionierung aus. Diese
Versionen sind aktuell auf DIESES eine Projekt zugeschnitten - Grenzen dieses Ansatzes, sobald
ein zweites Projekt mit anderen Tool-Versionen dazukommt, siehe
shared/enterprise-infrastructure/README.md, Abschnitt "Grenzen".
Wie die Umgebungen sich unterscheiden
Kein dupliziertes Set an Values-Dateien pro Umgebung (8 Services × 3 Umgebungen = 24 Dateien
wären unübersichtlich). Stattdessen nutzt die Pipeline Helms Layering-Mechanismus: die
bestehenden infra/helm/values/<service>-values.yaml (2 Replicas, Autoscaling an) sind bereits
"Prod-Shape" - für Prod ändert sich nichts. Für Test und QS liegt je EINE zusätzliche,
service-übergreifende Overlay-Datei unter infra/helm/values/env/ (test-values.yaml,
qs-values.yaml), die als zweite -f hinter der Service-Datei übergeben wird:
helm upgrade --install catalog-service infra/helm/library-service \
-f infra/helm/values/catalog-service-values.yaml \
-f infra/helm/values/env/test-values.yaml \
-n library-test --create-namespace --set image.tag=<git-sha>
Test reduziert Replica-Anzahl und Ressourcen-Limits (schnelle, günstige Automatisierung), QS bleibt näher an Prod-Größen, aber ohne Autoscaling (wenige gleichzeitige menschliche Tester:innen brauchen keine automatische Lastskalierung).
Stolperstein: kein echter Kubernetes-Cluster vorhanden (QS/Prod) - Test-Stufe seit Phase 11 gelöst
Wie in kubernetes-guide.md ausführlich dokumentiert, lief für dieses Projekt lange kein funktionierender Kubernetes-Cluster (CodeReady-Containers/OKD-Versuch an abgelaufenen Zertifikaten und RAM-Konflikten mit der lokalen Compose-Infrastruktur gescheitert). Die Deploy-Stufen würden ins Leere laufen, wenn sie das einfach ignorieren würden.
Lösung, ehrlich statt vorgetäuscht: jede Deploy-Stufe versucht zuerst, ein Jenkins-Credential
namens KUBECONFIG_TEST/KUBECONFIG_QS/KUBECONFIG_PROD (Typ "Secret file") zu laden. Fehlt
es, fällt die Stufe automatisch auf helm template (Dry-Run, reines Rendern der
Kubernetes-Manifeste ohne sie irgendwohin zu schicken) zurück, genau wie es der bestehende
helm-lint-Job in .github/workflows/ci.yml heute schon
tut. Die Pipeline ist damit immer lauffähig und grün, ohne einen Cluster zu behaupten, den
es nicht gibt.
Für die Test-Stufe ist das seit Phase 11 kein Dry-Run-Dauerzustand mehr: ein lokaler
kind-Cluster (siehe kind-cluster.md) liefert ein echtes KUBECONFIG_TEST,
gegen das "Deploy nach Test" wirklich deployt (inklusive kind load docker-image, mit
KIND_EXPERIMENTAL_PROVIDER=podman, vor jedem
Helm-Aufruf, siehe loadImageIntoKindIfClusterAvailable im Jenkinsfile).
QS und Prod bleiben bewusst Dry-Run-Kandidaten: in der Realität wären das separate, dauerhafte
Cluster (Cloud-Managed oder ein eigener CRC/OKD-Cluster), nicht das Laptop einer einzelnen
Person, das nach jedem Neustart wieder bei null anfängt - für sie gilt weiterhin, einfach die
passenden Credentials unter "Manage Jenkins → Credentials" anzulegen, sobald ein solcher Cluster
verfügbar ist, kein Code-Änderung nötig.
Vertiefungsaufgaben (nicht umgesetzt)
- Multibranch-Pipeline statt eines einzelnen Jobs:
casc.yamllegt aktuell einen Job für genaumasteran. EinmultibranchPipelineJob(ebenfalls per Job-DSL) würde automatisch für jeden Branch/PR einen eigenen Pipeline-Lauf anlegen - für dieses Lernprojekt (kein Branch außermasterdauerhaft aktiv) bewusst nicht umgesetzt. - Vollständig rootless Builds ohne Socket-Mount: Podman/Buildah können im Jenkins-Container
rootless bauen (
podman buildohne Daemon) - der aktuelle Weg mountet stattdessen den Podman-Socket des Hosts in den Jenkins-Container, siehe Kommentar beimjenkins-Service inshared/enterprise-infrastructure/docker-compose.yml; für ein lokales Lernprojekt bewusst so in Kauf genommen. - Branch-/Tag-gesteuerte Freigabe (z. B. nur Tags nach Prod, nur
mainnach QS) - die Pipeline läuft heute unabhängig vom Branch immer bis zur Prod-Freigabe durch, ein reales Projekt würde das vermutlich stärker eingrenzen (GitFlow o. Ä.).