Jenkins verhilft Ihnen nicht nur zu Continuous Integration und Continuous Delivery (CI/CD), sondern automatisiert auch andere Routineaufgaben der Softwareentwicklung. Zwar macht Jenkins es nicht überflüssig, Skripte für individuelle Schritte zu schreiben - integriert jedoch sämtliche Build-, Testing- und Deployment-Tools schneller und robuster, als Sie das selbst je bewerkstelligen könnten.
Vor Jenkins waren Entwickler darauf angewiesen, ihren Code sorgfältig zu schreiben und erfolgreich auf einem lokalen Rechner zu testen, wenn sie den nächtlichen Build nicht unterbrechen wollten. Das bedeutete allerdings, seine Änderungen isoliert testen zu müssen - unabhängig von anderen Commits. Insofern gab es keine Garantie dafür, dass der nächtliche Build den letzten Commit überlebt. Jenkins - ursprünglich Hudson - war die direkte Antwort auf diese Einschränkung.
Hudson und Jenkins
Erarbeitet wurde sie von Kohsuke Kawaguchi im Jahr 2004, damals Java-Entwickler bei Sun Microsystems. Der Developer war es leid, Builds abbrechen zu müssen und suchte nach einer Möglichkeit, die Funktionalität des Codes zu testen, bevor dieser an das Repository übergeben wird. Die fand er in Form eines Automatisierungs-Servers in und für Java - genannt Hudson. Die Lösung wurde bei Sun schnell populär und verbreitete sich in quelloffener Form auch in anderen Unternehmen.
Im Jahr 2011 führte ein Streit zwischen Oracle (das Sun übernommen hatte) und der Open-Source-Community von Hudson zu einer Abspaltung mit neuem Namen: Jenkins. Kawaguchi wurde 2014 zum CTO von CloudBees ernannt, einem Unternehmen, das CD-Produkte auf Jenkins-Basis anbietet. Inzwischen ist er beim Startup Launchable tätig und übernimmt bei CloudBees die Rolle eines Beraters.
Zunächst existierten Jenkins und Hudson parallel - inzwischen (2020) wurde Hudson allerdings eingestellt. Bereits im März 2019 gründete die Linux Foundation zusammen mit CloudBees, Google und einer Reihe anderer Unternehmen die Continuous Delivery Foundation (CDF). Die Jenkins-Verantwortlichen sprachen sich dafür aus, der Software-Stiftung ebenfalls beizutreten.
Mit Jenkins automatisieren
Heute ist Jenkins der führende Open-Source-Automatisierungsserver und bietet mehr als 1.800 Plugins, um alle möglichen Entwicklungs-Tasks zu automatisieren. Das ursprünglich von Kawaguchi adressierte Problem (die kontinuierliche Integration und Bereitstellung von Java-Code) ist dabei nur einer von vielen Prozessen, die sich automatisieren lassen. Die verfügbaren Automation-Plugins für Jenkings decken fünf verschiedene Bereiche ab:
Plattformen,
User Interface,
Administration,
Quellcode- und
Build-Management.
Jenkins wird als WAR-Archiv, Installationspaket für die wichtigsten Betriebssysteme, Homebrew-Package, Docker-Image und in Quellcode-Form bereitgestellt. Darüber hinaus unterstützt Jenkins auch die Installation und Skalierung auf Kubernetes. Der Jenkins-Quellcode besteht größtenteils aus Java, mit einigen Groovy-, Ruby- und Antlr-Anteilen.
Sie können Jenkins als WAR-Archiv entweder eigenständig oder als Servlet in einem Java-Anwendungsserver wie Tomcat ausführen. In beiden Fällen wird eine Web-Benutzeroberfläche erstellt und Calls an die REST-API akzeptiert.
Wenn Sie Jenkins zum ersten Mal ausführen, erstellt es ein Admin-Konto mit einem Zufallspasswort. Das können Sie nutzen, um die Installation zu entsperren. Ist Jenkins selbst installiert, haben Sie die Wahl: Entweder arbeiten Sie mit den Standard-Plugins oder Sie wählen Ihre eigenen aus.
Jenkins-Pipelines
Sobald Sie Jenkins konfiguriert haben, ist es an der Zeit, Projekte zu erstellen. Sie können zwar die Web-UI verwenden, um Skripte zu erstellen - die derzeit beste Vorgehensweise ist aber, ein Pipeline-Skript mit dem Namen Jenkinsfile zu erstellen und es in Ihr Repository einzuchecken. Der nachfolgende Screenshot zeigt die Konfiguration für eine Multibranch-Pipeline.
Wie im Screenshot zu sehen, können die Branch Sources für diese Art von Pipeline Git- oder Subversion-Repositories sein, inklusive GitHub. Sollten Sie andere Repositories oder entsprechende Services benötigen, müssen Sie lediglich die entsprechenden Plugins hinzufügen und Jenkins neu starten.
Jenkins-Pipelines können deklarativ oder skriptgesteuert sein. Die simplere Variante - eine deklarative Pipeline - nutzt eine Groovy-kompatible Syntax. Sie können also eine Datei mit #!groovy
starten, um Ihren Editor in die richtige Richtung zu lenken. Eine deklarative Pipeline beginnt mit einem pipeline
-Block, definiert einen agent
und stages
, die ausführbare steps
beinhalten, wie das folgende, dreistufige Beispiel demonstriert:
pipeline {
agent any
stages {
stage('Build') {
echo 'Building..'
}
}
stage('Test') {
steps {
echo 'Testing..'
}
}
stage('Deploy') {
steps {
echo 'Deploying....'
}
}
}
}
pipeline
ist der obligatorische äußere Block, um das Jenkins-Pipeline-Plugin aufzurufen. agent
definiert, wo die Pipeline ausgeführt werden soll. any
besagt, dass ein beliebiger verfügbarer Agent verwendet werden kann, um die Pipeline oder Stage auszuführen. Ein spezifischerer Agent könnte beispielsweise einen zu verwendenden Container deklarieren:
agent {
docker {
image 'maven:3-alpine'
label 'my-defined-label'
args '-v /tmp:/tmp'
}
}
stages
enthalten eine Sequenz von einer oder mehreren Stage Directives. Im obigen Beispiel sind die drei Phasen Build, Test und Deploy.
steps
übernehmen die eigentliche Arbeit: Im obigen Beispiel haben die Schritte lediglich Meldungen ausgegeben. Ein sinnvollerer Build-Schritt könnte wie folgt aussehen:
pipeline {
agent any
stage('Build') {
steps {
sh 'make'
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
}
}
}
}
Hier rufen wir make
von einer Shell aus auf und archivieren dann alle erzeugten JAR-Dateien im Jenkins-Archiv.
Der post-Abschnitt definiert Aktionen, die am Ende einer Pipeline oder Stage ausgeführt werden. In diesem Abschnitt stehen Ihnen eine ganze Reihe von Post-Condition-Blöcken zur Verfügung: always
, changed
, failure
, success
, unstable
und aborted
.
In der untenstehenden Jenkins-Datei wird beispielsweise JUnit immer nach der Testphase ausgeführt, aber nur dann eine E-Mail gesendet, wenn die Pipeline fehlschlägt.
pipeline {
agent any
stages {
stage('Test') {
steps {
sh 'make check'
}
}
}
post {
always {
junit '**/target/*.xml'
}
failure {
mail to: team@example.com, subject: 'The Pipeline failed :('
}
}
}
Eine deklarative Pipeline kann so gut wie alles ausdrücken, was Sie benötigen, um eine Pipeline zu definieren. Dabei ist sie wesentlich einfacher zu erlernen als die Syntax der skriptbasierten Pipeline, die eine Groovy-basierte DSL ist beziehungsweise eine vollwertige Programmierumgebung. Zum Vergleich: Die beiden folgenden Jenkinsfiles sind äquivalent.
Deklarative Pipeline
pipeline {
agent { docker 'node:6.3' }
stages {
steps {
sh 'npm -version'
}
}
}
}
Scripted Pipeline
node('docker') {
checkout scm
stage('Build') {
docker.image('node:6.3').inside {
sh 'npm -version'
}
}
}
Die Jenkins GUI
Wenn Sie Wert auf ein aktuelles und qualitativ höochwertiges Jenkins-User-Interface legen, sollten Sie das Blue-Ocean-Plugin verwenden, das eine grafische Benutzeroberfläche bietet. Das können Sie einfach zu Ihrer bestehenden Jenkins-Installation hinzufügen oder einen entsprechenden Docker-Container ausführen.
Anschließend können Sie Blue Ocean direkt aufrufen: Es befindet sich im /blue
-Ordner auf dem Jenkins-Server. Pipelines mit Blue Ocean zu erstellen ist ein - im Vergleich zum "normalen" Jenkins - eher grafischer Prozess. (fm)
Dieser Beitrag basiert auf einem Artikel unserer US-Schwesterpublikation Infoworld.