Erweiterte Jenkins – Funktionen

In diesem Artikel will ich den Build-Job um einige neue Features erweitern. Der erweiterte Build-Job soll folgendes können:

  • Auswertung der Junit-XML-Testergebnisse
  • Anzeige einer Build-Historie und Metriken
  • Codeanalyse mit Checkstyle
  • Automatisches Deployment in den Servlet-Container Tomcat 8.x

Auswertung der Junit-Testergebnisse

Der Junit-Test der Spring Boot Anwendung erzeugt Reports für die Tests im HTML- und im XML-Format. Die Reports findet ihr in diesen Verzeichnissen eures Projekts:

  • HTML-Format: build/reports/tests/test
  • XML-Format: build/test-results/test

Der HTML-Report eignet sich gut zur direkten Anzeige in eurem Web-Browser. Der XML-Report kann für automatisierte Auswertung verwendet werden, z.B. vom Jenkins.

In der Standard-Installation von Jenkins wird bereits das Junit Plugin installiert, das die XML-Reports von Junit auswerten kann.

Um das Plugin in den Build-Job einzubinden, ruft ihr unter Meine Ansichten -> Euer Build-Job -> Konfigurieren die Konfiguration-Seite für euren Build-Job auf. In der Rubrik Post-Build-Aktionen wählt ihr aus der Dropdown-Box diesmal Veröffentliche Junit-Testergebnisse aus.

Junit Testergebnisse veröffentlichen

Post-Build Junit-Testergebnisse

Danach müsst ihr dem Junit-Plugin nur noch mitteilen, wo es die XML-Testberichte von Junit finden kann und wie der Name der Berichte lautet. Hier sind Wildcards und relative Pfad-Angabe möglich. Gebt folgendes bei Testberichte im XML-Format ein:

**/build/test-results/test/TEST-*.xml

Junit-XML-Results

Pfad für Junit-XML-Results

Ein Problem mit dem Plugin hatte ich, wenn sich an den Junit-Tests nichts geändert hat.dann wurde der Build-Job als Failure markiert, wenn die Junit-XML-Dateien zwar vorhanden waren, sich aber vom Datum nicht geändert hatten. Das passiert z.B. dann, wenn sich zwischen zwei Builds nichts an den Sourcen und den Junit-Tests geändert hat.

Um dies zu umgehen, kann man in der Build-Job-Konfiguration unter der Rubrik Buildumgebung die Checkbox Arbeitsbereich vor dem Build löschen markieren. Unter Patterns for files to be deleted können die XML-Dateien des Junit-Builds angegeben werden. Dadurch werden sie vor jedem Build gelöscht und beim Build  wieder neu erzeugt. Gebt hier für Include folgendes ein:

**/build/test-results/test/TEST-*.xml

Junit-XML löschen

Löschen Junit-XML

Speichert die Einstellungen und startet den Build-Job wieder mit Jetzt bauen. Nach dem Build solltet ihr in der Status-Übersicht eures Build-Projekts ein neues Icon mit einem Link auf die Junit-Testergebnisse haben.

Veröffentlichte Junit-Test-Ergebnisse

Junit-Test-Ergebnisse

Build-Historie und Metriken

Metriken sind hilfreich, um z.B. zu sehen, wie oft die Projekt-Builds fehlerhaft oder ohne Fehler waren. Am Beispiel eines Jenkins Plugins, zeige ich euch, wie ihr euch Metriken für euer Projekt erzeugen und anzeigen lassen könnt. Mit Hilfe des Plugin Build History Metrics könnt ihr euch folgende Metriken in der Status-Übersicht eures Build-Projekts anzeigen lassen:

  • Mean time to failure (MTTF)
  • Mean time to recovery (MTTR)
  • Standard deviation of build times

Zunächst müsst ihr das Plugin unter Jenkins verwalten -> Plugins verwalten installieren. Sucht bei den Plugins nach Build History Metrics. Nach der Installation müsst ihr den Jenkins-Server neu starten. Wenn ihr nach dem Neustart in die Übersicht eures Build-Projekts wechselt, solltet ihr einen neuen Bereich mit den Metriken sehen.

Build - Metriken

Build – Metriken

Codeanalyse

Damit ihr ein Plugin für Codeanalyse in eurem Jenkins-Build-Job verwenden könnt, sind zwei Schritte nötig:

  • In eurem Gradle-Build der Spring Boot Anwendung  benötigt ihr ein Plugin, das beim Build des Projekts eine Code-Analyse durchführt und einen Report des Ergebnis erzeugt
  • Im Jenkins-Server benötigt ihr ein Plugin, das den erzeugten Report lesen und auswerten kann

Ein bekanntes Tool zur statischen Codeanalyse ist z.B. Checkstyle. Für Checkstyle gibt es Plugins für Eclipse, Gradle und für Jenkins.

Zunächst müsst ihr euer Gradle – Build – Script um das Checkstyle-Plugin erweitern. Fügt dazu in die Datei build.gradle folgende Zeilen ein:

apply plugin: 'checkstyle'

tasks.withType(Checkstyle) {
    ignoreFailures = true
}

Das Attribut ignoreFailures bewirkt, das Gradle den Build nicht abbricht, wenn Checkstyle-Fehler vorhanden sind.

Damit das Checkstyle-Plugin funktioniert benötigt ihr noch eine Konfiguration-Datei, in der ihr festlegt mit welchen Vorgaben Checkstyle euren Code prüfen soll. Dazu legt ihr euch eine Datei mit dem Namen checkstyle.xml in einem dem Verzeichnis config/checkstyle in eurem Projekt-Verzeichnis an.

Beispiele für checkstyle.xml findet ihr z.B. im Internet oder als Beispiel hier zum Download checkstyle.xml. Weitere Informationen zu Checkstyle findet ihr hier: http://checkstyle.sourceforge.net/.

Die Konfigurations-Datei sollte sinnvollerweise am Beginn eines Projekts erstellt und in das zentrale Repository hochgeladen werden. Jeder Entwickler sollte dann diese Checkstyle-Datei verwenden.

Ihr solltet nun eine neue Gradle-Task mit dem Namen check haben. Die Code-Analyse mit Checkstyle könnt ihr jetzt einfach aufrufen mit:

gradle check

Damit sollten die Checkstyle-Reports im Verzeichnis build/reports/checkstyle in eurem Projekt-Verzeichnis erzeugt worden sein. Die Reports werden sowohl im HTML-Format, zur Anzeige im Web-Browser, als auch im XML-Format erzeugt.

Nun müsst ihr alle Änderungen wieder in euer zentrales Repository hochladen, damit euer Build-Job am Jenkins-Server die aktuellste Version eures Gradle-Builds verwendet.

Im nächsten Schritt müsst ihr das Plugin für Jenkins in eurem Jenkins Server installieren. Wechselt dazu wieder zu der Plugin-Seite von Jenkins:  Jenkins verwalten -> Plugins verwalten. Sucht bei den Plugins nach Checkstyle und installiert das Plugin. Nach der Installation müsst ihr den Jenkins-Server noch mal neu starten.

Im letzten Schritt fügt ihr in der Konfiguration eures Build-Projekts eine neue Post-Build-Aktion hinzu. Wählt als Aktion Veröffentliche die Ergebnisse der Checkstyle Analyse.

Checkstyle

Post-Build-Aktion Checkstyle

Dem Checkstyle-Plugin müsst ihr wieder den Pfad zu den XML-Reports angeben, die Checkstyle beim Gradle-Build erzeugt hat. Die Report-Dateien liegen wieder im Workspace von Jenkins unter:

jenkins/workspace/SpringBoot/build/reports/checkstyle

Gebt im Feld Dateiname Checkstyle Ergebnisse den relativen Pfad zu diesen Dateien ein:

**/build/reports/checkstyle/*.xml

Checkstyle-Pfad

Checkstyle-Pfad

Speichert die Einstellungen und starten den Build-Job. Danach solltet ihr in der Konsole die Ergebnisse von Checkstyle sehen. Nach der Fertigstellung des Builds seht ihr in der Übersicht eures Build-Projekts einen neuen Menüpunkt Checkstyle Warnungen und eine Statistik über die Entwicklung der Checkstyle-Warnungen bei den Builds.

Checkstyle-Übersicht

Checkstyle-Übersicht

Continuous Deployment

Standardmäßig erzeugt der Gradle-Build der Spring Boot Anwendung ein JAR – File zusammen mit einem Embedded Servlet-Container, z.B. Tomcat. Beim Start der Anwendung wird die Anwendung automatisch in dem Embedded Tomcat ausgeführt.

Sehr oft ist auf dem Server, auf dem die Anwendung am Ende laufen soll, aber bereits ein Servlet-Container, wie Tomcat oder WildFly installiert und soll auch für die Anwendung verwendet werden. Zum Deployment der Spring Boot Anwendung in bestehenden Servlet-Container, muss der Gradle-Build ein WAR – File mit allen benötigten Dateien und Bibliotheken erzeugen.

Dieses WAR – File kann dann direkt auf dem bereits installierten Servlet-Container ausgeführt werden.

Erzeugen eines WAR-Files

Zunächst müsst ihr eueren Gradle-Build um ein neues Plugin zum Erzeugen des WAR-Files erweitern. Macht dazu folgende Einträge in die Datei build.gradle eures Projekts:

apply plugin: 'war'

dependencies {
  
  providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
}

Danach habt ihr eine neue Gradle-Task war. Mit gradle build oder gradle war wird nun im Verzeichnis build/libs auch ein WAR-File eurer Spring Boot Anwendung erzeugt.

Damit die Applikation im Servlet-Container gestartet werden kann, muss auch der Start der Spring Boot Anwendung ein wenig modifiziert werden. Kommentiert dazu in eurer Start-Klasse, z.B. SpringbootApplication.java, die Start-Befehle aus:

/*
@SpringBootApplication
public class JenkinstestApplication {
	public static void main(final String[] args) {
		// Test
		SpringApplication.run(JenkinstestApplication.class, args);
	}
}
*/

Und fügt dafür dieses Code-Schipsel ein:

@SpringBootApplication
public class JenkinstestApplication extends SpringBootServletInitializer {

    @Override
    protected final SpringApplicationBuilder 
       configure(final SpringApplicationBuilder application) {
        return application.sources(JenkinstestApplication.class);
    }
    public static void main(final String[] args) throws Exception {
        SpringApplication.run(JenkinstestApplication.class, args);
    }
}

Als nächstes muss noch ein Servlet-Container auf dem Server installiert werden. Dafür habe ich den Tomcat 8 auf dem Raspberry PI installiert.

Tomcat Installation

Auf dem Raspberry PI habe ich den Tomcat im Verzeichnis /usr/share/tomcat installiert. Zuerst legt ihr ein Verzeichnis an in das ihr Tomcat herunterladen könnt:

cd /usr/share
sudo mkdir tomcat
cd tomcat
sudo wget http://www-eu.apache.org/dist/tomcat/tomcat-8/v8.5.11/bin/apache-tomcat-8.5.11.tar.gz

Eine Übersicht über die aktuellen Versionen des Tomcat könnt ihr euch unter Tomcat-Versionen anschauen. Jetzt das ganze Entpacken und in das richtige Verzeichnis verschieben:

sudo tar xvzf ./apache-tomcat-8.5.11.tar.gz
sudo mv apache-tomcat-8.5.11/* . 
sudo rm apache-tomcat-8.5.11.tar.gz
sudo rm -r apache-tomcat-8.5.11

Dann dem User pi die Rechte auf das Tomcat-Verzeichnis geben (Ihr könnt euch natürlich auch einen eigenen User tomcat anlegen):

sudo chown -R pi:pi /usr/share/tomcat/*

Nun noch fix die Umgebungs-Variable CATALINA_HOME in eurer bash-Property-Datei .bash_profile gesetzt:

cd
nano .bash_profile

Fügt dann im Editor diese Zeile ein:

export CATALINA_HOME=/usr/share/tomcat

Danach die SSH-Verbindung zum Raspberry PI neu aufbauen, damit die Umgebungs-Variable gesetzt wird. Den Tomcat könnt ihr nun mit diesem Befehl starten:

$CATALINA_HOME/bin/startup.sh

Nach dem Start, könnt ihr die Testseite des Tomcats in eurem Web-Browser aufrufen. Der Standard-Port des Tomcat ist wieder 8080:

http://yourraspberry:8080
Tomcat-Startseite

Tomcat-Startseite

Damit es keine Port-Konflikte gibt, konfigurieren wir den Port des Tomcats gleich mal auf den neuen Wert 8280. Stopp dazu den Tomcat mit dem Befehl:

$CATALINA_HOME/bin/shutdown.sh

Der Port für den Tomcat wird in der Datei server.xml im Tomcat-Home-Verzeichnis unter conf angepasst:

nano $CATALINA_HOME/conf/server.xml

Navigiert dann im Editor zu diesem Abschnitt:

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

Ersetzt dort 8080 mit 8280, speichert die Änderung und startet den Tomcat neu. Der Tomcat sollte nun mit dem Port 8280 erreichbar sein.

Jetzt könnt ihr schon mal testen, ob sich eure Spring Boot Anwendung im Tomcat deployen lässt:

  • Stoppt dazu den Tomcat mit $CATALINA_HOME/shutdown.sh
  • Kopiert das von Gradle erzeugte WAR- File, z.B. mit einem SFTP-Client wie FileZilla, in das Tomcat-Deploy-Verzeichnis $CATALINA_HOME/webapps
  • Startet den Tomcat mit $CATALINA_HOME/startup.sh

Beim starten entpackt Tomcat das WAR-File in ein Verzeichnis mit dem Namen des WAR-Files. Die Spring Boot Anwendung solltet ihr nun im Web-Browser aufrufen können:

http://yourraspberry:8280/warfilename

Deployment Plugin installieren

Um die Spring Boot Anwendung automatisch auf dem installierten Tomcat zu deployen, verwenden wir z.B. das Plugin Deploy to Container. Dieses Plugin verwendet die Tomcat Management App, um die Anwendung zu deployen.

Zunächst installieren wir das Plugin im Jenkins unter Jenkins verwalten -> Plugins verwalten. Sucht nach dem Plugin Deploy to Container und installiert das Plugin. Nach der Installation müsst ihr den Jenkins Server neu starten.

Deployment Plugin

Jenkins Deployment Plugin

Damit das Plugin auf die Tomcat Manager – App zugreifen kann, um die Spring Boot Anwendung zu deployen, müsst ihr noch einen Tomcat – Benutzer konfigurieren, der auf die Manager-App zugreifen darf.

Fügt dazu in die Datei $CATALINA_HOME/conf/tomcat-users.xml Rollen und einen entsprechenden Benutzer ein:

nano $CATALINA_HOME/conf/tomcat-users.xml

Fügt dann folgende Rollen und den Tomcat-User in die Datei ein:

<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="manager-status"/>
<role rolename="manager-jmx"/>
<user username="yourusername" password="yourpassword" 
      roles="manager-gui,manager-jmx,manager-status,manager-script"/>

Wechselt dann in der Jenkins-Oberfläche in Meine Ansichten -> yourProject -> Konfigurieren und fügt eine neue Post-Build-Aktion hinzu. Wählt hier diesmal Deploy war/ear to container.

Post-Build Deployment

Post-Build Deployment

Im letzten Schritt, müsst ihr dem Deployment – Plugin angeben, welchen Servlet-Container ihr verwendet und wie er aufgerufen wird. Wählt in der Auswahl für Add Container den Tomcat 7.x (Hat bei mir problemlos mit dem Tomcat 8 funktioniert).

Danach müsst ihr noch folgende Parameter konfigurieren:

  • WAR/EAR-Files: Verzeichnis, in dem Jenkins euer WAR-File finden kann. Im Workspace eures Jenkins-Build-Jobs: **/build/libs/*.war
  • Context-Path: Der Context-Path eurer Spring Boot Applikation
  • Manager-Username: Username, den ihr in der tomcat-users.xml angelegt habt
  • Manager-Password: Passwort für den Tomcat-User
  • Tomcat-URL: URL unter der euer Tomcat erreichbar ist. Z.B.: http://yourraspberry:8280
Deployment-Plugin Konfiguration

Deployment-Plugin Konfiguration

Speichert die Einstellungen, startet den Tomcat und startet danach den Build mit Jetzt bauen. Der Build-Job sollte am Ende die Spring Boot Anwendung im Tomcat deployen. Den Prozess, könnt ihr wieder in der Jenkins-Console verfolgen.

Tomcat-Deployment

Tomcat-Deployment

Danach solltet ihr eure Applikation im Web-Browser aufrufen können:

http://yourraspberry:8280/yourcontextpath

Tomcat-Manager-Konsole

Über die Tomcat-Manager-App könnt ihr z.B. die Applikationen, die auf eurem Tomcat laufen verwalten, z.B:

  • Anschauen welche Applikationen aktuell deployt sind und laufen
  • Beenden und Starten der Applikationen

Die Tomcat-Manager-App ist standardmäßig nur auf dem Rechner aufrufbar, auf den auch der Tomcat installiert ist, also localhost oder 127.0.0.1. Falls ihr die Tomcat-Manager-App auch von einem anderen Client über den Web-Browser aufrufen wollt, müsst ihr die IP-Adresse eures Client-Rechners freischalten.

Ruft dazu die Datei context.xml im Verzeichnis $CATALINA_HOME/webapps/manager/META-INF im Editor auf und fügt die IP-Adresse eures Client-Rechners hinzu.Z.B.:

<Context antiResourceLocking="false" privileged="true" >
  <Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="192\.168\.2\.\d+|127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
</Context>

Danach solltet ihr die Tomcat-Manager-App in eurem Web-Browser auf dem Client-Rechner aufrufen können. Am einfachsten geht das auf der Home-Seite des Tomcats (http://yourraspberry:8280) über den Button Manager App.

Als Username und Passwort gebt ihr die Daten eures Users an, den ihr für das Deployment-Plugin in der Datei tomcat-users.xml konfiguriert habt.