Von Philipp Kürsten auf Dienstag, 17. Juli 2018
Kategorie: Application Development

Build on Commit mit Jenkins und Git

In der Entwicklungsphase eines jeden Software-Projektes ist eine frühe Erkennung von Fehlern unabdingbar. Bei großen Projekten mit vielen Abhängigkeiten kann es mühsam und langwierig sein, das gesamte Projekt auf dem lokalen Rechner zu kompilieren und zu testen. Abhilfe hierbei können Continuous Integration (CI) und Delivery (CD) Plattformen wie der Jenkins schaffen.​ Diese ermöglichen einen dezentralen Build-Prozess auf einem Server der meist deutlich mehr Leistung hat, als der eigene Rechner. Eine solche CI-Umgebung ermöglicht einen automatisierten Buildprozess, wahlweise event- u./o. zeitgesteuert (z. B. NightlyBuilds). Die Konfiguration einer Build-On-Commit Pipeline im Jenkins ist allerdings auf den ersten Blick nicht so einfach einzurichten. Dieser Blog-Beitrag soll ein Grundverständnis für den Aufbau einer solchen Build-On-Commit-Pipeline vermitteln und an einen Beispiel verdeutlichen.

Zeit gesteuerte Builds

In der Abbildung 1 werden die verschiedenen Arten zum Auslösen eines Builds im Jenkins dargestellt. Diese können wie folgt beschrieben werden:

Der am häufigsten verwendete Build-Auslöser ist der Zeit gesteuerte Build. Dieser kann durch die Eingabe einer speziellen Syntax, ähnlich der von Cron, genauer spezifiziert werden. Eine Beschreibung der möglichen Eingabewerte kann im Jenkins durch das klicken des '?'-Buttons erfragt werden.

Mit Hilfe dieser Syntax lassen sich somit zeitgesteuerte Builds konfigurieren, die beispielsweise wie folgt aussehen könnten: 

​Jenkins-Regel ​Beschreibung
​H/15 * * * 1-5 Löse den Build alle 15 Minuten aus, zwischen Montag und Freitag.
H H 1,15 1-11 *​Löse den Build immer am 1. und 15. eines Monats aus, außer im Dezember.
​45 H(9-16)/2 * * *​Löse den Build alle zwei Stunden zwischen 09:00 Uhr und 16:00 Uhr aus, aber 45 Minuten nach dem ersten Start (09:45-15:45 Uhr).

​Das Problem von zeitgesteuerten Builds liegt allerdings auf der Hand. Das Feedback, ob ein Build erfolgreich war und ob alle Unit-Tests etc. erfolgreich durchgelaufen sind, lässt sich somit für den Entwickler nicht unmittelbar nach dem Commit seines Arbeitsstandes prüfen, sondern erst nach dem Buildlauf, gem. definierter Zeiten. Abhilfe hierbei kann das Auslösen eines Build-Jobs durch das Abfragen des Source-Code Management Systems schaffen.

Reagieren auf Veränderung

Eine Reaktion auf Veränderung im Projekt-Repository lässt sich durch die Verwendung des Build-Auslösers 'Source Code Management System abfragen' realisieren.

Durch die Verwendung dieses Triggers wird beim Abfragen des Source Code Management Systems, in unserem Fall Git, anhand der Commit-ID geprüft, ob der aktuellste Stand bereits vom Jenkins gebaut wurde. Ist dies der Fall, passiert gar nichts. Andernfalls wird der gesamte Build-Prozess, der im Jenkins konfiguriert wurde, durchgeführt. Das Git-Abfrage-Protokoll wird im Jenkins-Job hinterlegt und sieht beispielhaft wie folgt aus:

Das Problem:

Auch beim Abfragen des Source Code Management Systems muss man im Jenkins angeben, wann diese Abfrage getätigt werden soll. Es ist natürlich möglich als zeitliche Regel eine ständige Abfrage zu konfigurieren (Jenkins-Regel: '* * * * *'). Hierdurch würde der Jenkins-Job jede Minute das Git Repository prüfen, ob in dem definierten Branch eine Änderung stattgefunden hat.

Die Art der Abfrage kostet eine Menge Rechenleistung und ist somit keine mögliche Lösung.

Die Lösung:

Anstelle der Anfrage des Jenkins-Jobs an das Git-Repository (Pull-Mechanismus), um eine Veränderung feststellen zu können, ist es sinnvoller den Jenkins über eine Anfrage zu informieren (Push-Mechanismus). Der Jenkins überprüft bei einer Veränderung automatisch das Git- Repository, somit werden die Abfragen auf ein Minimum reduziert.

Um diese Konfiguration möglich zu machen, muss der Build-Auslöser 'Source Code Management System abfragen' ausgewählt werden. Wenn keine Eingabe bei der zeitlichen Regel getätigt wird, dann wird der Build nur auf Zuruf angestoßen. Darüber hinaus muss das 'Git Plugin' [5] im Jenkins aktiviert sein, das automatisch folgenden Endpunkt bereitstellt:

curl http://dein-jenkins.server:8080/git/notifyCommit?url=<URL of the Git repository>[&branches=branch1[,branch2]*][&sha1=<commit ID>]

Schickt man nun mittels curl oder ähnlichem eine HTTP-Anfrage an den Jenkins, wird dieser darüber informiert das Git-Repository nach Veränderungen zu überprüfen.

Der Entwickler kann nun also seine Arbeit in Git per Commit an das Remote-Repository schicken und danach im Browser die oben genannte URL aufrufen, daraufhin wird das Projekt automatisch gebaut. 

Git-Hooks

Damit der gesamte Prozess nun wirklich während eines Commits angestoßen wird und keine weitere Nutzer-Interaktion benötigt, kann der Aufruf des Jenkins-Jobs automatisiert über Git-Hooks getätigt werden.

Git-Hooks sind Ereignis-basiert. Immer dann, wenn bestimmte Git-Befehle ausgeführt werden, überprüft die Software das Hooks-Verzeichnis, um festzustellen, ob für das aktuelle Ereignis ein Hook vorliegt und führt diesen dann aus.

Es gibt zwei Arten von Git-Hooks:


Die verschiedenen Git-Hooks, auf die reagiert werden kann, können der Dokumentation von Git [4] entnommen werden.

In der Regel ist die Verwendung des 'PostReceive'-Hooks die beste Wahl. Hierbei handelt es sich um einen Server-Seitigen Hook, der immer nach der Verarbeitung eines Commits aufgerufen wird. Die Verwendung des Server-Seitigen Hooks bietet sich an, da somit die Konfiguration für das Build-On-Commit nur an einer zentralen Stelle geändert werden muss. Bei einem Client-Seitigen-Hook müsste jeder Entwickler diese Einstellungen manuell in seinem Git-Repository vornehmen.

Um einen Hook zu aktivieren, muss eine Datei mit dem Namen 'post-receive' im Hooks-Verzeichnis des Git-Repositories auf dem Server hinterlegt werden. Dieses Verzeichnis wird automatisch bei der Initialisierung eines Git-Repositories angelegt. Der Inhalt der Datei wird als Shell-Skript geschrieben und kann in unserem Beispiel wie folgt aussehen:

Durch obige Konfiguration wird im ersten Schritt der Jenkins-Server benachrichtigt und im zweiten Schritte eine Bemerkung ausgegeben. 

Wenn nun ein Commit an das Remote-Repository abgeschickt wird (Push), so wird automatisch dieses Skript nach der Verarbeitung auf dem Server ausgeführt. Der Jenkins-Build wird daraufhin ebenfalls automatisch ausgeführt. 
Bei einem Git-Push sieht dies dann wie folgt aus:

Fazit

Die Kombination des Jenkins Git-Plugin mit Git-Hooks ermöglicht eine schnelle und einfache Konfiguration einer Build-On-Commit Pipeline im Jenkins. Wurden alle Schritte wie oben beschrieben umgesetzt, so steht einer Continuos Integration Plattform nichts mehr im Wege. Der Entwickler bekommt somit sofort Feedback über seinen veröffentlichten Arbeitsstand und kann dementsprechend reagieren.

Durch die Zunahme eines automatisierten Deployments im Jenkins auf die Zielumgebung (z.B. Applicationserver, Web-Container etc.), kann die Software sogar einem Tester sofort nach einem erfolgreichen Build-Prozess zur Verfügung gestellt werden.


Glossar

Continuous Integration
Continuous Integration beschreibt den Prozess des fortlaufenden Zusammenfügens von Komponenten zu einer Anwendung. Das Ziel der kontinuierlichen Integration ist die Steigerung der Softwarequalität.

Git
Eine Software zur verteilten Versionsverwaltung von Dateien. Git ist ein sehr beliebtes Tool innerhalb von Softwareentwicklungsprojekten, da hierdurch sowohl die Versionsverwaltung als auch die parallele Arbeit im Team ermöglicht wird.

Jenkins
Eines der bekanntesten Open-Source Applikationen in Bezug auf automatisierte Builds und Deployments für eine Menge an verschiedenen Projekttypen.

Cron
Ein Werkzeug im Linux-Umfeld, dass zur Automatisierung von Zeit gesteuerten Befehlen verwendet wird.

Links

[1] ORDIX Blog - Git

[2] ORDIX Blog - Softwareentwicklung auf einem höheren Level – Continuous Integration

[3] ORDIX® seminare - Continuous Integration Workshop

[4] Git - Git Hooks

[5] Git Plugin - Jenkins

Bildnachweis

© pixabay.com | gedanken-idee-innovation-phantasie-2123971