CI/CD-Mastery Serie | Teil 11 von 12
Von Code Sentinel, Technical Project Manager bei Java Fleet Systems Consulting
🗺️ Wo du in der Serie stehst
| Modul | Teile | Status |
|---|---|---|
| Modul 1: Foundations | Teil 1-4 | ✅ Abgeschlossen |
| Modul 2: Docker & Container | Teil 5-7 | ✅ Abgeschlossen |
| Modul 3: Deployment-Strategien | Teil 8-10 | ✅ Abgeschlossen |
| Modul 4: Enterprise & Advanced | Teil 11-12 | 🔄 Du bist hier! |
Level: 🟡 PROFESSIONALS | Lesezeit: ~15 Minuten | Voraussetzungen: Teil 1-10
📚 Was du bisher gelernt hast
Dein CI/CD-Stack bis jetzt:
- ✅ GitHub Actions Pipelines (Teil 1)
- ✅ Security & Quality Gates (Teil 2-4)
- ✅ Container-Workflows (Teil 5-7)
- ✅ Deployment-Strategien (Teil 8-9)
- ✅ GitOps mit ArgoCD (Teil 10)
Heute: Jenkins für Enterprise-Szenarien – wenn du 50+ Projekte mit konsistenten Pipelines managen musst.
⚡ 30-Sekunden-Überblick
Was du heute lernst:
- 🌱 Einsteiger: Warum Jenkins in Enterprises noch relevant ist
- 🌿 Erfahrene: Shared Libraries schreiben und nutzen
- 🌳 Profis: Pipeline-Templates für große Teams
Nach diesem Teil kannst du:
- ✅ Den Unterschied zwischen GitHub Actions und Jenkins Enterprise verstehen
- ✅ Eine Shared Library von Grund auf erstellen
- ✅ Pipeline-Templates für Teams designen
- ✅ Jenkins Configuration as Code (JCasC) einsetzen
👋 Code Sentinel: „Die Enterprise-Realität“
Hey! 👋
Code Sentinel hier.
Ich weiß was du denkst: „Jenkins? Ernsthaft? Wir haben doch GitHub Actions gelernt!“
Fair point. Für Greenfield-Projekte ist GitHub Actions oft die bessere Wahl. Einfacher, integrierter, weniger Maintenance.
Aber hier ist die Enterprise-Realität:
Oliver aus Frankfurt (Platform Engineer bei einem Finanzdienstleister) schrieb mir:
„Code, wir haben 180 Java-Microservices. Alle brauchen die gleiche Pipeline: Build, Test, Security-Scan, Container, Deploy. Mit GitHub Actions copy-paste ich 180 Workflow-Files. Bei jeder Änderung muss ich 180 PRs machen. Das skaliert nicht.“
Das Problem:
Abbildung 1: Der Maintenance-Unterschied bei 180 Services
Jenkins mit Shared Libraries:
Das ist der Grund, warum Jenkins in Enterprises nicht wegzudenken ist.
Lass uns das bauen! 🚀
🟢 GRUNDLAGEN
Wann Jenkins, wann GitHub Actions?

Entscheidungsmatrix:
| Szenario | Empfehlung |
|---|---|
| Kleines Team, < 10 Repos | GitHub Actions |
| Open Source Projekt | GitHub Actions |
| Greenfield, Cloud-native | GitHub Actions |
| 50+ Repos, gleiche Pipeline | Jenkins + Shared Libraries |
| On-Premise Requirement | Jenkins |
| Komplexe Genehmigungsprozesse | Jenkins |
| Bestehende Jenkins-Infrastruktur | Jenkins optimieren |
💡 Neu bei Jenkins? Jenkins ist ein Open-Source Automation Server. Pipelines werden in Groovy geschrieben (Jenkinsfile). Shared Libraries sind wiederverwendbare Groovy-Module.
Jenkins Pipeline Basics (Auffrischer)
Einfaches Jenkinsfile:
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'mvn clean package'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
}
stage('Deploy') {
steps {
sh 'kubectl apply -f k8s/'
}
}
}
post {
always {
junit '**/target/surefire-reports/*.xml'
}
}
}
Das Problem bei 180 Services:
Jeder Service hat fast das gleiche Jenkinsfile. Aber „fast“ ist das Problem:
- Service A braucht Java 17
- Service B braucht Java 21
- Service C hat zusätzliche Integration Tests
- Service D deployed nach AWS statt Azure
Copy-Paste führt zu Drift. Nach 6 Monaten hat jedes Jenkinsfile kleine Unterschiede. Maintenance-Hölle.
🟡 PROFESSIONALS
Shared Library Struktur
Abbildung 2: Die Architektur einer Jenkins Shared Library
Repository-Aufbau:
jenkins-shared-library/ ├── vars/ # Global verfügbare Funktionen │ ├── javaPipeline.groovy # Haupt-Pipeline │ ├── buildJavaApp.groovy # Build-Step │ ├── runSecurityScan.groovy │ └── deployToKubernetes.groovy ├── src/ # Groovy Klassen │ └── com/ │ └── javafleet/ │ └── PipelineConfig.groovy ├── resources/ # Nicht-Groovy Dateien │ └── scripts/ │ └── deploy.sh └── README.md
Wichtig:
vars/= Globale Funktionen, direkt aufrufbarsrc/= Klassen für komplexere Logikresources/= Shell-Scripts, Config-Files
Erste Shared Library schreiben
vars/javaPipeline.groovy:
#!/usr/bin/env groovy
def call(Map config = [:]) {
// Defaults setzen
def javaVersion = config.javaVersion ?: '17'
def deployEnv = config.deployEnv ?: 'dev'
def skipTests = config.skipTests ?: false
def enableSecurityScan = config.enableSecurityScan ?: true
pipeline {
agent {
kubernetes {
yaml """
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.9-eclipse-temurin-${javaVersion}
command: ['cat']
tty: true
- name: docker
image: docker:24-dind
securityContext:
privileged: true
"""
}
}
environment {
MAVEN_OPTS = '-Xmx1024m'
DOCKER_REGISTRY = credentials('docker-registry')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
container('maven') {
sh 'mvn clean package -DskipTests'
}
}
}
stage('Test') {
when {
expression { !skipTests }
}
steps {
container('maven') {
sh 'mvn test'
}
}
post {
always {
junit '**/target/surefire-reports/*.xml'
}
}
}
stage('Security Scan') {
when {
expression { enableSecurityScan }
}
steps {
container('maven') {
sh 'mvn org.owasp:dependency-check-maven:check'
}
}
post {
always {
dependencyCheckPublisher pattern: '**/dependency-check-report.xml'
}
}
}
stage('Build Container') {
steps {
container('docker') {
script {
def image = docker.build("${env.DOCKER_REGISTRY}/${env.JOB_NAME}:${env.BUILD_NUMBER}")
image.push()
image.push('latest')
}
}
}
}
stage('Deploy') {
steps {
deployToKubernetes(
environment: deployEnv,
image: "${env.DOCKER_REGISTRY}/${env.JOB_NAME}:${env.BUILD_NUMBER}"
)
}
}
}
post {
success {
slackSend(
color: 'good',
message: "✅ ${env.JOB_NAME} #${env.BUILD_NUMBER} deployed to ${deployEnv}"
)
}
failure {
slackSend(
color: 'danger',
message: "❌ ${env.JOB_NAME} #${env.BUILD_NUMBER} failed"
)
}
}
}
}
Modulare Helper-Funktionen
vars/deployToKubernetes.groovy:
#!/usr/bin/env groovy
def call(Map config) {
def environment = config.environment
def image = config.image
def namespace = config.namespace ?: environment
def timeout = config.timeout ?: 300
echo "Deploying ${image} to ${environment}"
// Kubeconfig basierend auf Environment
withCredentials([file(credentialsId: "kubeconfig-${environment}", variable: 'KUBECONFIG')]) {
// Image in Deployment updaten
sh """
kubectl set image deployment/\${JOB_NAME} \
app=${image} \
-n ${namespace}
"""
// Warten auf Rollout
sh """
kubectl rollout status deployment/\${JOB_NAME} \
-n ${namespace} \
--timeout=${timeout}s
"""
}
echo "Deployment to ${environment} successful"
}
vars/runSecurityScan.groovy:
#!/usr/bin/env groovy
def call(Map config = [:]) {
def failOnCritical = config.failOnCritical ?: true
def cvssThreshold = config.cvssThreshold ?: 7.0
stage('OWASP Dependency Check') {
sh """
mvn org.owasp:dependency-check-maven:check \
-DfailBuildOnCVSS=${cvssThreshold}
"""
dependencyCheckPublisher pattern: '**/dependency-check-report.xml'
}
stage('Trivy Container Scan') {
def trivyArgs = failOnCritical ? '--exit-code 1 --severity CRITICAL,HIGH' : ''
sh """
trivy image ${trivyArgs} \
--format json \
--output trivy-report.json \
\${DOCKER_REGISTRY}/\${JOB_NAME}:\${BUILD_NUMBER}
"""
archiveArtifacts artifacts: 'trivy-report.json'
}
}
Library in Jenkins einbinden
Methode 1: Global in Jenkins konfigurieren
- Jenkins verwalten → System konfigurieren
- Global Pipeline Libraries
- Name:
java-fleet-library - Default Version:
main - Retrieval Method: Modern SCM → Git
- Repository URL:
https://github.com/your-org/jenkins-shared-library.git
Methode 2: Im Jenkinsfile deklarieren
@Library('java-fleet-library@main') _
javaPipeline(
javaVersion: '21',
deployEnv: 'staging'
)
Verwendung in Projekten
Minimales Jenkinsfile (3 Zeilen!):
@Library('java-fleet-library') _
javaPipeline()
Mit Konfiguration:
@Library('java-fleet-library') _
javaPipeline(
javaVersion: '21',
deployEnv: 'production',
enableSecurityScan: true,
skipTests: false
)
Mit Custom Stages:
@Library('java-fleet-library') _
javaPipeline(
javaVersion: '17',
deployEnv: 'dev',
additionalStages: {
stage('Integration Tests') {
steps {
sh 'mvn verify -Pintegration'
}
}
}
)
🔵 BONUS
Configuration as Code (JCasC)
Das Problem: Jenkins-Konfiguration nur über UI? Nicht reproduzierbar, kein Audit-Trail.
JCasC löst das:
jenkins.yaml:
jenkins:
systemMessage: "Java Fleet CI/CD Server"
numExecutors: 0 # Controller führt keine Builds aus
securityRealm:
ldap:
configurations:
- server: "ldaps://ldap.company.com"
rootDN: "dc=company,dc=com"
userSearchBase: "ou=users"
userSearch: "uid={0}"
authorizationStrategy:
roleBased:
roles:
global:
- name: "admin"
permissions:
- "Overall/Administer"
entries:
- group: "jenkins-admins"
- name: "developer"
permissions:
- "Overall/Read"
- "Job/Build"
- "Job/Read"
entries:
- group: "developers"
clouds:
- kubernetes:
name: "kubernetes"
serverUrl: "https://kubernetes.default.svc"
namespace: "jenkins"
jenkinsUrl: "http://jenkins.jenkins.svc:8080"
podLabels:
- key: "jenkins"
value: "agent"
unclassified:
globalLibraries:
libraries:
- name: "java-fleet-library"
defaultVersion: "main"
retriever:
modernSCM:
scm:
git:
remote: "https://github.com/your-org/jenkins-shared-library.git"
credentialsId: "github-token"
slackNotifier:
teamDomain: "javafleet"
tokenCredentialId: "slack-token"
room: "#ci-notifications"
credentials:
system:
domainCredentials:
- credentials:
- usernamePassword:
scope: GLOBAL
id: "docker-registry"
username: "${DOCKER_USER}"
password: "${DOCKER_PASSWORD}"
- file:
scope: GLOBAL
id: "kubeconfig-production"
fileName: "kubeconfig"
secretBytes: "${KUBECONFIG_PROD_BASE64}"
Jenkins Helm Chart mit JCasC:
# values.yaml
controller:
JCasC:
configScripts:
welcome-message: |
jenkins:
systemMessage: "Java Fleet CI/CD"
installPlugins:
- kubernetes:latest
- workflow-aggregator:latest
- git:latest
- configuration-as-code:latest
- job-dsl:latest
Distributed Builds für große Teams
Problem: 180 Services, alle wollen gleichzeitig bauen.
Lösung: Kubernetes-basierte Agents
// In der Shared Library
def call(Map config = [:]) {
def podTemplate = """
apiVersion: v1
kind: Pod
metadata:
labels:
jenkins: agent
spec:
serviceAccountName: jenkins-agent
containers:
- name: maven
image: maven:3.9-eclipse-temurin-${config.javaVersion ?: '17'}
command: ['cat']
tty: true
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2000m"
volumeMounts:
- name: maven-cache
mountPath: /root/.m2
- name: docker
image: docker:24-dind
securityContext:
privileged: true
volumeMounts:
- name: docker-storage
mountPath: /var/lib/docker
volumes:
- name: maven-cache
persistentVolumeClaim:
claimName: maven-cache
- name: docker-storage
emptyDir: {}
"""
pipeline {
agent {
kubernetes {
yaml podTemplate
defaultContainer 'maven'
}
}
// ... stages
}
}
Skalierung:
- Kubernetes skaliert Pods automatisch
- Maven-Cache als PVC = schnellere Builds
- Jeder Build bekommt eigenen Pod = Isolation
Pipeline-Templates mit Job DSL
Für 180 Services automatisch Jobs erstellen:
seed-job.groovy:
// Liste aller Services aus Git oder Config
def services = [
[name: 'user-service', javaVersion: '21', team: 'platform'],
[name: 'order-service', javaVersion: '17', team: 'commerce'],
[name: 'payment-service', javaVersion: '21', team: 'finance'],
// ... 177 weitere
]
// Für jeden Service einen Job erstellen
services.each { service ->
multibranchPipelineJob("${service.team}/${service.name}") {
displayName(service.name)
branchSources {
git {
remote("https://github.com/your-org/${service.name}.git")
credentialsId('github-token')
}
}
orphanedItemStrategy {
discardOldItems {
numToKeep(10)
}
}
factory {
workflowBranchProjectFactory {
scriptPath('Jenkinsfile')
}
}
configure { node ->
def traits = node / sources / data / 'jenkins.branch.BranchSource' / source / traits
traits << 'jenkins.plugins.git.traits.BranchDiscoveryTrait' {}
}
}
}
Ergebnis: Ein Seed-Job erstellt/aktualisiert alle 180 Service-Jobs automatisch.
✅ Checkpoint
Hast du heute gelernt:
- [ ] Wann Jenkins sinnvoller ist als GitHub Actions?
- [ ] Wie Shared Libraries strukturiert werden?
- [ ] Wie du eine Pipeline-Funktion in
vars/schreibst? - [ ] Wie JCasC Jenkins-Konfiguration versionierbar macht?
- [ ] Wie Job DSL Jobs automatisch erstellt?
Quick-Test:
- Wo liegen globale Funktionen in einer Shared Library?
- Was macht
@Library('name') _im Jenkinsfile? - Warum sollte der Jenkins Controller keine Builds ausführen?
🎨 Challenge für diese Woche
Deine Mission:
- Erstelle eine Shared Library mit einer
javaPipeline-Funktion - Die Pipeline soll Build, Test und Security-Scan enthalten
- Mache Java-Version und Deploy-Environment konfigurierbar
- Teste mit mindestens 2 verschiedenen Projekten
Bonus: Füge JCasC hinzu, um die Library automatisch zu konfigurieren.
❓ FAQ
Frage 1: Kann ich GitHub Actions Workflows auch wiederverwenden?
Ja, mit Composite Actions oder Reusable Workflows. Aber: Du brauchst trotzdem ein Workflow-File pro Repo. Bei 180 Repos ist das mehr Overhead als ein 3-Zeilen-Jenkinsfile.
Frage 2: Wie teste ich Shared Libraries lokal?
Mit dem Jenkins Pipeline Unit Framework:
@Test
void testJavaPipeline() {
def script = loadScript('vars/javaPipeline.groovy')
script.call(javaVersion: '17')
// Assertions...
}
Frage 3: Was wenn ein Service eine komplett andere Pipeline braucht?
Dann schreib ein eigenes Jenkinsfile. Die Shared Library ist ein Angebot, kein Zwang. Für 90% Standard-Pipeline, für 10% Custom.
Frage 4: Wie migriere ich von GitHub Actions zu Jenkins?
Nicht komplett migrieren. Nutze Jenkins für die großen Projekte mit Shared Libraries. Lass kleinere Projekte bei GitHub Actions. Hybrid ist okay.
Frage 5: Wie update ich die Shared Library ohne alle Builds zu brechen?
Semantic Versioning. Projekte referenzieren @Library('lib@v1'). Breaking Changes gehen in v2. Projekte migrieren nach und nach.
Frage 6: Brauche ich Jenkins, wenn ich schon ArgoCD habe?
Ja, unterschiedliche Aufgaben. Jenkins baut und testet. ArgoCD deployed. Jenkins pusht Images → ArgoCD synct Kubernetes.
Frage 7: Wie geht ihr bei Java Fleet mit Legacy-Jenkins-Installationen um?
seufzt Das ist eher was für private logs. Aber kurz: Franz-Martin hat einmal eine Jenkins-Instanz von 2016 gefunden. Mit Plugins, die es nicht mehr gibt. Das Upgrade hat 3 Wochen gedauert. Kofi nennt es immer noch „Das Jenkins-Massaker von Q2“. Seitdem: JCasC. Immer. 🔧
📖 CI/CD-Mastery Serie – Alle Teile
| Teil | Thema | Status |
|---|---|---|
| 1 | Erste Pipeline | ✅ |
| 2 | Security Gates (OWASP & Trivy) | ✅ |
| 3 | Coverage Gates (JaCoCo) | ✅ |
| 4 | Quality Gates (SonarQube) | ✅ |
| 5 | Multi-Stage Docker Builds | ✅ |
| 6 | Container Security (SBOM) | ✅ |
| 7 | Registry Integration | ✅ |
| 8 | Blue-Green Deployments | ✅ |
| 9 | Canary & Kubernetes | ✅ |
| 10 | GitOps & Environments | ✅ |
| → 11 | Jenkins Enterprise | 📍 Du bist hier |
| 12 | Multi-Platform & Finale | 📅 Nächste Woche |
📦 Downloads
| Ressource | Beschreibung |
|---|---|
| shared-library-starter.zip | Komplette Shared Library Struktur |
| jcasc-example.zip | Jenkins Configuration as Code Templates |
| job-dsl-seed.zip | Seed Job für automatische Job-Erstellung |
Quick Start:
# 1. ZIP entpacken unzip shared-library-starter.zip cd shared-library-starter # 2. Als Git Repo initialisieren git init git add . git commit -m "Initial shared library" # 3. In Jenkins konfigurieren (siehe README)
🔗 Externe Links – Teil 11: Jenkins Enterprise
Für Einsteiger 🌱
| Ressource | Beschreibung |
|---|---|
| Jenkins Pipeline Syntax | Grundlagen der Pipeline-Syntax |
| Shared Libraries Intro | Einstieg in Shared Libraries |
| JCasC Getting Started | Configuration as Code Überblick |
Offizielle Dokumentation 📚
| Ressource | Beschreibung |
|---|---|
| Jenkins Shared Libraries | Komplette Shared Library Referenz |
| Configuration as Code Plugin | JCasC Plugin-Dokumentation |
| Job DSL Plugin | Job DSL Plugin-Dokumentation |
| Job DSL API Viewer | Interaktive API-Referenz |
| Jenkins on Kubernetes | Kubernetes-Installation |
Beispiele & Demos 🎯
| Ressource | Beschreibung |
|---|---|
| JCasC Demos | Offizielle JCasC-Beispiele |
| Pipeline Examples | Jenkins Pipeline-Beispiele |
| Shared Library Examples | Shared Library Patterns |
Helm & Kubernetes 🐳
| Ressource | Beschreibung |
|---|---|
| Jenkins Helm Chart | Offizielles Helm Chart |
| Artifact Hub – Jenkins | Helm Chart auf Artifact Hub |
| Kubernetes Plugin | Dynamische K8s-Agents |
Fortgeschritten 🌳
| Ressource | Beschreibung |
|---|---|
| Pipeline Unit Testing | Shared Libraries testen |
| Jenkins Operator | Jenkins als Kubernetes Operator |
| Jenkins X | Cloud-Native Jenkins |
🔮 Nächste Woche: Multi-Platform & Finale
Teil 12: GitLab CI, Azure DevOps & Platform-agnostisches Design
Du lernst:
- GitLab CI Pipeline Syntax
- Azure DevOps Pipelines
- Wie du Pipelines platform-agnostisch designst
- Migration zwischen Plattformen
Das große Finale der CI/CD-Mastery Serie! 🎉
👋 Bis nächste Woche!
Shared Libraries sind ein Game-Changer für große Teams. Einmal schreiben, überall nutzen. Änderungen zentral, sofort wirksam.
Aber vergiss nicht: Das Tool ist egal, die Prinzipien zählen. Ob Jenkins, GitHub Actions oder GitLab CI – die Konzepte (Gates, Security, Automation) bleiben gleich.
Fragen? Schreib mir: code.sentinel@java-developer.online
Keep automating, keep scaling! 🛡️
Bernd’s Corner:
„Shared Libraries? Wir hatten damals EIN Shell-Script. build.sh. Hat alles gemacht. Okay, 2000 Zeilen, keine Tests, niemand wusste genau was es macht. Aber es hat funktioniert! Meistens. Wenn Mercury nicht retrograd war.“
— Bernd, der sein build.sh von 2003 immer noch als „elegant“ bezeichnet
Tags: #Jenkins #SharedLibraries #DevOps #CI/CD #Enterprise
© 2025 Java Fleet Systems Consulting | java-developer.online

