Einleitung

Play ist ein Vertreter der HTTP-nahen, requestbasierten Webframeworks. In Gegensatz zu komponentenbasierten Frameworks (wie z.B. JSF) versucht es nicht, den HTTP Requests- und Responsezyklus hinter einer Abstraktion zu verstecken. Es gibt keinen Automatismus, der z.B. den Zustand von Formularen serverseitig vorhält.

Durch den Verzicht auf serverseitigen Zustand lassen sich solche Applikationen leicht horizontal skalieren. Es liegt also nahe, sie in einer elastischen Umgebung zu betreiben, die in der Lage ist, je nach Last Server hinzuzufügen und zu entfernen. In diesem Artikel verwende ich Amazons Elastic Beanstalk, um auf einfache Weise eine Play-Anwendung hochverfügbar zu deployen.

Docker

Docker ist eine relative junge Open Source Software, die Anwendungen in Container verpackt. Solche Container enthalten alle Abhängigkeiten der Anwendung. Docker ist dabei einerseits eine leichtgewichtige Alternative zur Virtualisierung: Jeder Container führt im wesentlichen nur einen Prozess aus. Die Isolation wird durch CGroups, Namespaces und das Overlay Dateisystem AuFS erreicht. Die andere Leistung von Docker besteht in der Standarisierung des Containers. Einmal erstellte Container können ohne Veränderung auf dem Entwicklerrechner genauso wie in verschiedenen virtuellen oder Cloudumgebungen laufen.

Beanstalk

Elastic Beanstalk (EB) ist ein “Platform as a Service”-Angebot von Amazon. Als Abstraktionsschicht oberhalb von EC2, S3 und anderen Amazon Web Services vereinfacht es das Deployment von Webanwendungen. Beanstalk ermöglicht für diese Anwendungen automatisiertes Deployment und automatische Skalierung.

Wie für PaaS üblich, konnte Beanstalk ursprünglich nur sehr spezifische Applikationsformate deployen: Neben WAR-Files auf einen Tomcat waren das Ruby, PHP und Python, .Net und später Node.js Anwendungen. Seit April 2014 können auch Docker Container verwendet werden. Daher kann man nun jede Webanwendung deployen, für die man einen Docker Container bereitstellt. Das macht auch den Weg für Play auf diese Plattform frei.

Dabei führt Beanstalks allerdings nur einen Docker Container auf jeder EC2 Instanz aus. Die Möglichkeit, mit mehreren Docker Containern auf einer Instanz vorhanden Resourcen besser auszunutzen, wird daher nicht ausgereizt.

Docker Container in Beanstalk deployen

Es gibt drei Möglichkeiten, einen Docker Container in elastic Beanstalks zu deployen.

Aus einem Dockerfile

Falls ein standard Dockerfile genügt, um den Container zu erstellen, kann dieses direkt verwendet werden. Das Dockerfile wird dazu über die Weboberfläche oder ein Kommandozeilenzool zu Beanstalk hochgeladen. EB baut aus dem Dockerfile einen Container und verwendet automatisch den ersten veröffentlichten (“Exposed”) Port des Containers, um Http-Requests zu bearbeiten.

Sobald aber auf ein privates Docker-Repository zurückgegriffen werden soll, zum Bau des Images lokale Dateien nötig sind oder bestimmte Volumes gemounted werden sollen, funktioniert das nicht mehr. In der Dokumentation bei Amazon gibt es ein Beispiel Dockerfile, das das Spiel “2048”" als Webanwendung deployed.

Verwendung eines Dockerrun.aws.json

Bei dieser Variante wir eine AWS spezifische JSON-Datei verwendet. EB wertet es aus und lädt das darin referenzierte Image aus einer Docker Registry. Diese Registry kann private sein, in diesem Fall müssen Authorisierungsinformationen in einem verlinktem S3 Objekt abgelegt werden. Die JSON-Datei erlaubt darüberhinaus Einfluss auf die gemappten Ports und Volumes.

ZIP

Für diese mächtigste Option verpackt man das Dockerfile und optional eine Dockerrun.aws.json Datei und beliebige statische Dateien (den “Docker Context”) in ein Zip-Archiv. Beanstalk entpackt das Archiv und baut das Dockerfile im resultierenden Verzeichnis.

Ich verwende in diesem Artikel die Zip-Variante, weil sie den Charme hat, in sich abgeschlossen zu sein. Für das Erstellen des Archivs ist kein Dockerhost notwendig und es müssen auch keine Abhängigkeiten in externen Repositories vorgehalten werden. Daher läßt sich dieser Prozess auch sehr einfach an einen CI-Server übertragen.

Leider läßt sich das so erstellte Zip nicht mehr direkt lokal mit Docker starten, sondern muss erst entpackt werden.

Die Play-Anwendung verpacken

Als Beispielanwendung verwende ich einfach das existierende play-scala template. Die Anwendung generiere ich mit

activator new play-eb play-scala

Play kann von sich aus ein Archiv mit allen Abhängigkeiten erstellen:

cd play-eb
activator dist

Das fertige Zip-File liegt in target/universal/play-eb-1.0-SNAPSHOT.zip

Elastic Beanstalk erwarte das Dockerfile im root-Verzeichnis des Zip. Um das zu erreichen, legen wir es z.B. im Root des Projektes ab und fügen es mit

zip target/universal/play-eb-1.0-SNAPSHOT.zip  Dockerfile

hinzu.

Hier ist das eigentliche Dockerfile (Eine Einführung in Docker gibt es zum Beispiel in Micro Service in Java):

FROM java:7
MAINTAINER Martin Eigenbrodt <[email protected]>
COPY play-eb-1.0-SNAPSHOT /opt/play-eb
RUN groupadd -r play -g 433 && \
useradd -u 431 -r -g play -d /opt/play-eb -s /sbin/nologin -c "Play user" play && \
chown -R play:play /opt/play-eb
USER play-eb

EXPOSE 9000
CMD ["-Dhttp.address=0.0.0.0", "-J-Xms128M", "-J-Xmx256m"]
ENTRYPOINT ["/opt/play-eb/bin/play-eb"]

Es enthält wenig Überraschungen. Der von Play erstellte Inhalt wird nach /opt/play-eb kopiert und zum Starten das darin enthaltene Script ausgeführt.

Erstellen einer Beanstalk Application

Nach der Anmeldung auf https://eu-central-1.console.aws.amazon.com/elasticbeanstalk/home kann durch Klicken auf “Launch Now” und die Wahl von Docker eine neue Beanstalk Anwendung angelegt werden.

Neue Anwendung anlegen
Neue Anwendung anlegen

Nach dem diese bereit ist, lade ich über “Upload and Deploy” das Zip (../target/universal/play-eb-1.0-SNAPSHOT.zip) hoch.

Deploy
Deploy

Nach einiger Zeit ist die Anwendung deployed und kann unter der im Dashboard verlinkten URL (http://default-environment-XXXXXXX.elasticbeanstalk.com/) aufgerufen werden.

Autoscaling

Elastic Beanstalk hat automatisch eine EC2 instanz mit dem Docker Container deployed. Darüber hinaus wurde ein Elastic-Loadbalancer eingerichtet, der in der Lage ist, eingehende Aufrufe auf mehrere Instanzen zu verteilen. In der Konfiguration der Applikation kann gewählt werden, anhand welcher Metrik neue Instanzen hinzugefügt und wieder entfernt werden sollen. Neue Instanzen werden automatisch im Loadbalancer registriert.

Fazit

Mit Amazons Elastics Beanstalk & Docker lassen sich Play-Anwendungen sehr schnell deployen. Aber es gibt auch einige Einschränkungen:

Leaky Abstractions

EB verwendet unter der Haub eine ganze Reihe von AWS Services. In diesem Artikel haben wir ec2 Instanzen und den Elastic Loadbalancer gesehen. In der Praxis sind das schnell noch mehr, z.B. S3 für Deployments, CloudWatch für Metriken / Alarme, Security Groups. Für einen sicheren und performanten Betrieb der Anwendung wollen all diese Technologien verstanden sein.

Docker

Docker wird hier mit dem Modell “Ein Container pro Server” verwendet, was sehr untypisch ist. Darüber hinaus verhindert das in diesem Artikel gewählte Deployment-Model mit einem Dockerfile in einem Zip das Realisieren von “Write once run everywhere”.

Ein idomatischere Unterstüzung von Docker findet man möglicherweise im recht jungen Amazon EC2 Container Service

Automatische Skalierung

Die automatische Skalierung kann lediglich eine Metrik betrachten. Ein kombinierte Metrik von z.B. CPU und Netwerkverkehr ist nicht möglich.