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