Schlagwörter

, , , ,

Continuous Delivery (CD) tritt an uns die Angst vor Releases zu nehmen. Die von CD gewählte Therapieform ist die Konfrontationstherapie: Wir release so oft bis es uns keine Angst mehr macht. Der Angsthemmer, den uns CD dabei an die Hand gibt heißt „Automatisierung“:

  • Automatisiertes Bauen
  • Automatisierte Deployments
  • Automatisierte Tests
  • Automatisiertes Konfigurationsmanagement

In diesem Post will ich mich den ersten beiden Punkten widmen.

Automatisiertes Bauen

Das automatisierte Bauen ist eine der am besten verstandenen Disziplinen, da Continuous Integration (CI) inzwischen weit verbreitet ist und viel Vorarbeit geleistet hat. Eine Besonderheit ergibt sich allerdings bei CD in Kombination mit Maven:

Wenn wir uns die Kernaussage von Continuous Delivery „Jedes Build ist ein potentielles Release“ anschauen, ergibt sich daraus, dass jedes Build ein Release sein sollte, und eben kein SNAPSHOT. Das ist schon mal die erste Herausforderung in Maven, da dieses Tool darauf ausgelegt ist so lange Snapshots rauszuhauen, bis sich jemand entschließt manuell (und seis per Release-Plugin) die Versionsnummer hochzuzählen.

Es gibt aber einen Trick (kurz vor Hack), mit dem wir Maven helfen seine Versionsnummern dynamisch zu vergeben: Wir müssen die Versoinsnummer einfach als Property definieren und dann beim Build überschreiben.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.mibutec</groupId>
  <artifactId>CiApp</artifactId>
  <version>${ciVersion}</version>

  <properties>
    <ciVersion>0.1-SNAPSHOT</ciVersion>
  </properties>
</project>

Der Aufruf erfolgt dann über

mvn deploy -DciVersion=42

Durch diese POM in Kombination mit dem gezeigten Aufruf wird in unserem Release-Repository ein neues org.mibutec.CiApp Artefact in der Version 42 abgelegt. Dass die Versionsnummer nun für kein anderes Build mehr verwendet werden kann und wir einen Automatismus brauchen, der uns eine Versionsnummer generiert, ist klar, aber letztlich auch gewollt. Schließlich müssen wir in folgenden Prozessschritten auf diese Versionsnummer zugreifen können.

Das Setzen der richtigen Versionsnummer ist Aufgabe der CI-Umgebung. Im einfachsten Fall nimmt man als Versionsnummer die aktuelle Buildnummer.

ScreenHunter_05 Mar. 14 09.14

Automatisierte Deployments

Nachdem die Applikation gebaut wurde, muss diese auch noch durch die verschiedenen Testinstanzen gestagt werden. Welche und wie viele es sind hängt von den individuellen Testprozessen des Projekte ab. In der Minimalform sollten die folgenden aber nicht fehlen:

  • Integration: Auf dieser Instanz wird aus den vielen Einzelmodulen ein großes Ganzes gebaut. Auf dieser Instanz findet eine Reihe von Integrationstests auf Basis von Testdaten statt.
  • QA: Auf dieser Instanz finden die automatisierten Akzeptanztests statt, die auf echten Daten beruhen. Auch wenn es manuelle Tests theoretisch bei CI nicht mehr gibt, würden diese hier stattfinden.
  • Predprod: Unterschiedliche Stages sind in der Regel unterschiedlich konfiguriert, so werden an den vorher genannten Testsystemen externe Systeme auch nur als Testsystem angebunden. Die Preprod hat eine Live-Konfiguration. Auf dieser finden nur noch lesende Smoketests statt, die die Richtigkeit der Live-Konfiguration testen sollen.

Die Release Artefakte selbst können aus dem Maven Repository geladen werden, dort sind sie zentral per http abrufbar. Eine Deployment-Strategie, die sich in meinen Projekten bewährt hat, ist es die Systeme selbst wissen zu lassen wie die Software auf diese deployed wird. Hierzu haben die Systeme ein Shell-Skript als Schnittstelle. Dieses Skript liegt in allen Systemen an der gleichen Stelle, hat den gleichen Namen und bekommt als Parameter mindestens die Versionsnummer des zu deployenden Artefakts übergeben. Diese Skript lädt die Software aus dem Repository, entpackt sie, legt an die richtige Stelle, startet den Server durch, …

#!/bin/bash
version=${1}

curl --user nexusUser --location "http://nexus.mibutec.org/service/local/artifact/maven/redirect?r=public&g=com.testo&a=TestoApp&v=${version}&p=war" > mibutec.war
if [ $? != 0 ]; then
  echo "curl failed for to load artifact, exiting with status code 2"
  exit 2
fi

# stop Tomcat
sudo /etc/init.d/tomcat6 stop
if [ $? != 0 ]; then
  echo "Stopping Tomcat failed, exiting with status code 2"
  exit 2
fi

mv ${TOMCAT_HOME}/webapps/mibutec* archive/`date`/
mv mibutec.war ${TOMCAT_HOME}/webapps

# start tomcat
sudo /etc/init.d/tomcat6 start
waitForStartup

if [ `<code>curl -I http://localhost:8080/index.jsp`</code> != 200 ]; then
  echo "Sanitycheck failed"
  exit 2
fi

Die generierten Fehlercodes sind wichtig, da diese die aufrufende CI-Umgebung informieren, wenn beim Deployment etwas schief gegangen ist. Wichtig sind auch die Zeilen 17 und 24. Diese implementieren (wenn in diesem Beispiel auch in einer sehr primitiven Form):

  • in Zeile 17 eine Rollback-Möglichkeit: Wenn sich rausstellt, dass das Deployment nicht gekalppt hat, kann die alte Verson der Applikation aus dem Archiv geladen und deployed werden.
  • in Zeile 24 einen ersten Sanity-Check, der prüft, ob die Applikation überhaupt richtig deployed wurde und starten kann (prüfe auf returncode 200 beim Abruf der index.jsp)

Für den Aufruf dieses Skripts wird ein Job im Jenkins erstellt, der als Parameter die zu deployende Version und ggf. das Zielsystem übergeben bekommt. Diese nutzt er dann, um per ssh das Skript auf dem Zielsystem aufzurufen.

ScreenHunter_05 Mar. 14 09.26 ScreenHunter_05 Mar. 14 09.27

Release

Funktional gesehen ist das Release auch nur ein Staging-Schritt (wenn auch der wichtigste), und wird wie die oben beschriebenen Schritte gehandhabt. Spätestens an dieser Stelle reicht das einfache Skript von oben allein nicht mehr aus, da nun noch Erweiterungen rein müssen, die ein Deployment erlauben ohne dass es für den Kunden spürbar ist. Diese hängen aber ganz individuell von der Deployment-Stretegie der Applikation ab.

Fazit

Dies ist schon einer meiner längeren Blog-Einträge, und ich habe es trotzdem nicht geschafft alles zu erwähnen, was man beim Einsatz von CD beachten muss. Wie führe ich meine automatierten Abnahmetests automatisch aus? Wie richte ich den Server ein, damit er meine Software überhaupt verwenden kann? Wie mache ich aus meinen vielen Jenkins Einzeljobs ein Großes und Ganzes? Wie gehe ich mit Datenbank-Änderungen um? Was wird aus meinen nightly builds? …

Aber das schöne an CD ist, ist dass es keine Alles-oder-Nichts Technik ist. Man muss nicht komplett alles umsetzen, um Vorteile zu haben. Da es sich bei den meisten Aspekten nur um Automatisierungen handelt, hilft jeder einzelne Schritt schon für sich manuelle Arbeit und damit verbundene Fehler einzusparen.

Wenn das Bauen der Software aufwändig ist und Schmerzen bereitet, dann baue eine Automatisierung dafür.

Wenn das Kopieren der Binaries und Aufsetzen der Applikation auf einer Instanz aufwändig ist und Schmerzen bereitet, dann baue eine Automatisierung dafür.

Wenn das manuelle Testen der Applikation aufwändig ist und Schmerzen bereitet, dann erstelle automatisierte Abnahmetests.

Wenn das regelmäßige Ausführen und Auswerten der Tests aufwändig ist und Schmerzen bereitet, dann baue eine Automatisierung dafür.

Wenn das Updaten der Konfiguration der Server aufwändig ist und Schmerzen bereitet, dann baue eine Automatisierung dafür.

Ihr könnt euch sicher vorstellen, wie das jetzt weiter geht.

Der Begriff Continuous Delivery ist übrigens nicht geschützt. Jeder kann selber entscheiden ab welchem Grad der Autoamtisierung er CD erreicht hat 😉

Advertisements