CI/CD Pipeline auf einem Raspberry Pi – Teil 4

von | 09.03.2019

Im 4. und letzten Teil des Experiments, eine CI/CD Pipeline auf einem Raspberry Pi aufzusetzen, geht es um die verbleibenden beiden Projekte aus dem Build-Prozess: build-server und deploy-application. Und es geht um die E-Mail Notifikation und das Testen der implementieren Pipeline. Und last but not least möchte ich Ihnen mein Fazit und einen Ausblick geben.

Beginnen wir mit dem Projekt “build-server”:

Projekt build-server

Das Projekt „build-server“ erstellen Sie so, wie Sie das Projekt “build-client” bereits erstellt haben: klicken Sie in Jenkins auf der Hauptseite auf „Element anlegen“. Glücklicherweise können Sie sich etwas Zeit und Arbeit sparen, indem Sie unter „Kopieren von“ das Projekt „build-client“ auswählen. Dadurch werden alle Einstellungen und Build-Schritte von Ihrem vorherigen Projekt einfach kopiert:

Build-Server-Projekt durch Kopieren anlegen

Nach dem Klicken auf „Ok“ landen Sie auf der Konfigurationsseite des Projekts. Entfernen Sie dort unter “Build Auslöser“ das Häkchen „Source Code Management System abfragen“ und setzen dafür das Häkchen bei „Starte Build, nachdem andere Projekte gebaut wurden“. Geben Sie anschließend „build-client“ ein und wählen Sie die Option „Nur auslösen, wenn der Build stabil ist“. Damit wird dieser Build automatisch gestartet, sofern „build-client“ erfolgreich durchgelaufen wurde.

Im Abschnitt „Buildverfahren“ editieren Sie nun bitte das Script-Fenster, um die Tests des Servers durchzuführen:

cd src/server/ReportGenerator.Tests
dotnet restore
dotnet build
dotnet test --no-build -l "trx;LogFileName=TestResult.trx" || true
cd ../../../

An dieser Stelle wird Testprojekt erzeugt, danach der Testrunner gestartet und das Ergebnis in einer MSTest-Result-Datei gespeichert, wobei standardmäßig der Ausführungspfad im Unterordner TestResults gespeichert wird.

Als nächstes sollten Sie den Client im wwwRoot des Servers bereitstellen, damit die integrierte Anwendung als Paket auf einen Webserver hochgeladen werden kann. Doch wie kommen Sie jetzt an einen Client heran? Der wurde doch in einem separaten Build-Job erstellt? Dafür gibt es ein hilfreiches Jenkins-Plugin namens „Copy Artifact Plugin“. Dieses finden Sie über die Namenssuche auf der Hauptseite bei „Jenkins verwalten“ → „Plugins verwalten“. Bitte installieren Sie das Plugin, indem Sie einfach den Anweisungen auf der Oberfläche folgen.

Sie kommen übrigens schnell über die Hauptseite zur Konfigurationsseite zurück, wenn Sie in der Liste der Build-Jobs direkt neben dem Namen das kleine Dreieck drücken und im Dropdown „Konfigurieren“ wählen:

Mit einem Klick Konfigurieren auswählen

Fügen Sie nun nach dem „Shell ausführen“ Build-Schritt über „Artefakte aus einem anderen Projekt kopieren” ein. Das Quellprojekt ist „build-client“. Zielverzeichnis ist das Serververzeichnis:

Artefakte aus einem anderen Projekt kopieren

Fügen Sie nun noch einen letzten Build-Schritt hinzu, der wieder den Typ „Shell ausführen“ besitzt:

cd src/server/test-server

# extract client and pack to wwwRoot folder
mkdir -p wwwRoot
tar -xzf client.tgz -C wwwRoot
# build self-contained dotnet core application with frontend bundled
dotnet publish -c Release -f netcoreapp2.1 -o "bin/publish" --self-contained -r win-x86 -v normal

# pack application to compressed file.
cd "bin/publish"
tar cvfz application.tgz ./*

cd ../../../../../

Im vorherigen Schritt haben Sie das “client.tgz” Artefakt in den Ordner “src/server/test-server” kopiert, so dass es somit in den wwwRoot-Ordner entpackt werden kann. Danach wird die Anwendung mit dem Kommandozeilen-Tool “dotnet publish” als “self-contained” Anwendung gebaut und im Ordner “bin/publish” bereitgestellt.

Der nachfolgende Aufruf von “tar” legt das gesamte Paket als „application.tgz“ im Ordner src/server/test-server/bin/publish ab.

Abschließend müssen Sie noch die erstellte Anwendung als Build-Artefakt ablegen und das Testergebnis vom Testlauf veröffentlichen. Für letzteres benötigen Sie ein weiteres Jenkins-Plugin namens „MSTest Plugin“. Bitte installieren Sie dieses über die Jenkins-Konfigurationsseite, bevor sie weitermachen.

Nun brauchen Sie noch zwei Post-Build-Aktionen: „Artefakte archivieren“ und „Publish MSTest test result report“. Konfigurieren Sie die Schritte wie auf dem folgenden Bild angegeben:

Post-Build-Aktionen im Projekt "build-server"

Prima, damit ist Ihr „build-server“-Job fertig konfiguriert, und sollte fortan Testergebnisse produzieren und eine fertige Anwendung als Build-Artefakt hinterlegen. Probieren Sie auch ruhig schon mal aus, ob beim Start des „build-client“-Jobs auch der „build-server“-Job automatisch gestartet wird.

Projekt deploy-application

Der letzte Teil der Pipeline ist dafür zuständig, die gebaute Webanwendung per FTP auf einem Webserver hochzuladen. Ziel für eine .NET Core Anwendung kann ein Internet Information Service (IIS) in Ihrer Infrastruktur, eine Azure WebApp oder ein beliebiger Cloud-Anbieter sein. In meinem Fall habe ich eine Anwendung auf einem Drittanbieter namens A2 Hosting provisioniert – welches sich an dieser Stelle mit FTP einfach realisieren lässt. Auch hier bietet Jenkins ein passendes Plugin, so dass dieser Schritt lediglich konfiguriert werden muss. Bitte installieren Sie das Plugin „Publish Over FTP“.

Sobald die Installation abgeschlossen ist, gehen Sie zu „Jenkins verwalten“ → „System Konfigurieren“. Im Abschnitt „Publish over FTP“ konfigurieren Sie die Zugangsdaten zum FTP-Server. Ein Knopfdruck auf „Test Configuration“ stellt sicher, dass die Verbindung korrekt eingerichtet ist.

Publish over FTP

Nach einem Klick auf “Speichern” können Sie auf die Hauptseite zurückkehren und das nächste Projekt anlegen. Es handelt sich wieder um ein “Free Style”-Projekt mit dem Namen „deploy-application“, allerdings basiert es diesmal nicht auf einem vorherigen Projekt.

Dieses Projekt ist etwas anderes, denn hier benötigen Sie den Source Code nicht mehr, sondern nur noch das gebaute Build-Artefakt aus dem Projekt „build-server“. Daher bleibt bei „Source Code Management“ alles leer. Der Build-Auslöser ist „Starte Build, nachdem andere Projekte gebaut wurden“. In das Eingabefeld geben Sie einfach „build-server“ ein und wählen anschließend die Option „Nur auslösen, wenn der Build stabil ist.

Im Abschnitt Buildverfahren fügen Sie bitte als ersten Schritt „Artefakte aus einem anderen Projekt kopieren“ ein:

Buildverfahren

Danach benötigen Sie noch einen Schritt „Shell ausführen“, um das Artefakt auszupacken und den Upload vorzubereiten. Hier ist der Script-Inhalt:

rm -rf upload
mkdir upload
tar xf application.tgz -C upload

Hier wird nur ein Verzeichnis erstellt und die integrierte Anwendung entpackt. Als nächstes laden Sie die Anwendung per FTP hoch. Dazu verwenden Sie einfach den Build-Schritt „Send files over FTP“ aus dem „Publish over FTP“ plugin. Wählen Sie eine konfigurierte Verbindung unter den globalen Einstellungen aus und dort Quelldateien und Zielordner auf dem FTP. Das war es dann auch schon:

Send files over FTP

Herzlichen Glückwunsch! Sie haben nun eine vollständig funktionierende CI/CD Pipeline auf Ihrem Raspberry Pi.

E-Mail Notifikationen

Als Sahnehäubchen können Sie jetzt noch eine E-Mail Notifikation bei erfolgreichen oder fehlerhaften Builds einrichten. Dafür müssen Sie nur unter „Jenkins verwalten“ → „System Konfigurieren“ im Abschnitt „Extended E-Mail Notifications“ Ihren E-Mail-Server konfigurieren und unter „Default Triggers“ einstellen, wann E-Mails automatisch an wen versendet werden sollen.

Pipeline testen

Funktioniert die CI/CD Pipeline wie erhofft? Das sollten Sie jetzt natürlich auch testen. Starten Sie einfach den Build-Prozess auf dem Client; nach einigen Minuten sollten Sie einen erfolgreichen Build und einen Upload auf dem Zielsystem verzeichnen können.

Als weiteren Anwendungsfall sollten Sie aber auch untersuchen, ob die Pipeline auch im Fehlerfall reagiert. Dazu müssen Sie noch einige Schritte in Angriff nehmen. Erstellen Sie bitte als ersten einen Github-Account und forken Sie das Repository: https://github.com/PFriedland89/ci-cd-test-application. Dadurch wird das Github-Repository in Ihren Github-Account geklont und referenziert. Dadurch haben Sie jetzt die Chance, auch Änderungen an der Anwendung durchzuführen.

Nun müssen Sie noch in jedem Projekt die Source-Code-Management-Einstellungen auf Ihr Repository umstellen. Gehen Sie dazu in die Konfiguration jedes Ihrer Projekte und ändern entsprechend die URL  unter Source-Code-Management. Starten Sie testhalber die Build-Kette, um zu prüfen, ob die Umstellung funktioniert hat.

Nun können Sie Ihren Fork auf Ihren Computer klonen, so dass Sie jetzt die Anwendung erweitern können; bspw. könnte sie anstelle von zwei nun drei Strings ausgeben. Ändern Sie hierfür die Datei src/server/test-server/Controllers/ValuesController.cs die Methode Get() wie folgt ab:

[HttpGet]
public IActionResult Get()
{
    return Ok(new string[] { "value1", "value2", "value3" });
}

Der dritte String sollte fortan auf der Oberfläche nach dem Build zu sehen sein. Pushen Sie das Ganze jetzt auf dem Master-Branch und beobachten Sie Jenkins. Sie werden sehen, dass „build-client“ automatisch getriggert wird und die CI/CD Pipeline beginnt.

Allerdings werden Sie auch feststellen, dass die Anwendung nicht deployed wird, weil der Build-Job „build-server“ fehlschlägt. Sollten Sie Ihre Mail Notifikationen eingerichtet haben, werden Sie sogar darüber per Mail informiert. Bei einem Blick in den Build-Job sehen Sie, dass ein Test fehlgeschlagen ist:

Expected okResult.Value to be a collection with 2 item(s), but {"value1", "value2", "value3"}"
"contains 1 item(s) more than"
"{"value1", "value2"}.

Auch ein Blick in die Anwendung verrät, dass nicht wie gewünscht deployed wurde:

Welcome to the Test-Client.

Es gibt einen Unit-Test, der den Rückgabewert des Controllers testet und nicht zusammen mit der Code-Änderung aktualisiert wurde. Das holen Sie nach, indem Sie die betreffende src/server/test-server.Tests/Controllers/ValuesControllerTests.cs anpassen:

okResult.Value.Should().BeEquivalentTo(new[] {
    "value1", "value2", "values3"
});

Wenn Sie diese Änderung nun per Push veröffentlichen, wird die Build Pipeline wieder gestartet und das gewünschte Ergebnis lässt sich nach einigen Minuten sehen:

Das gewünschte Ergebnis mit drei Strings

Mein Fazit

CI/CD ist ein beliebtes Thema und in meiner Tätigkeit als Softwareentwickler und Softwarearchitekt konnte ich verschiedene Pipelines bspw. mit dem Team Foundation Server implementieren. Meiner Meinung nach ist es ein sehr nützliches Konzept, um kontinuierliche Integration zu gewährleisten, und den Fokus auf fertige Features zu legen. Essenzielle Voraussetzung dafür sind Unit- und idealerweise sogar Integrationstests, sowie eine stabile, nachhaltig implementierte Automatisierung von Build und Deployment.

Insgesamt funktioniert eine CI/CD Pipeline auf einem Raspberry Pi überraschend gut – besser als ich es erwartet habe. Das Betriebssystem Raspbian Lite Stretch booted innerhalb von Sekunden, der Jenkins Server ist nach ungefähr 20 Sekunden verfügbar. Durch die Tatsache, dass sich alles mit einem vollwertigen Linux-System administrieren lässt, steht in Sachen Tooling quasi alles zur Verfügung, was auch eine „große Kiste“ liefern würde.

Allerdings gibt es auch einige Abstriche: Die Performance ist vergleichsweise eingeschränkt. Größere Anwendungen, besonders I/O- und CPU-lastige Operationen, reagieren ziemlich träge. Bestes Beispiel ist das Kompilieren der SASS-Abhängigkeit mittels C++. Das “npm Install” dauert – wenn alles bereits vorhanden ist – etwa 40 Sekunden. Das geht auf einer vollwertigen Maschine deutlich schneller. Daher scheint der Einsatz für große Systeme eher ungeeignet.

Die meisten Sorgen macht der geringe Arbeitsspeicher (1 GB). In meinem Experiment war er hinreichend groß, aber mit hungrigeren Anwendungen könnte der Raspberry Pi schnell an seine Grenzen stoßen. Bedingt durch seine ARM-Architektur schränkt der er ggf. auch gewisse Technologien ein; ist bspw. kein SDK für ARM verfügbar, lässt sich eine Pipeline nur schlecht auf dem Raspberry Pi implementieren.

Die Arbeit mit der Jenkins-Weboberfläche empfinde ich als sehr angenehm, auch wenn sie zeitweise etwas langsam reagiert, so war sie insgesamt doch recht schnell. Wenn Sie jetzt noch bedenken, dass wir im Zuge des Experiments das Feature der Build-Nodes von Jenkins gar nicht verwendet haben – Jenkins kann seine Builds auch auf andere Instanzen verteilen, anstatt selbst seine Rechenkapazität zu opfern – dann schreit dies  geradezu danach, pro Build-Slot einen eigenen Pi für den Server oder das Frontend zu nutzen. Auch mehrere parallele Builds könnten implementiert und auf verschiedenen Stages gleichzeitig deployed werden. Dieser Ansatz ließe sich beliebig skalieren.

Darüber hinaus bietet Jenkins auch ein neues, eigenes Konzept zum Erstellen von CI/CD Pipelines mit dem passenden Namen „Pipeline“. Die Pipeline wird dann als Ganzes in einer deklarativen oder gescripteten Syntax beschrieben und auch direkt in Git versioniert. Entsprechende Informationen dazu finden Sie in der offiziellen Dokumentation unter https://jenkins.io/doc/book/pipeline/.

Vielen Dank für Ihr Interesse an meinem Experiment. Für mich war es ein Erfolg. Für Sie auch?

 

Hinweise:

Interessieren Sie sich für weitere Tipps aus der Praxis? Testen Sie unseren wöchentlichen Newsletter mit interessanten Beiträgen, Downloads, Empfehlungen und aktuellem Wissen.

Wir suchen Softwareentwickler. Berufseinsteigerinnen, Entwickler mit einigen und Expertinnen mit vielen Jahren Erfahrung.

Hier finden Sie die anderen drei Teile der Serie von Peter Friedland:

t2informatik Blog: CI/CD Pipeline auf einem Raspberry Pi – Teil 1

CI/CD Pipeline auf einem Raspberry Pi – Teil 1

t2informatik Blog: CI/CD Pipeline auf einem Raspberry Pi – Teil 2

CI/CD Pipeline auf einem Raspberry Pi – Teil 2

t2informatik Blog: CI/CD Pipeline auf einem Raspberry Pi – Teil 3

CI/CD Pipeline auf einem Raspberry Pi – Teil 3

Peter Friedland
Peter Friedland

t2informatik GmbH

Peter Friedland ist bei der t2informatik GmbH als Software Consultant tätig. In verschiedenen Kundenprojekten entwickelt er innovative Lösungen in enger Zusammenarbeit mit Partnern und den Ansprechpartnern vor Ort. Und ab und an bloggt er auch.