Über unsMediaKontaktImpressum
Moritz Reinwald 21. Januar 2020

Continuous Delivery mit Jenkins im Oracle-Datenbankumfeld

Jenkins ist der weltweit am meisten genutzte Open-Source-Automatisierungsserver mit unzähligen Plugins für eine Großzahl an Technologien und Einsatzszenarien rund um Continuous Integration und Continuous Delivery [1]. Dieser Artikel stellt verschiedene Möglichkeiten und Werkzeuge vor, mit denen der Jenkins-Server im Oracle-Datenbankumfeld dazu genutzt werden kann, Continuous Delivery in Form von Pipelines umzusetzen.

Continuous Delivery ist eines der Schlagworte, die überall genannt werden, aber oftmals gar nicht richtig interpretiert werden können. Was ist z. B. der Unterschied zwischen Continuous Integration, Continuous Delivery und Continuous Deployment? Und wie passt DevOps dazu? Die Unterschiede zeigen sich deutlich, wenn die einzelnen Schritte des Software-Lebenszyklus betrachtet werden.

Nun ist auch ersichtlich, dass Continuous Delivery kein Synonym von Continuous Deployment ist, auch wenn beide häufig mit CD abgekürzt werden. Ein Release in eine Staging-Area – also Testumgebung – einzuspielen und danach manuell auszuliefern oder ein Release direkt in das Produktivsystem des Kunden einzuspielen ist ein großer Unterschied.

Oracle APEX

Wer des Öfteren im Oracle-Datenbankumfeld unterwegs ist, der hat womöglich schon von Oracle Application Express (kurz Oracle APEX) gehört. APEX ist eine Low-Code-Entwicklungsplattform für Webanwendungen, welche auf der Oracle-Datenbank aufsetzt und kostenlos verfügbar ist. Zusammengefasst ermöglicht APEX, Webapplikationen assistentengestützt durch eine deklarative Art auf Basis der Oracle-Datenbank zu erstellen. Dabei werden die Anwendungen innerhalb der Datenbank nur als Metadaten gespeichert. Beim Aufruf werden dann aus diesen Metadaten die HTML-Seiten generiert. Die Entwicklung und Verwaltung erfolgt hierbei ausschließlich über einen Webbrowser. Mehr Informationen gibt es auf der offiziellen Internetseite [2].

Jenkins

Als Fork des ehemals von Oracle entwickelten Hudson ist Jenkins mittlerweile der führende Open-Source-Automatisierungsserver und kostenlos verfügbar. Jenkins basiert auf Java und wird von einem Team um den Hauptentwickler Kohsuke Kawaguchi weiterentwickelt und verbessert. Es existiert auch eine Enterprise-Version welche von Cloudbees betreut wird. Durch eine große Anzahl an Plugins kann Jenkins technologieübergreifend eingesetzt werden.

In Verbindung mit der Oracle-Datenbank kann Jenkins verschiedene Aufgaben abwickeln. Dazu gehören:

  • Ex-/Importieren von APEX Anwendungen,
  • Ex-/Importierten von Datenbankobjekten,
  • Sichern/Wiederherstellen von Schemata über die Datapump-Funktionalität der Datenbank,
  • Ausführen von Front- und Backendtests,
  • Generieren von Patchdateien,
  • Ausliefern von Skripten auf verschiedenen Datenbanken,
  • Erstellen von Benutzern, Berechtigungen, etc. und
  • Ausliefern von Apps über Pipelines.

Jenkins muss hier als zusätzliche Automatisierungsschicht über der Datenbank betrachtet werden. Über Jenkins werden Prozesse, welche dann auf die Datenbank zugreifen, automatisiert angestoßen. Für die Kommunikation mit der Datenbank wird die CLI-Schnittstelle der Datenbank, SQL*Plus, genutzt. Seit einiger Zeit kann hier auch der von Oracle veröffentlichte, inoffizielle Nachfolger SQLcl genutzt werden.

Jenkins-Build-Jobs

Ist ein Jenkins-Server eingerichtet und aktiv, können Aufgaben in sogenannten Jenkins-Build-Jobs definiert werden. Hier bietet Jenkins verschiedene Arten von Jobs an, welche durch Plugins noch ergänzt werden können. Im Normalfall reicht der klassische "Freestyle-Job".

Diese Jobs können einzeln über die Oberfläche konfiguriert und manuell oder automatisiert, z. B. zeitgesteuert oder bei Änderungen in der Versionsverwaltung, gestartet werden. Ein Job besteht dabei aus einem oder mehreren Build Steps. Diese können z. B. der Aufruf eines Skripts oder das Committen von Änderungen ins Versionskontrollsystem sein. Wird ein Job gestartet, erzeugt er einen Build. Hier werden alle zu diesem Durchlauf relevanten Informationen, Dateien, etc. gebündelt.

Im Normalfall übernimmt ein Job eine einzelne Aufgabe, wie z. B. das Exportieren einer Anwendung. Pro Projekt werden daher mehrere Jobs benötigt. Hier gibt es mehrere Möglichkeiten diese Jobs zu erstellen. Zum einen können Parameter genutzt werden. Damit kann ein Job für verschiedene Umgebungen etc. genutzt werden. Für eine automatische Ausführung dieser Jobs sind Parameter allerdings eher hinderlich. Ein fest definierter Job passt hierfür immer besser, da es keine Variablen gibt, die von Durchlauf zu Durchlauf anders sein können. Hier muss also abgewogen werden, welche Art für den jeweiligen Anwendungsfall besser geeignet ist.

Nützliche Plugins für Jobs

Es gibt einige nützliche Plugins, welche die Konfiguration der Jobs erweitern und deshalb hier nicht unerwähnt bleiben sollten.

Name Anwendung Erklärung
Jenkins Text Finder Error Handling Nach Texten suchen z. B. ERROR oder ORA- und das Ergebnis des Builds verändern.
Flexible Publish Post-Build-Step Andere Post-Build-Steps (z. B. Text Finder) mehrmals und nur unter bestimmten Bedingungen ausführen.
Build Name Setter Plugin Übersichtlichkeit Dem Build zur Laufzeit einen Namen statt der standardmäßigen Nummer geben.
Office365 Connector Benachrichtigungen Benachrichtigungen an ein beliebiges Office365-Produkt senden (z.B. Microsoft Teams).
Parameterized Trigger Plugin Auslösen von Jobs Andere Jobs mit bestimmten Parametern (oder den eigenen Parametern) anstoßen.

Wichtige Punkte in der Job-Konfiguration

Innerhalb der Konfiguration eines Jenkins-Jobs gibt es einige wichtige Punkte, welche aber nicht direkt als solche erkennbar sind.

Hierzu zählt die Eigenschaft, einen selbst definierten Arbeitsbereich zu setzen. Diese Option befindet sich im General-Tab der Konfiguration unter dem Erweitert-Button und ist eigentlich unabdingbar. Im Standard hat jeder Job einen eigenen Workspace unterhalb des Jobordners. Wenn nun zehn Jobs eines Projektes mit dem selben Repository des Versionskontrollsystems arbeiten, wird dieses Repository in jedem Arbeitsbereich jeweils einmal – dementsprechend insgesamt zehn Mal – ausgecheckt. Das ist speicherplatz- und performancemäßig nicht sinnvoll. Besser ist ein zentraler Ordner pro Projekt, in dem das Repository ausgecheckt ist und in welchem dann jeder Job arbeitet. Die Zuordnung des Workspaces innerhalb des Jobs erfolgt hierbei am besten mit einer Variable.

Variablen sind ein weiterer wichtiger Punkt bei der Konfiguration eines Jobs. Diese lassen sich in den Einstellungen des Jenkins systemweit konfigurieren. Innerhalb der Jobs können diese dann mit $variablenname verwendet werden. Variablen sollten vor allem für Pfade, Repository URLs, Verbindungsdaten zur Datenbank, etc. aber nicht für Passwörter genutzt werden.

Für Passwörter stellt Jenkins eine bessere Möglichkeit bereit. Das Credentials-Plugin ist ein Teil des Jenkins, welches standardmäßig bei jeder Installation automatisch mitinstalliert wird. Hier können verschiedene Arten von Passwörtern sicher hinterlegt werden. In den Jobs können diese hinterlegten Passwörter dann zur Laufzeit mithilfe von Bindings unter der Rubrik Buildumgebung mit dem Punkt Use secret text(s) or file(s) an Variablen gebunden und genutzt werden.

Um Jobs automatisiert zu starten gibt es mehrere Möglichkeiten. Eine einfache Möglichkeit ist eine reine Zeitschaltung mittels der CRON-Syntax. Zudem kann der Job auch gestartet werden, sobald in das Repository committet wurde. Hierfür muss zusätzlich in Git/Subversion/etc. ein Webhook erstellt werden, welcher eine Benachrichtigung an den Jenkins-Server schickt. Die Konfiguration innerhalb des Jobs erfolgt hierbei über die Rubrik Buildauslöser.

Ablauf eines Jobs

Alle Jobs sollten einem gewissen Ablauf folgen. Hierzu zählt, vor dem eigentlich Build Step einmal das Versionskontrollsystem nach Änderungen abzufragen, den Workspace zu setzen und die Umgebung d. h. Passwörter, Variablen, etc. vorzubereiten bzw. zu definieren. Zudem sollten nach der Ausführung des Build Steps eventuelle Änderungen in das Versionskontrollsystem committet werden und der Build sollte auf Fehler überprüft werden. Hier bietet es sich bei Oracle an, nach den bekannten ORA- oder SP2-Fehlercodes zu suchen und dann das Ergebnis des Builds nachträglich zu verändern.

Die einzelnen Jobs

Die eigentliche Logik der einzelnen Jobs lässt sich jeweils auf ein bis zwei Schritte herunterbrechen. Da fast alle Jobs auf der Datenbank arbeiten müssen, werden im Jenkins nur Windows-Batch- bzw. Linux-Shell-Skripte aufgerufen. Diese nutzen dann wiederum SQL*Plus oder SQLcl um Befehle auf der Datenbank auszuführen. Durch die Arbeit mit der Datenbank sind die Skripte – bis auf wenige Unterschiede durch die unterschiedlichen Sprachen – identisch für Windows und Linux.

Eine Ausnahme bietet hier der Job zum Export einer APEX-Anwendung. Da die Entwickler von APEX ein Werkzeug zum Export der Anwendung mittels Java bereitstellen, kann hier auf eine Verbindung via SQL*Plus oder SQLcl verzichtet werden.

Für den Import einer Anwendung kann die vorher exportierte .sql-Datei nach dem Setzen einiger Parameter einfach aufgerufen werden. Dies kann beispielsweise über ein SQL-Skript umgesetzt werden, welches die nötigen Parameter, Workspace ID, App ID, etc., setzt und danach die eigentliche .sql-Datei aufruft. Da dieses Skript wiederum auf der Datenbank läuft, muss eine Batch/Shell-Datei eine Verbindung zur Datenbank herstellen und das Skript ausführen.

Das Batch/Shell-Skript welches eine Datenbankverbindung herstellt und eine SQL-Datei auf der Datenbank ausführt, kann für mehrere Jobs wiederverwendet werden. Es ist z. B. ebenfalls für den Export und Import der zu einer APEX-Anwendung gehörenden Datenbankobjekte nötig.

Für den Export kann mithilfe des SPOOL-Befehls von SQL*Plus und der GET_DDL-Funktion der Oracle-Datenbank ein SQL-Skript geschrieben werden, welches die DDL der Objekte selektiert und per SPOOL in eine Datei schreibt. Dies lässt sich auch so konfigurieren, dass jedes Objekt in einer eigenen Datei liegt. Für den Import können diese Dateien dann wiederum aufgerufen werden.

Der letzte große Punkt ist der Ex-/Import eines gesamten Schemas inklusive Daten per Datapump. Da Datapump ein Werkzeug von Oracle ist, welches über die Konsole aufgerufen wird, kommt hier wieder ein Batch/Shell-Skript zum Einsatz. Hier muss lediglich der Befehl expdp/impdp mit einigen Parametern hinterlegt werden.

Sind weitere Skripte nötig, wie z. B. alle Datenbankobjekte auf eine Umgebung zu löschen um dann neue Objekte zu importieren, können diese wiederum über ein Batch/Shell-Skript auf der Datenbank ausgeführt werden. Dieses Skript kann dann mithilfe eines Jenkins-Jobs gestartet werden.

Probleme der Jobs

Über die Zeit kann aus wenigen Jobs relativ schnell eine große und unübersichtliche Menge an Jobs heranwachsen, welche sich nur schwer und mit viel Zeitaufwand pflegen lässt. Soll dann z. B. das Versionierungssystem von Subversion auf Git umgestellt werden, muss jeder Job einzeln abgeändert bzw. neu konfiguriert werden. Das kostet Zeit und Nerven. Da Jenkins die Jobs intern als XML-Dateien speichert, ist es durchaus möglich, die Änderungen dort vorzunehmen. Allerdings ist dies sehr kompliziert und oftmals fehlerbehaftet, da dort keinerlei Validierung der vorgenommenen Änderung stattfindet. Damit ist die Änderung der XML-Dateien keine sinnvolle Alternative. Für die Bearbeitung von mehreren Jobs gibt es zudem Plugins aus der Jenkins-Community, welche mehr oder weniger gut funktionieren. Das generelle Ziel sollte hier allerdings nicht sein, Werkzeuge zu finden, die bei der aufwändigen Änderungsmethode von Jenkins-Jobs unterstützen, sondern eine Methode zu finden, welche es ermöglicht, Jenkins-Jobs einfach zu erstellen und später auch zu pflegen.

Ein weiterer Punkt, welcher gegen eine Vielzahl von Jenkins-Jobs spricht, ist die Dezentralisierung. Verteilt sich ein Prozess auf verschiedene Jobs, können diese zwar verkettet und somit automatisch hintereinander aufgerufen werden, allerdings kann dies auch Risiken mit sich bringen. Ein Nutzer kann eine Verkettung nicht direkt sehen, da er dazu in die Konfiguration der einzelnen Jobs schauen müsste. Führt dieser Nutzer zum falschen Zeitpunkt unwissentlich eine ganze Reihe an Jobs, anstatt eines einzelnen Jobs, aus, kann es schnell zu Problemen kommen. Zudem ist es schwierig, ein Reporting über mehrere Jobs zu realisieren, um den Fortschritt und die jeweiligen Ergebnisse aller Jobs auf einen Blick sehen zu können. Hier liegen die Informationen alle verteilt in jedem einzelnen Jenkins-Job.

Jenkins stammt ursprünglich aus einer Zeit, in welcher Programme ausschließlich über die Benutzeroberfläche gesteuert wurden. Damals boten viele Programme keinerlei Optionen, erstellte Neuerungen als Code zu speichern, um diesen dann zu versionieren. Auch die Jenkins-Build-Jobs verlassen sich daher zu viel auf die Benutzeroberfläche, d. h. sie sind dazu designt, deklarativ über die Oberfläche erstellt zu werden. Damit Jenkins-Build-Jobs versioniert werden können, müssen viele verschiedene XML-Dateien in das Versionierungssystem eingespielt werden. Zusätzlich zur Versionierung spielt hier auch Dokumentation eine Rolle. Da die Jenkins-Build-Jobs über eine Oberfläche erstellt werden, lassen sich alle Änderungen und Konfigurationen nicht einfach dokumentieren, stattdessen müssen alle in der UI vorgenommenen Änderungen detailgetreu aufgeschrieben oder Screenshots angefügt werden. Hier zeigt sich auch das Problem der Reproduzierbarkeit: Wird ein kleines Detail vergessen, kann es vorkommen, dass zwei fast identische Jenkins-Build-Jobs komplett unterschiedliche Ergebnisse erzielen.

Zusammengefasst haben Jenkins-Build-Jobs mit umfangreicheren Projekten einige Probleme, sei es die reine Anzahl der einzelnen Jobs, die Unübersichtlichkeit und Dezentralisierung von Informationen, ungewollte Risiken und die nicht triviale Versionierungsmöglichkeit. Eben diese Hürden gilt es zu überwinden um Continuous-Delivery-Pipelines mit Jenkins umzusetzen. Genau für dieses Problem gibt es seit Frühjahr 2016 eine offizielle Lösung vom Jenkins-Entwicklerteam rund um Kohsuke Kawaguchi.

Jenkins-Pipelines

Das Pipeline-Plugin [3] oder besser die Pipeline-Plugin-Suite, da das Pipeline-Feature auf mehrere Plugins aufgeteilt ist, wird bei einer normalen Installation des Jenkins-Servers automatisch mit installiert. Diese Plugins erweitern den Jenkins-Server um die Funktionalität, Pipelines auszuführen. Pipelines sind Automatisierungsketten, welche, im Falle einer Continuous-Delivery-Pipeline beispielsweise alle Schritte zum Release einer Software abarbeiten. Nach einer einfachen Installation der Plugins auf dem Jenkins-Server kann bereits mit den Pipelines gearbeitet werden, in den meisten Fällen sogar ohne einen Neustart.

Im Gegensatz zu den Build-Jobs wird bei einem Pipeline-Job die gesamte Logik in einem Skript, dem sogenannten Jenkinsfile, hinterlegt. Dieser Jenkinsfile sollte auch in das Versionierungssystem eingepflegt werden. Aktuell gibt es zwei Versionen von Jenkins-Pipelines. Die erste, initiale Variante heißt Scripted Pipeline. Hierbei wird der Jenkinsfile, auf welchem die Pipeline aufbaut, noch größtenteils in Groovy, einer auf der Java-Plattform basierenden Programmier- und Skriptsprache, geschrieben. Zusätzlich gibt es die zweite – neuere – Möglichkeit, die Pipelines deklarativ zu erstellen. Die sogenannten Declarative Pipelines basieren auf einer DSL (Domain Specific Language), welche auf Groovy aufbaut. Wird eine Pipeline geschrieben, sollte der deklarative Ansatz gewählt werden, da die Scripted Pipeline in die deklarative Variante mit eingebunden werden kann, und somit obsolet ist.

Da Jenkins die Pipelines als weitere Job-Art definiert, können Pipelines auf die gleiche Weise wie die normalen Build-Jobs angelegt werden. Nach einem Klick auf die Schaltfläche Element anlegen kann nun als Job-Art Pipeline Job ausgewählt werden.

Die Oberfläche zur Konfiguration eines Pipeline-Jobs sieht hierbei ähnlich aus, wie die der Freestyle-Jobs. Hier sollte allerdings nicht der Fehler gemacht werden, dass zu viel in der Oberfläche konfiguriert wird. Diese Konfigurationsoptionen stehen ebenfalls im Jenkinsfile zur Verfügung. Wenn mehrere Pipelines auf einem Jenkinsfile aufbauen, muss hier nicht jeder Pipeline-Job einzeln konfiguriert werden, sondern jeder Job zieht sich die notwendige Konfiguration aus dem Jenkinsfile. Einzig die Verbindung zum bevorzugten Versionierungssystem muss in jedem Pipeline-Job in der Oberfläche gesetzt werden.

Da ein Jenkinsfile genutzt wird, findet sich im Pipeline-Job selbst keinerlei Konfiguration wieder. Theoretisch besteht auch die Möglichkeit, die Konfiguration direkt in der Oberfläche des Pipeline-Jobs zu erstellen und nicht in einen Jenkinsfile zu schreiben. Dies wiederspricht allerdings der Idee der Jenkins-Pipelines, da diese Pipeline-Jobs dann genau wie normale Build-Jobs konfiguriert sind und alle Vorteile verloren gehen. Daher sollte ganz unten in der Konfiguration unbedingt die Option, den Jenkinsfile aus der Versionierung zu laden, gewählt werden. Hier stehen alle unterstützten Versionierungssysteme von Jenkins zur Verfügung. Neben Git lassen sich weitere Systeme wie z. B. Subversion und BitBucket über Plugins installieren.

Jenkinsfile

Der Jenkinsfile einer Declarative Pipeline besteht hauptsächlich aus Sektionen und Direktiven. Der Anfang jeder Pipeline ist der pipeline-Block. Alles, was in der Pipeline passiert, muss innerhalb dieses Blockes stehen. Sind selbst geschriebene Groovy-Funktionen vorhanden, können diese auch außerhalb des Blockes definiert werden. Sinnvoller ist es in diesem Fall aber, eine sogenannte Shared Library zu nutzen [4]. Dies ist ein Feature, über welches die Standard-DSL-Sprache um eigene Funktionen erweitert werden kann.

Die grobe Struktur innerhalb des pipeline-Blockes ist durch sogenannte stages definiert. In jeder stage können verschiedene Befehle innerhalb einer steps-Sektion ausgeführt werden. Zudem kann in jeder stage eine post-Sektion eingefügt werden. In dieser können verschiedene Befehle aufgrund des Ergebnisses der steps-Sektion ausgeführt werden. Diese post-Sektion kann ebenfalls am Ende der gesamten stages-Sektion definiert werden.

Als erstes innerhalb des pipeline-Blockes muss mit Hilfe der agent-Sektion ein Exekutor, auf welchem die Pipeline oder einzelne stages ausgeführt werden, gewählt werden. Hier bietet Jenkins verschiedene Möglichkeiten. Mit agent any bestimmt Jenkins den Exekutor, mit agent none wird global kein Exekutor festgelegt, dieser muss dann innerhalb der jeweiligen stages definiert werden. Soll ein bestimmter Exekutor gewählt werden, kann dies über ein label geschehen. Weitere Möglichkeiten sind in der Jenkins-Pipeline-Dokumentation beschrieben [5].

Nach dem der Exekutor definiert wurde, können nun durch weitere Direktiven einige Optionen, Parameter, Trigger und Umgebungsvariablen gesetzt werden. Darauf folgt dann die stages-Direktive. Nachfolgend sind alle häufig genutzten Sektionen und Direktiven aufgelistet, ausführlichere Informationen zu diesen und weiteren Befehlen finden sich ebenfalls in der Dokumentation der Jenkins Pipelines [5].

Name Benötigt Verwendbar in Beschreibung
pipeline Ja (1x) Der Hauptblock
agent Ja pipeline, stage Exekutor für pipeline oder jede stage
stages Ja pipeline (1x) Hauptsektion für einzelne Stufen
stage Ja, mind. 1x stages Direktive für eine Stufe (Name muss angegeben werden)
steps Ja stage Sektion für die einzelnen Befehle
post Nein pipeline, stage Aktionen, je nach Ergebnis der stage/pipeline ausführen
environment Nein pipeline, stage Definiert Umgebungsvariablen
options Nein pipeline (1x), stage (eingeschränkt) Definiert Build-Optionen für die pipeline (für eine stage lassen sich nur bestimmte Optionen aktivieren)
parameters Nein pipeline (1x) Definiert Parameter, welche der User mitgeben soll
triggers Nein pipeline (1x) Definiert automatisierte Starts der pipeline
parallel Nein stage Führt mehrere stages innerhalb einer stage parallel aus
script Nein steps Führt Code einer Scripted Pipeline oder Groovy-Code aus
when Nein stage Bedingte Ausführung der stage
catchError Nein steps Fängt einen Fehler ab (ähnlich zu try/catch)

Um die Pipeline nun mit Funktionalität zu versehen, müssen die gewünschten Befehle in die steps-Sektion geschrieben werden. Hierfür empfiehlt es sich, den durch die Pipeline-Plugins in den Jenkins integrierten Pipeline-Snippet-Generator zu nutzen. Dieser findet sich innerhalb eines Pipeline-Projektes im Menü auf der linken Bildschirmseite. Hier sind alle verfügbaren Befehle für eine Pipeline enthalten und können mit Hilfe der Oberfläche konfiguriert werden. Als Ergebnis liefert der Generator ein Snippet, welches nahtlos in die Pipeline integriert werden kann. Für Plugins, welche Jenkins-Pipelines unterstützen, sind die Befehle ebenfalls im Snippet-Generator verfügbar. Zusätzlich ist auch ein Direktiven-Generator für Declarative Pipelines und viele Referenzen, beispielsweise für einzelne Steps oder globale Variablen, vorhanden.

Zudem gibt es die Möglichkeit, den Jenkinsfile validieren zu lassen, bevor die Pipeline ausgeführt wird, indem der Jenkins Pipeline Linter genutzt wird. Dieser wird auf dem Jenkins-Server ausgeführt und der Jenkinsfile per HTTP-Post-Request übertragen. Je nach Konfiguration müssen hier noch Vorkehrungen getroffen werden, damit der Jenkins-Server die Anfrage erfolgreich erhält, mehr dazu findet sich in der Dokumentation [6].

Plugins in der Pipeline

Da Plugins angepasst werden müssen um auch Jenkins-Pipelines zu unterstützen sind nicht alle verfügbaren Plugins für Jenkins auch in Jenkins-Pipelines nutzbar. Hier sollte vorher ein wenig Recherche betrieben werden. Ein gutes Indiz bietet auch hier wieder der integrierte Pipeline-Snippet-Generator, da dort auch Snippets von allen installierten und kompatiblen Plugins zur Auswahl angeboten werden. Ist das Plugin dort nicht gelistet, unterstützt es zwar nicht die deklarative Art der Jenkins-Pipelines, es besteht allerdings noch die Chance, dass die geskriptete Variante unterstützt wird. Dies lässt sich oftmals über die Suchmaschine des Vertrauens herausfinden. Wenn dem so ist, kann das Plugins über die script-Direktive aufgerufen werden.

Unterstützt das Plugin gar keine Jenkins-Pipelines, besteht noch die Möglichkeit, die Funktionalität selbst in einer Groovy-Funktion umzusetzen und diese wiederum in der script-Direktive aufzurufen. Final kann natürlich auch direkt das Plugin weiterentwickelt werden, sofern der ursprüngliche Entwickler zustimmt.

Pipeline-Besonderheiten und Best Practices

Sollen Informationen des aktuellen Builds des Pipeline-Jobs abgerufen werden, kann dies mit Hilfe der Eigenschaft currentBuild bewerkstelligt werden. Diese enthält Informationen über die ID bzw. Nummer, den Display-Namen, die Beschreibung und das aktuelle Ergebnis, auch zur Laufzeit. Wo bei Freestyle-Jobs ein Plugin genutzt werden muss, um z. B. den Build-Namen zu ändern, kann dies hier einfach mithilfe dieser Eigenschaft erledigt werden.

Zusätzlich ist es möglich, das Ergebnis eines Build-Schrittes oder auch der gesamten Pipeline zu prüfen und manuell das Ergebnis zu setzen, sollte der Jenkins-Server dies einmal nicht automatisch richtig erkennen. Ist das Ergebnis allerdings einmal fehlerhaft (FAILURE), lässt es sich nur noch auf instabil (UNSTABLE) und nicht mehr auf erfolgreich (SUCCESS) ändern. Bricht ein Benutzer die Pipelineausführung ab, gibt es noch den Status abgebrochen (ABORTED). Hier sollte also möglichst vor dem Setzen des Ergebnisses geprüft werden.

Auch Pipeline-Jobs sollten nicht im Standard-Jenkins-Workspace, sondern in einem Custom-Workspace laufen. Im Gegensatz zu den Freestyle-Jobs muss hier darauf geachtet werden, dass der Jenkinsfile zuerst aus der Versionierung ausgecheckt wird und dann erst ausgeführt wird. Da der Custom-Workspace allerdings erst im Jenkinsfile definiert wird, wird der Jenkinsfile selbst immer in den Standard-Workspace ausgecheckt. Damit nur der Jenkinsfile einzeln in diesen Standard-Workspace ausgecheckt wird, sollte bei der Konfiguration die Repository-URL direkt auf diese Datei zeigen und nicht auf das gesamte Repository.

Um im Jenkinsfile den Custom-Workspace zu setzen, muss in der agent-Direktive die label-Direktive "missbraucht" werden. Wird ein leeres label gesetzt, kann noch die Eigenschaft customWorkspace hinzugefügt werden (s. Abb. 10).

Soll eine Pipeline mit Parametern ausgestattet werden, können diese entweder, genau wie bei den Freestyle-Jobs, in der Oberfläche deklarativ hinzugefügt werden oder direkt im Jenkinsfile mit der parameter-Direktive definiert werden. Wird der Weg über den Jenkinsfile gewählt, werden die Parameter nach dem ersten Durchlauf des Jobs in der Oberfläche angezeigt und können ab dann wie gewohnt für jeden neuen Build gesetzt werden. Hier ist also eine Art Testlauf nötig um die Parameter aus dem Jenkinsfile in die Oberfläche zu übertragen.

Ein Jenkinsfile muss nicht zwangsläufig "Jenkinsfile" heißen. Hier kann ein sinnvollerer Name gewählt werden und somit können auch mehrere Jenkinsfiles in einem Verzeichnis gespeichert werden. Zudem kann ein Jenkinsfile auch für mehrere Pipeline-Jobs genutzt werden. Hier können im Jenkinsfile die Werte der Parameter aus der Oberfläche abgerufen werden. Diese Parameter können, zwecks der angestrebten automatisierten Ausführung der Pipeline, auch als feste Parameter in der Oberfläche definiert werden (z. B. ein Auswahl-Parameter mit nur einer vordefinierten Auswahlmöglichkeit). Somit sind die Parameter fix, welches eine automatisierte Ausführung ermöglicht, der Jenkinsfile an sich kann aber in anderen Jobs wiederverwendet werden. Bei dieser Möglichkeit sollten im Jenkinsfile an sich keinerlei Parameter definiert werden. Der Abruf der Parameter erfolgt über das params-Objekt. Mehr dazu gibt es in der offiziellen Dokumentation [7].

Funktioniert eine Pipeline nicht wie gewünscht, gibt es in der Oberfläche des Pipeline-Jobs eine Replay-Funktion. Mit dieser ist es möglich, den Jenkinsfile temporär in der Oberfläche zu ändern, um dann einen erneuten Durchlauf zu starten. Der Jenkinsfile im Hintergrund bleibt dabei unberührt. Somit ist es einfacher, einen Fehler innerhalb des Jenkinsfiles zu finden, ohne diesen jedes Mal committen zu müssen.

Pipeline-Oberfläche

Wurde ein Pipeline-Job angelegt und mindestens einmal ausgeführt, erscheint in der Oberfläche eine Übersicht über die Ausführungsdauer und das Ergebnis der einzelnen Stages. Zudem lassen sich die Logs der jeweiligen Stages mit einem Klick auf die Stage abrufen. Ist die Pipeline erfolgreich durchgelaufen, erscheinen alle Stages grün. Gab es einen Fehler in einer Stage, ist diese und alle nachfolgenden, nicht ausgeführten, Stages dunkelrot eingefärbt. In diesem Fall färben sich auch die zuvor erfolgreich durchgelaufenen Stages hellrot. Oben in der Übersicht stehen die zuvor vergebenen Namen der Stages und eine Anzeige, wie lange die Ausführungsdauer diese Stage durchschnittlich war. Auf der linken Seite werden die verschiedenen Builds, sprich Ausführungen, mit der jeweiligen Build-Nummer, dem Start-Datum und der Anzahl an Commits in die Versionierung seit der letzten Ausführung aufgelistet.

Neben der Standardoberfläche gibt es per Plugin [8] auch noch die vom Jenkins-Entwicklerteam extra komplett neu entwickelte Oberfläche namens Blue Ocean. Sie soll die User Experience von Jenkins deutlich steigern, indem sie folgende Hauptmerkmale mit sich bringt [9]:

  • Unterstützung von anspruchsvollen Visualisierungen von Continuous-Delivery-Pipelines.
  • Intuitives Erstellen von Pipelines in einem visuellen Prozess durch den Pipeline-Editor.
  • Anpassungen an die rollenbasierten Anforderungen jedes Teammitglieds.
  • Präzision bei der Lokalisierung von Problemen innerhalb einer Pipeline.
  • Native Integration von Branches und Pull Requests für GitHub und BitBucket.

Wird Blue Ocean genutzt, kann auf einen Blick gesehen werden, wo die Pipeline fehlerhaft war und welche Stages eventuell trotzdem danach noch ausgeführt wurden. Zudem sieht die Oberfläche deutlich "frischer" aus und ist von Grund auf für Pipelines entwickelt worden.

Fazit

Jenkins bietet mit den Pipeline-Plugins und der Blue-Ocean-Oberfläche eine gute Basis für Continuous-Delivery-Pipelines. Nach einer kurzen Eingewöhnung an den Aufbau des Jenkinsfiles geht die Erstellung von Pipelines, durch den deklarativen Ansatz, einfach von der Hand. Sollte die DSL mal nicht ausreichen, existiert noch immer die Möglichkeit, eigene Groovy-Funktionen zu schreiben und einzubauen. Mit Blue Ocean bietet Jenkins auch an der Oberfläche viele Funktionen und eine gute Übersicht, somit lassen sich Pipelines gut verwalten und Probleme leichter beheben.

Autor

Moritz Reinwald

Moritz Reinwald arbeitet als Berater mit dem Schwerpunkt APEX Development und DevOps. Neben der Erstellung von Webanwendungen liegen seine Schwerpunkte im Bereich CI/CD mit Jenkins und Testautomatisierung.
>> Weiterlesen
Kommentare (0)

Neuen Kommentar schreiben