Suche
Ein Praxisbericht

Operation am offenen Herzen: JEE-7-Migration im laufenden Betrieb

Dirk Ehms

©Shutterstock/Suzanne Tucker

Wie kann die Umstellung einer hochverfügbaren Live-Plattform mit mehr als 100 Application-Servern von JEE 6 auf JEE 7 ohne Ausfallzeiten gelingen? Und wie wird Continuous Delivery während der gesamten Projektlaufzeit unterstützt, wenn 70 Entwickler gleichzeitig Änderungen an der Codebasis durchführen? Basierend auf einem Projekt bei GameDuell beschreibt dieser Praxisbericht die technischen Maßnahmen und Anpassungen des Build- und Release-Prozesses, um die gestellten Anforderungen zu erfüllen und die Migration von 300 Maven-Modulen erfolgreich abzuschließen.

In einem Markt mit hohem Wettbewerbsdruck, wie dem für Online-Spiele, ist es für Unternehmen überlebenswichtig, technologisch Schritt zu halten. Aus diesem Grund hat GameDuell über mehrere Monate hinweg seine gesamte Spiele-Plattform von GlassFish-3 auf GlassFish-4 umgestellt.

Projektanforderungen

Das Besondere an diesem Migrationsprojekt waren vor allem die Randbedingungen, unter denen es durchgeführt werden musste. Zum einen sollte die Weiterentwicklung des bestehenden Systems von der Migration weitgehend unbeeinflusst bleiben. Dies war insbesondere deshalb eine Herausforderung, weil 70 Softwareentwickler parallel zum Projekt in der Lage sein mussten, kontinuierlich Erweiterungen am laufenden System durchzuführen. Das bedeutete, dass alle Änderungen des Migrationsprojekts am Quelltext über die gesamte Projektlaufzeit vollständig abwärtskompatibel implementiert werden mussten, um den Betrieb der Live-Plattform nicht zu beeinträchtigen.

Weiterhin musste die Umstellung auf den neuen Application-Server ohne Downtimes des hochverfügbaren Live-Systems durchgeführt werden.

Plattform-Topologie

Abbildung 1 zeigt eine Übersicht der zu migrierenden Live-Plattform. Das gesamte System teilt sich in zwei weitgehend unabhängige Bereiche. Auf der linken Seite befindet sich die sogenannte Classic Plattform. Diese hat sich aus dem ursprünglichen Geschäftmodell des Unternehmens entwickelt und bietet dem Anwender die Möglichkeit um echtes Geld zu spielen. Der Zugriff auf das System erfolgt mit einem Standard-Internet-Browser. Durch den Einsatz von HTML5 ist die Nutzung der Plattform auch mit mobilen Endgeräten möglich. Über einen Loadbalancer verbinden sich die Clients auf einen Server aus dem Frontend Server Pool. Alle Server verwenden GlassFish als Laufzeitumgebung für die unterschiedlichen JEE-Applikationen von GameDuell.

Abb. 1: GameDuell Plattform

Abb. 1: GameDuell Plattform

Auf der rechten Seite ist die Social/Mobile Plattform abgebildet. Diese unterscheidet sich vor allem durch eine starke Kopplung an Facebook, wobei der Zugriff auch über einen Guest-Account möglich ist. Die mobilen Endgeräte dieser Plattform verwenden native Apps, welche für die Betriebssysteme iOS und Android zur Verfügung stehen. Zusätzlich kommen noch Flash-Clients zum Einsatz.
Zur Datenspeicherung kommen sowohl relationale (SQL) als auch NoSQL-Datenbanken zum Einsatz. Anwenderdaten werden in einem LDAP Cluster abgelegt. Die Frontends greifen auf die Datenbanken überwiegend lesend zu. Für den schreibenden Datenzugriff werden sogenannte Persister-Server eingesetzt, die auch GlassFish als Application-Server verwenden. Die Kommunikation zwischen Frontends und Persisters wird durch Java Messaging (JMS) realisiert. Für administrative Zwecke und das Reporting sind noch weitere Server im Einsatz.

Maven Dependency Management und Profile

Der Quelltext des Systems ist in über 300 Maven-Modulen organisiert, die wiederum in 10 unterschiedlichen Deployment-Artefakten gebündelt sind. Dabei handelt es sich sowohl um Enterprise-Archive als auch um Web-Archive. Alle Maven-Module verwenden einen gemeinsamen Parent-POM (Project Object Model) wie in Listing 1 dargestellt.

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

Durch den Einsatz eines Parent-POMs können modulübergreifende Konfigurationen und die zu verwendenden Versionen von Dependencies leicht angepasst werden. Die Entscheidung darüber, welche Version der javax:javaee-api-Bibliothek beim Build verwendet werden soll, wird in die Maven-Profile jee6 und jee7 ausgelagert (Listing 2).

    <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>
                        <scope>provided</scope>
                    </dependency>
                </dependencies>
            </dependencyManagement>
        </profile>
        <profile>
            <id>jee7</id>
            <dependencyManagement>
                <dependencies>
                    <dependency>
                        <groupId>javax</groupId>
                        <artifactId>javaee-api</artifactId>
                        <version>7.0</version>
                        <scope>provided</scope>
                    </dependency>
                </dependencies>
            </dependencyManagement>
        </profile>

Die standardmäßige Aktivierung des Maven-Profils jee6 erlaubt es allen Entwicklern, ihre Maven Builds wie bisher durchzuführen. Die Verwendung des optionalen Parameters –P jee7 aktiviert das Maven-Profil jee7 und ermöglichte es den Teammitgliedern des Migrationsprojekts, vorhandene Kompatibilitätsprobleme in den einzelnen Maven-Modulen zu erkennen und aufzulösen (Listing 3).

$ mvn clean install

$ mvn clean install -P jee7

Continuous Integration

Abbildung 2 stellt die einzelnen Schritte der Implementierung von Continuous Integration bei GameDuell dar.

Abb. 2: Continuous Integration

Abb. 2: Continuous Integration

 

  1. Der Softwareentwickler checkt seine Änderungen im Versionskontrollsystem ein. Die Commit-Message enthält die ID des JIRA-Tickets, das die Grundlage für die durchgeführten Änderungen an der Codebasis waren. Durch das Anfügen von //patch, //minor oder //major besteht die Möglichkeit Jenkins mitzuteilen, dass eine neue Version des Artefakts erstellt werden soll:
    $ svn commit –m “XYZ-4711, … //minor”
  2. Durch einen Post Commit Hook wird der dem Maven-Modul zugeordnete Jenkins Job angestoßen und ein vollständiger Build mit allen zugehörigen Tests des Moduls durchgeführt.
  3. Versionierte Artefakte werden in das firmeneigene Artefakt-Repository übertragen.
  4. Jenkins vermerkt die Erstellung eines neuen Artefakts als Kommentar im zugehörigen JIRA-Ticket.
  5. Jenkins versendet eine E-Mail über den Erfolg oder Misserfolg der Build-Ausführung an den verantwortlichen Softwareentwickler.
  6. Der Softwareentwickler aktualisiert den Status des JIRA-Tickets auf integrated und signalisiert damit, dass die durchgeführten Änderungen beim nächsten Deploy auf die Live-Plattform übernommen werden können.

Versionskontrolle mit Branches

Für die normale Entwicklung gilt die Vorgabe, dass alle Änderungen direkt auf dem Trunk durchgeführt werden. Allerdings sind nicht alle für die Migration notwendigen Anpassungen abwärtskompatibel möglich. In solchen Fällen muss auf Branches der Versionskontrolle zurückgegriffen werden. Auf diese Weise können alle Änderungen unabhängig von der aktiven Produktentwicklung durchgeführt werden. Die einzelnen Teilschritte sind in Abb. 3 dargestellt.

Abb. 3: Änderungen verwalten

Abb. 3: Änderungen verwalten

 

  1. Anlegen eines Branches, wobei ein getrennter Bereich für die notwendigen JEE-7-Anpassungen entsteht.
  2. Alle notwendigen Anpassungen durchführen, um das zugehörige Maven-Modul JEE-7-kompatibel zu gestalten.
  3. Auf dem Trunk erfolgt die Weiterentwicklung wie bisher für JEE 6.
  4. Bei jedem Check-in auf dem Trunk müssen alle Änderungen automatisch in den JEE 7 Branch zugeführt werden.

Rückblick: Der Umgang mit nicht-kompatiblen Branches und den daraus erzeugten Maven-Artefakten, stellte eine große Herausforderung bei der Integration in den bestehenden Release-Prozess dar. Unser erster Lösungsansatz war, die Deployment-Artefakte für den JEE-7 Application Server für eine kurze Übergangszeit mit SNAPSHOT-Versionen zu bestücken. Allerdings sprach sich das Operations-Team strikt gegen diesen Ansatz aus. Der primäre Grund hierfür war, dass dieses Vorgehen keine eindeutige Zuordnung des in der Produktion befindlichen Softwarestandes mit denen in der Versionskontrolle zulässt. Schließlich fanden wir mit dem Maven Classifier eine Lösung, die alle Beteiligten zufriedenstellte.

Maven Classifier

Die Unterstützung von Continuous Integration für JEE-7 und die Verwendung von Branches erfordert eine Erweiterung der vorhandenen Jenkins-Konfigurationen (Abb. 4).

Abb. 4: Sekundäre Jenkins Jobs konfigurieren

Abb. 4: Sekundäre Jenkins Jobs konfigurieren

  1. Alle bereits vorhandenen Jenkins-Jobs (Primär-Jobs) werden dupliziert. Die neuen Jobs (Sekundär-Jobs) werden so konfiguriert, dass das Maven-Profil jee7 bei der Ausführung verwendet wird (Listing 3). Bei Maven-Modulen, die aus Kompatibilitätsgründen einen Branch benötigen, muss zusätzlich die URL zur Versionskontrolle angepasst werden.
  2. Nach der erfolgreichen Ausführung eines Primär-Jobs werden alle Codeänderungen des betreffenden Maven-Moduls vom Trunk in den Branch überführt (Abb. 3).
  3. Der Sekundär-Job wird als Nachfolger in Jenkins gestartet. Nichtkompatible JEE-7-Artefakte werden zur eindeutigen Kennzeichnung mit einem Maven Classifier versehen.
  4. Gegebenenfalls auftretende Merge-Konflikte müssen manuell aufgelöst werden. Nach der Konfliktlösung werden die durchgeführten Änderungen im Branch eingecheckt und der erfolglose Jenkins-Jobs neu gestartet.

Die unter Punkt 3. genannte Verwendung von Maven Classifiers sind ein essentieller Bestandteil des in diesem Artikel vorgestellten Migrationskonzepts. Durch diesen Ansatz können Artefakte für unterschiedliche JEE-Zielumgebungen, bei identischen Artefaktnamen und Versionsnummern, erzeugt und verwendet werden. Die hierfür notwendige Konfiguration innerhalb einer POM-Datei ist exemplarisch in Listing 4 dargestellt.

 
    </project> 
    ... 
    <profiles> 
        <profile> 
            <id>jee6</id> 
            <dependencies> 
                <dependency> 
                    <groupId>de.gameduell.social</groupId> 
                    <artifactId>social-services-jpa</artifactId> 
                    <version>2.9.1</version> 
                </dependency> 
            </dependencies> 
        </profile> 
        <profile> 
            <id>jee7</id> 
            <dependencies> 
                <dependency> 
                    <groupId>de.gameduell.social</groupId> 
                    <artifactId>social-services-jpa</artifactId> 
                    <version>2.9.1</version> 
                    <classifier>jee7</classifier> 
                </dependency> 
            </dependencies> 
        </profile> 
    </profiles>
    ...

Continuous Delivery

Zu jeder vollen Stunde wird die Release Pipeline gestartet, um alle Release-fähigen Änderungen für das Live Deployment vorzubereiten. Zur Identifikation der Änderungen, die in ein neues Release einfließen sollen, und zur Aktualisierung von Abhängigkeiten wird ein speziell entwickeltes Skript verwendet, das den Namen Assembly Monkey trägt.

Abb. 5: Continuous Delivery

Abb. 5: Continuous Delivery

 

  1. Start des Assembly Monkeys zur vollen Stunde.
  2. Suchen aller JIRA-Tickets mit dem Status integrated inklusive der Ermittelung zugehöriger neu versionierter Artefakte.
  3. Anpassung der Versionen von Dependencies aller Deployment-Artefakte und anschließender Check-in der aktualisierten POM-Dateien.
  4. Der Post Commit Hook der Versionskontrolle startet die Jenkins-Jobs aller Deployment-Artefakte mit geänderten POM-Dateien.
  5. Versionierte Deployment-Artefakte werden in das firmeneigene Artefakt-Repository übertragen.
  6. Deployment-Artefakte werden auf einer Pre-Production Stage installiert und gegen verschiedene User-Acceptance-Tests geprüft. Bei erfolgreicher Testausführung werden alle JIRA-Tickets mit dem Status integrated nach build stable überführt.

Zero Downtime Deployment

Ein- bis zweimal täglich werden alle integrierten Codeänderungen auf der GameDuell Live-Plattform online gestellt, wobei die hundertprozentige Verfügbarkeit des Systems oberstes Ziel ist. Abb. 6 zeigt den Ablauf eines Deployments.

Abb. 6: Deployment auf der Live-Plattform

Abb. 6: Deployment auf der Live-Plattform

 

  1. Ein Teil der Frontend-Server wird vom Operations-Team in den Standby-Modus geschaltet. Alle laufenden Spiele können noch beendet werden. Neue Anfragen werden auf aktive Frontends weitergeleitet.
  2. Das initiale Deployment erfolgt auf einem einzelnen Frontend, auf dem sich nun keine aktiven Benutzer mehr befinden.
  3. Eine weitere Sammlung von automatischen User-Acceptance-Tests wird gegen das einzelne Frontend gestartet.
  4. Ein Mitglied des QA-Teams sichtet alle in das Deployment eingeflossenen JIRA-Tickets und führt zusätzliche manuelle Tests durch. Bei erfolgreichem Test wird der Status der Tickets entsprechend fortgeschrieben.
  5. Das einzelne Frontend wird wieder in die Verteilung durch den Loadbalancer genommen, wodurch „echte“ User auf den Server zugreifen können. Für die nächsten 15-20 Minuten erfolgt ein verstärktes Monitoring des Frontends, um evtl. auftretende Anomalien zu erkennen.
  6. Wenn das Frontend ohne Auffälligkeiten funktioniert, wird der Rollout auf alle weiteren im Standby laufenden Frontends ausgeführt.
  7. Abschließend werden alle noch nicht aktualisierten Frontends sukzessive in den Standby-Mode gesetzt, aktualisiert und anschließend wieder in die Verteilung genommen.

Unter Anwendung des beschriebenen Prozesses können einzelne Server-Instanzen alternativ auch mit einem JEE-7-kompatiblen Application Server betrieben werden. Dieser Ansatz minimiert das Risiko, da nicht alle Server auf einmal umgestellt werden.
Rückblick: Im Projekt wurde der Parallelbetrieb über einen längeren Zeitraum aufrechterhalten, wobei dem Migrationsteam vor allem ein massiv erhöhter Speicherbedarf des GlassFish 4 zu schaffen machte. Die Ursache hierfür lag in einer fehlerbehafteten Weld-Implementierung und der standardmäßigen Aktivierung von CDI bei Verwendung von JEE 7. Ein Upgrade auf eine neuere Version schaffte Abhilfe.
Einen wichtigen Beitrag bei der Fehlersuche und dem Monitoring leistete Java VisualVM. Mit diesem Tool wurden Speicherverbrauch, CPU-Nutzung, Anzahl der Threads und das Verhalten des Garbage Collectors während der Umstellungsphase überwacht.

Aufräumarbeiten

Nach der vollständigen Migration der Live-Plattform zu JEE 7 heißt es nun: Aufzuräumen. Alle angelegten Branches müssen wieder in den Trunk integriert werden, da die Weiterentwicklung für JEE 7 ab diesem Zeitpunkt ausschließlich auf dem Trunk erfolgt (Abb. 7). Die Branches können anschließend gelöscht oder stillgelegt werden.

Abb. 7: Branches der Versionskontrolle integrieren

Abb. 7: Branches der Versionskontrolle integrieren

Im nächsten Schritt werden die nicht länger benötigten Maven-Profile aus den POM-Dateien entfernt, um auf diese Weise die Komplexität zu verringern. Außerdem wird Jenkins in seinen ursprünglichen Zustand zurückgesetzt, indem alle sekundären Jenkins-Jobs gelöscht werden.

Fazit

Das in diesem Artikel beschriebene Vorgehen ist sicher nicht die einzige Lösung, um unter den gegebenen Randbedingungen die Migration von JEE 6 auf JEE 7 erfolgreich abzuschließen. Allerdings überzeugt es durch seine unkomplizierte Struktur und kann ggf. mit kleinen Anpassungen auch in anderen Bereichen angewendet werden. Es bietet somit einen guten Ansatz zur Durchführung von Migrationsprojekten im Umfeld hochverfügbarer Systeme und der Verwendung von Continuous Delivery.
Nach dem Abschluss der Migration und dem Einsatz des neuen Application-Servers können künftige Systementwicklungen von den erweiterten Möglichkeiten von JEE 7 profitieren. Als zusätzlicher Bonus ist der Weg, sehr zur Freude aller GameDuell-Entwickler, für die Umstellung auf JDK 8 bereitet. Doch das ist eine andere Geschichte …

Aufmacherbild: Hands holding paper heart von Shutterstock / Urheberrecht:  Suzanne Tucker

Verwandte Themen:

Geschrieben von
Dirk Ehms
Dirk Ehms
Dirk Ehms besitzt mehr als 20 Jahre Erfahrung in der Software-Entwicklung. Er ist spezialisiert auf den Entwurf und die Implementierung von Java-Enterprise Softwaresystemen. Als Software-Architekt von GameDuell widmet er sich der Erweiterung und Optimierung der modularen Spiele-Plattform auf Basis von JEE sowie zugehöriger agiler Entwicklungsprozesse wie Continuous Delivery und testzentrierten Ansätzen.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.