Container-Deployment auf AWS

Eines der fantastischen Phänomene, welche die zunehmend Cloud-zentrierte IT-Welt in jüngerer Zeit maßgeblich beeinflusst, sind zweifelsohne die Amazon Web Services (AWS). Natürlich beschäftigen auch wir bei 7P uns entsprechend eifrig mit der Thematik und unseren Kunden bleibt die Popularität der AWS nicht verborgen. So ist es wenig verwunderlich, dass der Begriff und die Möglichkeit eines Einsatzes in der Amazon Cloud bei praktisch jedem neuen Projekt fällt bzw. diskutiert wird. Höchste Zeit uns der Frage auch hier im Blog zu widmen.
Als erster Artikel zum Thema AWS auf diesem Blog, richtet sich dieser eher an Leser, die wenig bis keine Vorerfahrung haben.

Offiziell an den Start gingen die Amazon Web Services bereits im Jahr 2006 und mittlerweile umfasst das Portfolio über 50 verschiedene Services, die mehr oder minder vollständig von verschiedenen Server-Farmen in 14 Regionen weltweit angeboten werden. Die geographisch nächstgelegene hiervon ist die EU-Central-1 mit Servern in Frankfurt. Mit der Beschreibung dieser Menge an Diensten ließen sich sicher unzählige Seiten füllen. An dieser Stelle wollen wir uns jedoch auf solche beschränken, die wir in der Business Area „Software Application Development“ typischerweise einsetzen.

Konkret geht es in unserem Beispiel um eine Middleware, die der Einfachheit halber auf eine eigene Datenhaltung verzichtet. Letzteres vereinfacht auch einen Einsatz der Anwendung in Docker Containern. Die Amazon Services, die für uns daher besondere Relevanz haben, sind Elastic Compute Cloud (EC2) und EC2 Container Service (ECS). Ersterer erlaubt es uns, Instanzen virtueller Maschinen anzufordern, deren Kosten ausschließlich von der tatsächlichen Laufzeit in Minuten abhängen. ECS bietet dazu die Tools, um Docker Container auf unseren EC2-Instanzen zu starten und im Cluster zu betreiben.

Docker

Bevor wir Docker Container erstellen können, benötigen wir ein Docker Image, das unsere Applikation enthält. Der Einfachheit halber lassen wir uns an dieser Stelle die Arbeit abnehmen ein eigenes Image mit einem korrekt konfigurierten Application Server zu bauen und bauen daher auf dem im Docker Hub verfügbaren Image payara/server-full:latest auf. Um unsere Applikation in ein neues Image zu integrieren müssen wir lediglich unser WAR-File ins Autodeploy-Verzeichnes des Payaras kopieren und sicherstellen, dass der Application Server beim Container-Start ebenfalls startet. Unser Dockerfile sieht entsprechend simpel aus:

FROM payara/server-full:latest
ADD my-app.war /opt/payara41/glassfish/domains/domain1/autodeploy/
CMD ["bin/asadmin", "start-domain", "-v"]

Bauen können wir das Image dann beispielsweise mit docker build –tag my-project/my-image:latest .

AWS CLI

Zur praktikableren Bedienung der Amazon Web Services und insbesondere, um unser gerade erstelltes Docker Image in die AWS Container Registry hochladen zu können, müssen wir das AWS Command Line Interface (CLI) installieren. Nach der Installation muss lediglich noch der Zugang zu unserem Account konfiguriert werden. Hierzu benötigen wir einen Access Key: In der AWS-Weboberfläche wählen wir den Identity an Access Management (IAM) Service und wählen einen User aus bzw erstellen einen mit den gewünschten Rechten. Im Reiter „Security Credentials“ können wir einen neuen Access Key erstellen. Nach den hier generierten Schlüsseln werden wir gefragt, wenn wir in unserer lokalen Konsole aws configure aufrufen. Als „Default Region name“ bietet sich „eu-central-1“ an. Das vorgeschlagene „Default output format“ json behalten wir bei.

ECS-Ressourcen

Als erste Ressource setzen wir nun einen ECS Cluster auf: Wir wählen aus der Services-Übersicht der AWS-Weboberfläche den EC2 Container Service. Da wir momentan noch keine ECS Ressourcen angelegt haben, leitet uns Amazon auf eine Übersichtsseite, die uns einen Überblick über die Funktionalität des Services vermitteln soll. Ein Klick auf „Get started“ leitet uns in einem Wizard durch die Erstellung eines Clusters und praktischerweise auch des benötigten Docker Image Repositories.

In Letzteres werden wir später das Docker Image unserer Applikation aus unserer Entwicklungsumgebung hochladen. Im ersten Schritt wählen wir also einen Namen für unser Repository. Hier gilt es zu beachten, dass ein AWS Image Repository für das Sammeln verschiedener Versionen nur eines Images gedacht ist. Es lassen sich innerhalb jedes Repos also Tags vergeben (z.B. „latest“) aber keine unterschiedlichen Namen. Entsprechend sollte der Repository-Name eine Kombination aus Namespace und Image-Namen sein. Wir wählen an dieser Stelle beispielsweise „my-project/my-image“. Im nächsten Schritt werden uns die Befehle präsentiert, die wir für den Upload von Images ausführen müssen. Da wir zuvor bereits die AWS CLI installiert und ein Docker Image mit unserer Applikation erstellt haben, können wir die angezeigten Befehle direkt ausprobieren. Je nach Größe unseres Docker-Images kann der Upload eine Weile dauern. Währenddessen fahren wir im ECS-Wizard mit dem nächsten Schritt fort.

Für das Starten von Docker-Containern auf EC2-Instanzen sind sogenannte Tasks zuständig. Ein Task wird wiederum entsprechend einer Task-Definition erstellt. Eine solche erstellen wir also als nächstes. Der Wizard nimmt uns bereits einiges an Schreibarbeit ab und füllt die wichtigsten Felder aus. Zu beachten ist, dass wir den zu startenden Containern genügend Speicherressourcen gewähren und die richtigen Container-Ports auf die Host-Ports mappen. Wir erhöhen in unserem Beispiel das Memory Limit auf 500MB und mappen Container-Port 8080 auf Host-Port 8080.

Im nächsten Schritt bietet der Wizard zudem an einen „Service“ zu erstellen. Services laufen in einem Cluster und sorgen dafür, dass dort immer eine bestimmte Anzahl von Tasks basierend auf einer bestimmten Task Definition ausgeführt werden. In unserem Beispiel belassen wir die gewünschte Anzahl bei 1 und lassen Elastic Load Balancing (ELB) beiseite.

Im letzten Schritt konfigurieren wir schließlich den Cluster. Der Wizard bietet es uns hier an, gleich eine beliebige Anzahl von EC2-Instanzen zu starten und mit dem Cluster zu verknüpfen. In diesem Kontext werden diese dann bei AWS auch als „Container Instances“ bezeichtet. Wir benötigen an dieser Stelle bloß eine. Um den SSH-Zugriff auf die zu erstellende Maschine zu ermöglichen, muss ein Schlüsselpaar ausgewählt werden. Solche lassen sich im EC2-Service unter unter dem Punkt „Network & Security“, „Key Pairs“ generieren und herunterladen. Den Zugriff auf EC2-Instanzen regelt AWS über sogenannte „Security Groups“. Hier lässt sich definieren von welchen IP-Addressen der Zugriff gestattet werden soll. „Anywhere“ ist die einfachste Lösung für unser Beispiel, öffnet die Instanz natürlich aber auch für den Zugriff durch jedermann. Weiterhin gilt es eine „IAM Role“ mit unserer neuen EC2-Instanz zu verknüpfen. Diese gewährt dem künftig auf der Maschine laufenden „ECS Container Agent“ die nötigen Rechte für den Zugriff auf die ECS API. Der Wizard nimmt uns die Erstellung einer neuen Rolle an dieser Stelle ab.

Schließlich können wir unser Setup noch überprüfen und die Erstellung aller benötigten Ressourcen veranlassen. Nach etwas Warten können wir in der Übersicht aller EC2-Instanzen (Weboberfläche à Service EC2 à Instances) eine neue Maschine entdecken und in deren Detailbeschreibung auch die Public DNS entdecken. Kopieren wir diese in unsere Browser-Adresszeile und fügen den Port 8080 an, sollten wir die Payara-Startseite zu Gesicht bekommen. Somit haben wir mit vergleichsweise wenig Aufwand unsere Applikation in die Cloud gebracht.

Schaubild: Übersicht und Zusammenhänge der verwendeten AWS-Ressourcen

Übersicht und Zusammenhänge der verwendeten Ressourcen

Redeployment

Wollen wir nun Änderungen an unserer Applikation vornehmen, lassen sich diese in kürzester Zeit deployen. Nachdem wir die Applikation getestet und neu gebaut haben, erstellen wir, wie schon zuvor, ein neues Docker-Image und laden es in unser ECR-Repository hoch. Um einen Container basierend auf dem neuen Image zu starten, müssen wir jetzt lediglich den aktuell im Cluster laufenden Task stoppen. Der ebenfalls im Cluster aktive Service wird das Fehlen des Tasks umgehend bemerken und entsprechend seiner Zielvorgabe einen neuen basierend auf unserer Task Definition starten. Da diese stets auf die Image-Version mit dem Tag „latest“ verweist wird nun also der von uns erwünschte Container aus dem aktualisierten Docker Image gestartet. Ein Script, um diesen Prozess weitestgehend zu automatisieren, könnte wie folgt aussehen. (Achtung: Das Commandline-Tool jq wird hier benötigt.)

#!/bin/bash

CLUSTER_NAME=mycluster
BUILD_DIR=../target
DOCKER_TAG=123456789012.dkr.ecr.eu-central-1.amazonaws.com/my-project/my-image:latest

echo "copying war"
cp ${BUILD_DIR}/my-app.war .

echo "building and tagging docker image "
docker build --tag ${DOCKER_TAG} --file Dockerfile .

echo "logging in to AWS ECR"
$(aws ecr get-login --region eu-central-1 --cli-read-timeout 30 --cli-connect-timeout 30)

echo "pushing docker image to AWS ECR"
docker push ${DOCKER_TAG}

echo “stopping current task to trigger redeployment”
task_arn=$(aws --region eu-central-1 ecs list-tasks --cluster ${CLUSTER_NAME} | jq '.taskArns[]' | tr -d '"')

aws --region eu-central-1 ecs stop-task --cluster ${CLUSTER_NAME} --task $task_arn

Fazit und Ausblick

Dieser Artikel zielt vorrangig darauf ab, „AWS-Anfängern“ einen schnellen und motivierenden Einstieg in das Deployment von Docker-Containern zu geben. Daher wurde das Szenario hier bewusst einfach gehalten und entsprechend wichtige, aber komplexere Themen außen vor gelassen.

So ist in einem „production-ready“ Setup natürlich auch eine Ausfallsicherheit für die Container Instance erforderlich. Auch automatische Skalierung und Load Balancing haben wir hier noch nicht thematisiert. Gerade wenn eine Anwendung aber mit schwankender Last umzugehen hat, ist dies ein wichtiger Punkt und großer Vorteil eines Deployments mit den AWS. Weiterhin lässt sich auch das Setup der benötigten Ressourcen mit AWS CloudFormation automatisieren (Stichwort „Infrastructure as Code“).

Dennoch ist es auch wichtig festzustellen, dass die Amazon Web Services kein Allheilmittel darstellen und man durchaus auf Unzulänglichkeiten stößt. Deutlich komplexer wird das Setup etwa, wenn mehrere Komponenten deployt werden, die miteinander kommunizieren sollen, bspw. in einer Microservice-Architektur. Die Konfiguration eines solchen Szenarios gestaltet sich schwieriger. Hier bietet sich ein Vergleich mit den Kubernetes-basierenden Konkurrenten Google Container Engine oder OpenShift an.

Abschließend lässt sich festhalten, dass die Welt der Cloud-Services im Allgemeinen, wie auch AWS im Speziellen, tolle Möglichkeiten für IT-Unternehmen und ihre Kunden bietet. In zukünftigen Blog-Artikeln werden wir weitere, tiefere Einblicke in unsere hiesigen Aktivitäten geben, andere Services und Technologien behandeln sowie komplexere Vorhaben demonstrieren.

0 Antworten

Hinterlassen Sie einen Kommentar

Wollen Sie an der Diskussion teilnehmen?
Feel free to contribute!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.