In diesem Groovy-Tutorial für Jenkins erfahrt ihr, wie ihr mit Apache-Groovy-Skripten eine Jenkins-Pipeline erstellt. Groovy eignet sich für Einsteiger und ist eine gute Wahl, um die Skripte verschiedener Teams zu vereinheitlichen.
Sandeep Kinayath
Sandy works as a consultant at Eficode Denmark. He’s passionate about cloud computing, IoT, and hardware integrations. While also having a penchant for football and cricket!
Aber was ist Groovy? Apache Groovy ist eine objektorientierte Programmiersprache für die JVM-Plattform. Diese dynamische Sprache bietet viele Features, die von Python, Smalltalk und Ruby inspiriert sind. Mit ihr könnt ihr eure Pipeline in Jenkins orchestrieren und verschiedene Sprachen verbinden. So können Teams in eurem Projekt in unterschiedlichen Sprachen beitragen.
Groovy lässt sich nahtlos mit Java verbinden, und die Syntax von Java und Groovy ist sehr ähnlich. Wenn ihr beim Schreiben von Groovy die Syntax vergesst, könnt ihr einfach direkt in Java-Syntax weiterschreiben. Groovy kann auch als eine der Skriptsprachen für die Java-Plattform verwendet werden. Groovy-Skripte lassen sich aus Java heraus aufrufen, was den Aufwand bei der Java-Entwicklung effektiv reduziert.
Zudem bietet Groovy zahlreiche produktivitätssteigernde Features wie DSL-Unterstützung, Closures und dynamische Typisierung. Anders als einige andere Sprachen dient es als Ergänzung zu Java, nicht als Ersatz. Groovy-Quellcode wird in JVM-Bytecode kompiliert und kann daher auf jeder Plattform ausgeführt werden.
Warum Groovy verwenden?
Hier sind die wichtigsten Gründe, warum ihr Groovy als gängige Option zum Erstellen von Pipeline-Dateien in Betracht ziehen solltet.
1. Wie bereits erwähnt, ist Groovy eine agile und dynamische Sprache. Sie lässt sich nahtlos in alle vorhandenen Java-Objekte und -Bibliotheken integrieren.
Groovy ist Java ohne Typen, also ohne die Definition grundlegender Datentypen wie int, float, String usw. Im Grunde ist es wie bei Java: Variablen definieren, ohne einen Typ anzugeben. Groovy bestimmt den Typ anhand des Werts des Objekts.
2. In Groovy sind „for“-Schleifen kompakter und leichter lesbar. Groovy-Schleifen unterstützen while, for, for-in, break und continue und sind insgesamt mit Java konsistent.
Zum Beispiel:
0..5 bedeutet, dass die ganzen Zahlen 0, 1, 2, 3, 4 und 5 enthalten sind.
0..<5 bedeutet 0, 1, 2, 3, 5 und a..d bedeutet a, b, c, d:
Groovy unterstützt außerdem Standardwerte für Parameter:
3. In Groovy können wir Bereiche verwenden, um Collections oder Arrays zu definieren.
Auch die Suchmethode in Groovy ist flexibler.
Groovy ermöglicht es außerdem, Collections zu Collections hinzuzufügen oder aus ihnen zu entfernen.
Um Elemente zu einer Collection hinzuzufügen, können wir die folgenden Methoden verwenden:
4. Ein weiterer Vorteil von Groovy sind seine Operatoren:
Die arithmetischen, logischen, relationalen und bitweisen Operatoren von Groovy sind mit Sprachen wie nodeJS konsistent. Groovys == entspricht den equals-Methoden in Java.
5. Ein weiterer Vorteil sind die in Groovy verfügbaren List-Methoden.
Add( ) Fügt den neuen Wert am Ende dieser Liste an.
Get( ) Gibt das Element an der angegebenen Position in dieser Liste zurück.
Contains( ) Gibt true zurück, wenn diese Liste den angegebenen Wert enthält.
Minus( ) Erstellt eine neue Liste mit den ursprünglichen Elementen, aus der das angegebene Element entfernt wurde.
Plus ( ) Erstellt eine neue Liste aus den Elementen der ursprünglichen Liste und den angegebenen Elementen.
Pop( ) Entfernt das letzte Element aus dieser Liste.
Remove( ) Entfernt Elemente an der angegebenen Position in der Liste
Reverse() Erstellt eine neue Liste in umgekehrter Reihenfolge der Elemente der ursprünglichen Liste
Size( ) Gibt die Anzahl der Elemente in dieser Liste zurück.
Sort( ) Gibt eine sortierte Kopie der ursprünglichen Liste zurück.
6. In jeder anderen objektorientierten Sprache gibt es die Konzepte von Objekten und Klassen, um den objektorientierten Charakter einer Programmiersprache darzustellen. Groovy erstellt implizit Getter, Setter-Methoden und stellt Konstruktoren mit Argumenten bereit.
7. Die Exception-Behandlung funktioniert wie in Java: In Groovy werden Exceptions mit try und catch abgefangen.
Groovy bietet viele fortgeschrittene Themen (JSON-Operationen, Methoden, Maps usw.), die in der offiziellen Apache-Dokumentation nachgelesen werden können.
Groovy kombiniert außerdem Funktionen von Python, Ruby und anderen Skriptsprachen. Die Sprache bietet viel syntaktischen Zucker. In der Java-Entwicklung muss viel Code geschrieben werden, der in Groovy – wie die vorherigen Beispiele zeigen – entfallen kann.
Zusammenfassend: Der wichtigste Vorteil von Groovy ist, dass ihr dieselbe Funktion mit weniger Code schreiben könnt. Das bedeutet aus meiner Sicht prägnanteren und aussagekräftigeren Code als in Java.
Welche Nachteile hat die Verwendung von Groovy?
Manche würden sagen, dass eine einfachere Infrastructure as Code mit deklarativen Dateien – die Groovy möglicherweise nicht abbildet – langfristig die bessere Wahl ist, wenn ihr DevOps verfolgt. Außerdem kann Apache Groovy auf den ersten Blick wie eine etwas improvisierte Sprache wirken.
In der Praxis habe ich jedoch erlebt, dass die Einführung von Groovy Teams in Unternehmen zusammengebracht hat, die DevOps zuvor nicht ausdrücklich verfolgt hatten. Sie konnten dadurch unabhängig arbeiten und gleichzeitig mehr gemeinsame Bibliotheken nutzen.
Für eine ausführlichere Einführung in Groovy findet ihr alles Wichtige in der Dokumentation.
Jenkins Pipeline im Jenkinsfile
Sehen wir uns den Jenkins-Pipeline-Ansatz in Jenkins und die richtige Pipeline-Syntax an. Mit der integrierten Groovy-Unterstützung von Jenkins und einer umfassenden Referenzbibliothek für Pipeline-Schritte könnt ihr einen komplexen Build- und Release-Prozess durch eigene Pipeline-Skripte implementieren.
Eine Jenkins Pipeline kann auf folgende Arten erstellt werden:
Über die klassische UI/Blue Ocean: Ihr könnt die grundlegende Pipeline direkt über die klassische Jenkins-UI eingeben oder die UI von Blue Ocean verwenden. In der Blue Ocean-UI könnt ihr das Jenkinsfile der Pipeline schreiben und in die Versionsverwaltung einchecken \weitere Informationen zu Blue Ocean findet ihr hier]
In SCM: Das Jenkinsfile kann manuell geschrieben und in das Source-Control-Repository des Projekts eingecheckt werden.
Statt Freestyle-Jobs zu erstellen und zu konfigurieren, enthält eine Jenkins Pipeline Anweisungen, die der Jenkins-Job ausführt.
Ein über die klassische UI erstelltes Jenkinsfile wird direkt von Jenkins gespeichert. Eine über die klassische Jenkins-UI erstellte Pipeline wird im Root-Verzeichnis von Jenkins gespeichert, und das Skript wird in der Jenkins-Skriptkonsole ausgeführt. Das Jenkinsfile ist der Basiscode für Jenkins, der es in der Jenkins-Skriptkonsole als Groovy-Skript ausführt.
Aufbau eines Jenkinsfiles
Die erste Zeile, der Shebang, definiert die Datei als Groovy-Skript:
Inline-Pipeline-Dateien enthalten keinen Shebang, da dieser intern bereitgestellt wird.
Das oben beschriebene Beispiel für eine deklarative Pipeline kann auch als Code für eine Scripted Pipeline geschrieben werden.
Eine Scripted Pipeline ist eine Groovy-basierte DSL. Sie bietet Jenkins-Nutzern mehr Flexibilität und Skalierbarkeit als die deklarative Pipeline.
Groovy-Skripte eignen sich nicht unbedingt für alle Benutzer. Daher hat Jenkins die Declarative Pipeline entwickelt. Die Syntax der Declarative Pipeline ist strenger. Sie muss die vordefinierte DSL-Struktur von Jenkins verwenden, die eine einfachere und klarere Syntax zum Schreiben von Jenkins-Pipelines bietet.
Hier wird festgelegt, auf welchem Rechner der Code ausgeführt wird.
oder
Der Abschnitt muss auf der obersten Ebene innerhalb des Pipeline-Blocks definiert werden, die Verwendung auf Stage-Ebene ist jedoch optional.
Zur Einordnung: Jenkins unterscheidet zwischen Master- und Worker-Nodes, also Slaves.
Warum benötigen wir mehrere Nodes?
1. Um die Job-Last in Jenkins zu verteilen
2. Wir haben mehrere Nodes für unterschiedliche Projektarten
Nehmen wir beispielsweise an, wir haben zwei verschiedene Projektarten:
Java
iOS
Für das Erstellen eines Java-Jobs benötigen wir eine Java Build Machine bzw. einen entsprechenden Node.
Für einen iOS-Build benötigen wir entsprechend einen MAC Slave, einen Mac oder einen Windows-Rechner.
Stage
Unser Build besteht aus mehreren Stages, wobei jede Stage unterschiedliche Schritte und Befehle umfasst. Der Großteil der Arbeit einer Jenkins-Pipeline wird in einer Abfolge von einer oder mehreren Stages ausgeführt. Es wird empfohlen, dass die Pipeline mindestens eine Stage-Direktive enthält, um verschiedene Delivery-Prozesse wie Build, Test und Deployment zu verbinden.
Parallele Stages
Stages in einer Pipeline können innerhalb eines Parallel-Blocks mehrere verschachtelte Stages deklarieren. So lässt sich die Effizienz bei zeitaufwendigen Stages steigern, die nicht voneinander abhängig sind. Außerdem können mehrere Schritte innerhalb eines einzelnen Parallel-Blocks parallel ausgeführt werden.
Schritte
Der zentrale und grundlegende Bestandteil einer Pipeline ist ein Schritt. Pipelines bestehen aus mehreren Schritten, mit denen ihr Anwendungen bauen, testen und deployen könnt. Schritte sind die grundlegendsten Bausteine der Syntax für Declarative Pipelines und Scripted Pipelines und teilen Jenkins mit, was zu tun ist.
Die Scripted Pipeline beschreibt Schritte nicht ausdrücklich als Teil ihrer Syntax. Im Referenzdokument zu Pipeline-Schritten werden jedoch die Schritte der Pipeline und ihrer Plugins detailliert erläutert.
Mit einer Jenkins-Pipeline könnt ihr mehrere Schritte zusammenstellen und so unterschiedlichste Automatisierungsprozesse erstellen. Stellt euch einen Schritt als einzelnen Befehl vor, der eine Aktion ausführt. Wird ein Schritt erfolgreich abgeschlossen, folgt der nächste. Kann ein Schritt nicht korrekt ausgeführt werden, schlägt die Pipeline fehl.
Wenn alle Schritte der Pipeline erfolgreich abgeschlossen wurden, gilt die Pipeline als erfolgreich ausgeführt.
Timeouts und Wiederholungsversuche
Trigger
Die triggers-Direktive definiert, wie eine Pipeline automatisiert ausgelöst wird. Laut Jenkins-Dokumentation sind derzeit cron, pollSCM und upstream als Trigger verfügbar.
Cron
Akzeptiert einen Cron-ähnlichen String, um das regelmäßige Intervall für die Auslösung der Pipeline zu definieren, zum Beispiel:
pollSCM
Akzeptiert einen Cron-ähnlichen String, um das regelmäßige Intervall zu definieren, in dem Jenkins auf Änderungen im SCM prüft. Gibt es neue Änderungen, wird die Pipeline erneut ausgelöst.
When
Mit dem Befehl when kann die Pipeline anhand der angegebenen Bedingungen bestimmen, ob diese Phase ausgeführt wird. Der Befehl when muss mindestens eine Bedingung enthalten. Komplexere bedingte Strukturen für Pipelines lassen sich mit verschachtelten Bedingungen erstellen: not, allOf oder anyOf. Enthält die Direktive when mehrere Bedingungen, müssen alle Unterbedingungen true zurückgeben, damit die Stage ausgeführt wird. Bedingungen können beliebig tief verschachtelt werden.
Wird meist verwendet, um einen Schritt oder eine Stage zu überspringen oder auszuführen
Post-Build-Abschnitte
Definiert die Aktion am Ende der Pipeline- oder Stage-Ausführung. Möglicherweise müsst ihr Bereinigungsschritte ausführen oder abhängig vom Ergebnis der Pipeline bestimmte Aktionen durchführen. Der Post-Condition-Block unterstützt folgende Komponenten: always, changed, failure, success, unstable und aborted. Mit diesen Blöcken können Schritte abhängig vom Status der Pipeline am Ende der Pipeline- oder Stage-Ausführung ausgeführt werden.
Umgebungsvariablen
Umgebungsvariablen können wie im folgenden Beispiel global oder pro Stage festgelegt werden. Wie erwartet gelten Umgebungsvariablen, die pro Stage festgelegt werden, nur für die Stage, in der sie definiert sind.
Exception Handling in Jenkins Pipeline
Für das Exception Handling könnt ihr jetzt den try-catch-Block verwenden, um einen Schritt auszuführen und Exceptions abzufangen.
Tests und Artefakte erfassen
Jenkins kann Testergebnisse erfassen und aggregieren, sofern euer Test Runner Dateien mit Testergebnissen ausgeben kann. Jenkins enthält in der Regel den JUnit-Schritt. Können eure Test-Tools jedoch keine XML-Reports im JUnit-Stil ausgeben, gibt es zusätzliche Plugins, die nahezu jedes gängige Format für Test-Reports verarbeiten.
Um unsere Testergebnisse und Artefakte zu erfassen, verwenden wir den Post-Abschnitt.
Benachrichtigungen senden
Wenn die Pipeline erfolgreich ist oder fehlschlägt, muss eine Benachrichtigung über den aktuellen Status an das zuständige Team gesendet werden. Wir können der Pipeline Benachrichtigungs- oder andere Schritte hinzufügen, um Abschluss-, Benachrichtigungs- oder andere Aufgaben am Ende der Pipeline auszuführen.
E-Mail-Benachrichtigung
Slack-Benachrichtigung
Jenkins Pipeline Groovy: Shared Libraries
Wenn die Pipeline in immer mehr Projekten einer Organisation eingesetzt wird, entstehen wahrscheinlich gemeinsame Muster. Oft ist es sinnvoll, Teile von Pipelines zwischen verschiedenen Projekten zu teilen, um Redundanzen zu reduzieren.
Pipelines unterstützen die Erstellung von Shared Libraries, die in externen Source-Control-Repositories definiert und in bestehende Pipelines geladen werden können.
Eine Shared Library wird mit einem Namen, einer Methode zum Abrufen des Source Codes, beispielsweise über SCM, und optional einer Standardversion definiert. Der Name sollte eine kurze Kennung sein, da er in Skripten verwendet wird.
Betrachtet die folgende Ordnerstruktur: (direkt aus der Jenkins-Dokumentation)
Das Verzeichnis src sollte der üblichen Java-Quellverzeichnisstruktur entsprechen. Dieses Verzeichnis wird beim Ausführen von Pipelines zum Classpath hinzugefügt.
Das Verzeichnis vars enthält Skriptdateien, die in Pipelines als Variablen verfügbar sind. Der Dateiname entspricht dem Namen der Variablen in der Pipeline. Eine ausführlichere Anleitung zum Definieren einer Shared Library in Jenkins findet ihr hier. Es gibt weitere Optionen und verfügbare Snippets, die wir mit einem Jenkinsfile im Jenkins-System erstellen und ausführen können.
Im Verzeichnis resources können alle für die Pipeline benötigten Nicht-Groovy-Dateien und Hilfsskripte verwaltet werden. In der obigen Struktur wird die Beispiel-JSON-Vorlage im Ressourcenverzeichnis gespeichert und kann in der Shared Library mit der Funktion libraryResource aufgerufen werden.
Ihr könnt die erstellte Library wie folgt verwenden oder aufrufen:
Beispiel zum Schreiben von Funktionen in der Groovy Shared Library
Globale Variablen einer Shared Library definieren
Das Verzeichnis vars enthält Skripte, die globale Variablen definieren, auf die über die Pipeline zugegriffen werden kann.
Intern werden Skripte im Verzeichnis vars bei Bedarf als Singletons instanziiert. So können mehrere Methoden bequem in einer einzelnen .Groovy-Datei definiert werden.
Auf Steps zugreifen
Groovy-Klassen in einer Shared Library können Steps wie sh oder git nicht direkt aufrufen. Stattdessen können wir einer Klassenmethode explizit die spezifischen globalen Variablen env (enthält alle aktuellen Umgebungsvariablen) und steps (enthält alle Standard-Pipeline-Steps) übergeben. Sie können jedoch Methoden außerhalb des Geltungsbereichs einer umschließenden Klasse implementieren, die wiederum Pipeline-Steps aufrufen.
Zum Beispiel:
Diese kann dann über eine Scripted Pipeline aufgerufen werden:
Die vollständige Dateistruktur eines Jenkins-Groovy-Skriptbeispiels sieht wie folgt aus:
Weitere Jenkins-Groovy-Snippets findet ihr im GitHub-Repository von Dennyzhang.
Ich hoffe, das hilft euch auf eurer Reise mit Groovy!
- DevOps
- CI/CD
Subscribe to our newsletter
Related blogs