CI/CD Pipeline auf einem Raspberry Pi – Teil 3

von | 02.03.2019 | Softwareentwicklung | 0 Kommentare

Nachdem wir in Teil 1 und 2 des Experiments den Raspberry Pi in Betrieb genommen und einen Jenkins-Server installiert haben, kommen wir nun in Teil 3 zur eigentlichen CI/CD Pipeline. Unser Ziel ist es, die zu entwickelnde Software automatisch auf einer Zielinfrastruktur bereitzustellen.

Die Beispiel-Anwendung

In meinem Beispiel verwende ich eine einfache Webanwendung. Sie besteht aus einem Frontend, das in Angular umgesetzt ist, und einem  Backend, das in .NET Core implementiert wurde. Da ich den Fokus auf die CI/CD Pipeline legen möchte, sind die Features der Anwendung denkbar simpel: Das Frontend lädt über einen REST-Aufruf eine Liste von Strings vom Server und zeigt diese in einer Liste an.

Welcome to test-client

Neben der eigentlichen Anwendung gibt es Unit-Tests, die beim Build automatisch ausgeführt werden und ein Ausrollen auf dem Zielsystem verhindern, sofern sie fehlschlagen. Die entsprechende Anwendung – sowohl das Frontend als auch das Backend – habe ich für Sie in Github angelegt: https://github.com/PFriedland89/ci-cd-test-application

Projekt-Struktur

Zuerst sollten wir einen Blick auf das git-repository werfen, denn hierauf setzt unser build auf. Dazu gehe ich kurz auf die Ordnerstruktur, dessen Inhalte und Funktionen ein:

• src
    ◦ client
    ◦ server
          ▪ test-server.Tests
          ▪ test-server

Alles ist in dem „src“-Ordner gebündelt. Innerhalb des Ordner gibt es jeweils einen Order für den Client Code („client“) und den Server Code („server“).

Im Ordner „client“ befindet sich eine typische Angular Anwendung, wie sie das Angular CLI nach dem Generieren einer Anwendung erzeugt. Einfachheitshalber ist jegliche Funktionalität in der App.Component und einem dazu gehörenden App.Service implementiert. Die Tests habe ich an dieser Stelle nur lauffähig gemacht – das ist völlig hinreichend für unsere Testzwecke.

Im Ordner „server“ gibt es zwei Unterordner. Im Ordner „test-server“ befindet sich ein .NET Core webAPI Projekt mit einem Controller, der über ein GET auf „http://localhost:5000/api/values“ angesprochen werden kann und ein Array von Strings liefert.

Im Ordner „test-server.Tests“ sind Unit Tests auf Basis von Xunit und FluentAssertions implementiert, die den Output des Controllers testen.

Aufbau der CI/CD Pipeline

Ich möchte an dieser Stelle betonen, dass es verschiedene Möglichkeiten gibt, eine CI/CD Pipeline technisch umzusetzen. Ich habe mich für eine einfache Lösung entschieden, um die Machbarkeit des Experiments bewerten zu können.

Jenkins organisiert seine Builds in Projekten. Jedes Projekt hat einen Namen, hat Start-Trigger, Build-Schritte und Post-Build-Aktionen. In unserem Experiment ist ein Projekt ein in sich geschlossener Schritt in der CI/CD Pipeline. Die verschiedenen Ablaufpfade der CI/CD Pipeline können Sie in nachfolgender Grafik erkennen:

Ablaufpfade CI/CD Pipeline

Der Build-Prozess wird aus drei Projekten bestehen:

• build-client
• build-server
• deploy-application

Diese drei Projekte sind miteinander verkettet. Die Ausführung eines Projektes erzeugt einen Build. Ist ein Build eines Projektes erfolgreich, startet das nächste Projekt in der Kette einen Build. Sollte ein Build nicht erfolgreich sein, weil bspw. der Code nicht kompilierfähig ist oder Tests nicht erfolgreich sind, soll eine E-Mail versendet werden. Ist auch der Build des Projekts „deploy-application“ erfolgreich beendet, wird eine Erfolgsmail mit dem Hinweis verschickt, dass die Anwendung erfolgreich gebaut und deployt wurde.

Da wir hier von einer CI/CD Pipeline sprechen, sollte das Triggern der Builds und ggf. auch das Deployment automatisch ablaufen. Dazu wird üblicherweise auf den ersten Build-Job der Kette ein Start-Trigger gesetzt. Da wir in unserem Beispiel auf Git setzen, wird ein Branchname benötigt, mit dem der Build-Prozess gestartet werden soll. Dadurch kann man immer noch entscheiden, wann die Kette getriggert wird.

Vorbereitung des Jenkins-Servers

Bevor wir die Projekte anlegen und fleißig Builds triggern können, müssen zuerst einige Installation nachgeholt werden. Bisher habe ich auf dem Raspberry Pi keinerlei Tooling bereitgestellt, um eine Angular-Anwendung oder .NET Core zu bauen und zu testen. Dazu verbinden Sie sich bitte per SSH mit Ihrem Raspberry Pi.

Frontend Build Tools

Für den Build von Angular benötigen wir Node.js als JavaScript-Laufzeitumgebung und npm als Paketmanager für Node.js. Diese lassen sich einfach mit der Paketverwaltung installieren:

Prüfen Sie bitte, ob Node.js korrekt installiert wurde, indem Sie sich die Version ausgeben lassen:

Installieren Sie als nächtes npm und testen Sie die Verfügbarkeit:

Backend Build Tools

Für das Backend benötigen Sie das .Net Core SDK. Glücklicherweise gibt es seit Version 2.2 auch offizielle Builds für ARM Architekturen. Eine Kleinigkeit gilt es zuallererst zu installieren:

Laden Sie als nächstes das .Net Core SDK herunter und installieren es:

Legen Sie jetzt noch schnell einen Link an, damit Sie bequem von überall die Kommandozeile nutzen können:

Und natürlich sollten Sie die Korrektheit der .NET Core Installation testen:

Damit sind Ihre Vorbereitungen abgeschlossen und Sie können nun über die Jenkins-Oberfläche beginnen, Projekte anzulegen und zu konfigurieren.

Projekt build-client

Der Build des Clients soll folgende Schritte umfassen:

  1. Aktuellen Source Code Stand aus dem Master holen
  2. Angular-Projekt erstellen
  3. Angular-Anwendung als Artefakt veröffentlichen

Melden Sie sich bitte zuerst bei Jenkins an und legen Sie von der Hauptseite aus per „Element anlegen“ ein neues Projekt vom Typ „Free Style“ an.

Jenkins - Free Style

Nun werden Sie auf die Konfigurationsseite des Projektes umgeleitet. Arbeiten Sie jetzt alle Schritte aus unserem Plan ab und beginnen Sie mit dem Checkout des Source Codes. Dazu gehen Sie auf „Source Code Management“ und geben die Daten vom Github Projekt an:

Source-Code-Management in Jenkins

Danach gehen Sie auf die Gruppe Build-Auslöser. Wählen Sie das Häckchen „Source Code Management System abfragen“ und geben bei Zeitplan „* * * * *“ ohne Anführungszeichen ein. Damit schaut Jenkins jede Minute, ob sich der Branch Master geändert hat, um ggf. den Build zu starten.

Als nächstes konfigurieren Sie die konkreten Build-Schritte, indem Sie auf den Reiter „Buildverfahren“ klicken. Auf dem Dropdown „Build-Schritt hinzufügen“ wählen Sie „Shell ausführen“. Das führt dazu, dass Sie ein kleines Textfenster erhalten, in dem Sie Shell-Befehle ausführen können. Hier gilt es alle Kommandos einzugeben, um den Client zu bauen, die Tests laufen zu lassen und den gebauten Client einzupacken. Geben Sie dazu folgenden Text in das Script-Feld ein:

Wechseln Sie in das Verzeichnis des Angular-Projekts und installieren Sie von dort aus alle npm-Pakete mittels „npm install“.

Erstellen Sie anschließend den Client mittels Angular CLI im Prod-Modus. Und als letztes Verpacken Sie das Ergebnis aus dem dist-Ordner in einen komprimierten Ordner und legen Sie es im Verzeichnis src/client ab.

Das Ergebnis eines Builds enthält in der Regel Build-Artefakte. Diese werden in einem separaten Verzeichnis vom Jenkins gespeichert und können aus erfolgreichen Builds heruntergeladen oder weiterverarbeitet werden. Es ergibt Sinn, den gebauten Client als Build-Artefakt zu sichern. Verwenden Sie dazu „Artefakt archivieren“, das Sie unter „Post-Build-Aktionen“ per Klick auf „Post-Build-Aktion erhalten, und vergeben Sie „src/client/client.tgz als Platzhalter:

Post-Build-Aktionen in Jenkins

Damit ist Ihr erster Buildjob fertig und bereit zum Testen. Speichern Sie die Konfigurationsseite und triggern einen Build (siehe rotes Rechteck):

Build triggern in Jenkins

Unten links sehen Sie den aktuell laufenden Build:

Build-Prozessor-Status

Wenn Sie auf den Fortschrittsbalken unter dem Text „build-client“ klicken, lande Sie direkt im Logfile. Dort können Sie den Verlauf des Builds beobachten.

An dieser Stelle ist mir beim ersten Durchlauf etwas ziemlich interessantes aufgefallen: npm versucht eine SASS-Abhängigkeit für die Plattform ARM aufzulösen, wofür es anscheinend keine Pakete gibt – schade! Allerdings bricht npm dann nicht ab, sondern lädt sich die Sourcen selbstständig herunter und übersetzt alles selbst mit g++ für ARM. Ich muss an dieser Stelle sagen, dass ich das ziemlich abgefahren finde.

Einziges Manko ist, dass das Ganze gefühlt ewig dauert. Bei mir hat das Kompilieren ca. 10-15 Minuten beansprucht. Glücklicherweise ist dies nur einmalig zu tun, danach sind die npm Module gecached und das Wiederherstellen dauert bei meinem Pi ca. 40 Sekunden. Für mich ist das akzeptabel.

Wenn der Build erfolgreich durchgelaufen ist, sehen Sie direkt das erstellte Build-Artefakt „client.tgz“:

Build-Artefakt

Damit haben Sie Ihren ersten Build-Job auf dem Raspberry Pi erfolgreich konfiguriert.

Im 4 und letzten Teil des Experiments beschreibe ich Ihnen die Erstellung des Build-Server-Projekts, das Deployment der Applikation und das Testen der Pipeline. Machen Sie dann auch wieder mit? Hier finden Sie Teil 4.

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.

Gefällt Ihnen dieser Beitrag?

Melden Sie sich für unsere News-Updates an und erhalten Sie einmal pro Woche Tipps und Meinungen von Fachleuten direkt in Ihren Posteingang.

Gefällt Ihnen dieser Beitrag?

Share This