Erfahrungsbericht: Migration eines hochverfügbaren Applikationsservers im Live-Betrieb

Architektur/Methoden  –  3 Kommentare

Die Migration ihres Applikationsservers stellt Java-Enterprise-Entwickler regelmäßig vor Herausforderungen. Glücklicherweise sind sie nur im Abstand einiger Jahre zu bewältigen.

Um den Entwicklungsaufwand gering zu halten, greifen viele Unternehmen für ihre Software auf Komponenten von Drittanbietern zurück. Ob Bibliothek, Webserver oder Anwendungsframework, spätestens beim nächsten Major Release stellt sich die Frage, ob sich Upgrade oder Migration lohnen.

Wie bei allen anderen Projekten sollte sich ein Unternehmen vor Beginn eines Migrationsprozesses über Kosten und Nutzen im Klaren sein, da die Arbeiten je nach Größe und Komplexität des Gesamtsystems relativ aufwendig werden können. Statt allerdings die Wirtschaftlichkeit der Maßnahme anhand des ROI (Return on Investment) zu beurteilen, ist es bei Migrationsprojekten häufig sinnvoller, über den Return on Non-Investment nachzudenken. Versperrt man sich dem Upgrade, könnte es auf Dauer schwer fallen, mit der technischen Entwicklung Schritt zu halten und im Wettbewerb zu bestehen.

2015 hat sich der Online-Game-Anbieter GameDuell für ein Update des GlassFish-Applikationsservers von Version 3.1 auf 4.1 entschieden und seine hochverfügbare Games-Plattform von JEE6 nach JEE7 migriert. Die Projektlaufzeit erstreckte sich über mehrere Monate und sollte insbesondere zwei zentrale Anforderungen erfüllen. Zunächst sollte die Weiterentwicklung des Live-Systems nicht beeinträchtigt werden, um die Kunden weiterhin mit neuen Features zu versorgen. Dem zu entsprechen ist nicht trivial, wenn 70 Softwareentwickler parallel zu den Migrationsarbeiten kontinuierlich die Codebasis ändern.

Alle im Rahmen der Migration durchgeführten Änderungen müssen vollständig abwärtskompatibel sein, um den Betrieb der Games-Plattform nicht zu gefährden. Diese Randbedingung leitet zur zweiten zentralen Anforderung über: Das Umstellen musste ohne Ausfallzeiten (Zero Downtime) der hochverfügbaren Plattform erfolgen. Der Grund hierfür ist einfach: Ist die Plattform nicht erreichbar, werden keine Umsätze generiert. Außerdem ist der Markt für Online-Games heiß umkämpft und ein Systemausfall könnte Kunden abwandern lassen.

Um den Anforderungen gerecht werden zu können, hat das Team zunächst einen Maßnahmenkatalog entwickelt. Bevor die Maßnahmen näher beschrieben werden, sind zum besseren Verständnis noch einige Erklärungen der Entwicklungsumgebung bei GameDuell nötig (siehe Abb. 1).

Während der Migration sind in allen Phasen des Entwicklungszyklus Modifikationen durchzuführen (Abb. 1).


Verteilt auf mehrere Teams führen 70 Entwickler kontinuierlich Änderungen am Code der Spieleplattform durch, die in über 300 Maven-Modulen organisiert ist. Letztere sind in zehn Deployment-Artefakten gebündelt, bei denen es sich sowohl um Web- (WAR) als auch um Enterprise-Archive (EAR) handelt. Ein eigenes, "Assembly Monkey" genanntes Tool erstellt die Artefakte einmal pro Stunde. Auf seine genaue Funktionsweise wird später noch eingegangen. Automatische Akzeptanztests validieren die Tauglichkeit der Deployment-Artefakte für ein Rollout, das ein bis zwei Mal pro Tag stattfindet.

Darüber hinaus kam einigen der in der Entwicklung verwendeten Werkzeugen eine wichtige Bedeutung bei der Migration zu. Als erstes ist Maven zu nennen, das durch sein integriertes Abhängigkeitsmanagement besonders hilfreich war. Zur Unterstützung von Continuous Integration und Continuous Delivery kommen mehrere Jenkins-Server zum Einsatz. Das Tracking von Zustandsübergängen bei der Feature-Implementierung erfolgte durch JIRA, und für die Versionskontrolle setzte das Team Subversion ein.

Idealerweise ließe sich eine Enterprise-Applikation einfach auf dem neuen Release des Applikationsservers installieren. In der Praxis funktioniert das jedoch meist nur mit vergleichsweise einfachen Anwendungen. Insbesondere ergeben sich Komplikationen in komplexen Projekten, die zum Umsetzen von Anwendungslogik auf proprietäre Erweiterungen zurückgreifen, die in späteren Implementierungen durch Standard-APIs ersetzt werden (z.B. JAX-RS) oder in neuen Versionen nicht mehr zur Verfügung stehen. Auch der Einsatz spezieller Erweiterungen des Applikationsservers kann bei einem Versionswechsel zu Problemen führen.

Aus dem Grund ist die Anwendung einer Kompatibilitätsprüfung zu unterziehen. Ist die Applikation modularisiert, bietet es sich an, zuerst die einzelnen Komponenten zu testen und erst im Anschluss die gesamte Anwendung. Im Folgenden wird die Prüfung auf der Basis eines einzelnen Maven-Moduls beschrieben.

Die zentrale Konfiguration der zu verwendenden Version der Java-Enterprise-API im Parent-POM der Module kann wie folgt aussehen:

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>de.gameduell</groupId>
<artifactId>jee-parent</artifactId>
<packaging>pom</packaging>
<version>1.4.0</version>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
</dependency>
</dependencies>
</dependencyManagement>
...
</project>

Da die POM-Datei die Angabe nun dem Elternelement übernimmt, ist die Versionsnummer der JavaEE-API nicht mehr zu definieren:

<project>
<parent>
<groupId>de.gameduell</groupId>
<artifactId>jee-parent</artifactId>
<version>1.4.0</version>
</parent>
...
<dependencies>
...
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

Damit die Konfiguration sowohl mit JEE6 als auch JEE7 umgehen kann, ist sie zu erweitern. Eine Option dafür sind Maven-Profile, in welche die bisher definierte Version der JavaEE-API verschoben wird. Das Profil erhält die ID jee6 und ist standardmäßig aktiviert:

<project>
...
<profiles>
<profile>
<id>jee6</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</profile>
<profile>
<id>jee7</id>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</profile>
...

Da es nun während des Build-Prozesses aktiv ist, ist die explizite Angabe des Profils zu dem Zeitpunkt nicht mehr notwendig. Damit lässt sich eine Beeinträchtigung der parallel laufenden Weiterentwicklung des Systems minimieren.

Sonderheft "Altlasten im Griff"

Mehr Artikel zum Thema Legacy-Code sind im Sonderheft iX Developer 01/2017 zu finden, dass sich unter anderem im heise Shop erwerben lässt.


Um beide Versionen abzudecken, ist ein zusätzliches Profil mit der ID jee7 vorgesehen. Seine Konfiguration erfolgt analog zum JEE6-Profil. Das Migrationsteam kann nun durch Angabe des Profils jee7 die Kompatibilität der einzelnen Maven-Module überprüfen:

$ mvn clean install -P jee7

Sollte die gewünschte Kompatibilität nicht gegeben sein, ist zu untersuchen, ob sich die relevanten Stellen im Code so anpassen lassen, dass abwärtskompatible Änderungen möglich sind. Maßnahmen zum Umgang von Maven-Modulen, bei denen das keine Option ist, behandelt der nächste Abschnitt.