Backend mit Amazon Web Services

In unserem zweiten Artikel  dieser Serie werden wir erneut ein Backend zum Speichern und Abfragen von Daten erstellen. Als Cloud-Anbieter werden jetzt die Amazon Web Services (AWS) verwendet.

Im Gegensatz zu der Azure-Implementierung unseres Backends werden wir bei AWS den REST-Service nicht selber implementieren. Stattdessen werden wir bestimmte AWS-Services verwenden, welche uns eine Servless-Implementierung des kompletten Backends ermöglicht. Bei Servless müssen wir uns keine Gedanken um etwaige Dienste machen, die wir Verwalten und mit Code deploymen müssen. All dieser administrative Overhead wird uns von AWS abgenommen.

Architekturübersicht

Für unseren servless REST-Backend werden für von AWS folgende Services verwenden:

  • API-Gateway: Stellt uns einen REST-Service-Endpunkt zur Verfügung.
  • Lambda Functions: Verarbeitet die Anfragen vom REST-Service.
  • DynamoDB: Speichert die strukturierten Daten als JSON.
  • IAM Roles: Stellt uns eine Rechteverwaltung für den Zugriff auf die Daten bereit.
  • CloudWatch Logs: Speichert die von den Lambda Function erzeugten Logs (z.B. Fehler).
Systemarchitektur des Backends in der AWS-Cloud

Aufsetzen der DynamoDB

Die DynamoDB ist eine klassische NoSQL-Datenbank, wobei sich Amazon bei der Benennung der Sache in der AWS-Konsole eher an die klassischen Begriffe einer relationalen Datenbank orientiert hat. Daher legen wir eine neue Tabelle an und vergeben einen Primärschlüssel.

Ein Entscheidender Unterschied zu der Azure Cosmos-DB besteht darin, dass wir keine DynamoDB-Instanz erstellen müssen. AWS stellt den Service bereit und ermöglicht den Entwickler nur die Anlage von Tabellen und Daten. Um sicherzustellen, dass nicht jede Anwendung alle Tabellen des von AWS bereitgestellten DynamoDB-Service verwenden kann, müssen wir IAM-Rollen anlegen, welche den Zugriff auf die Tabellen und die erlaubten Operationen steuert.

Einrichten der Zugriffsberechtigungen für die DynamoDB-Tabelle

Implementierung der Lambda Functions

Als nächstes legen wir eine Lambda-Funktion an. Sie hat die einfache Aufgabe, die Daten von einem HTTP-Request in Daten für eine DynamoDB zu transformieren. In diesem Beispiel ist dies anhand der Anlage eines Tasks dargestellt.

Zu allererst müssen wir die Lambda-Funktion anlegen. Dazu vergeben wir einen Namen und wählen eine Runtime aus. Neben den Verfügbaren Runtimes wie Node.JS, .NET und Java wählen wir hier aber Python. Als nächstes ist es wichtig, dass wir unsere eben angelegt Ausführungsrolle hinterlegen, da nur so die Lambda-Funktion die Berechtigung auf das Arbeiten der angelegten Tabelle in der DynamoDB erhält.

Erstellung einer Lambda Function
Zu guter Letzt können wir nun die Implementierung hinterlegen. Dazu reicht für den Anfang der Code-Editor in der AWS Console komplett aus. Die Logik besitzt dabei grob folgenden Ablauf:
  • Lade eine Referenz zur Tabelle task
  • Lege einen neuen Task mit den Informationen aus dem Request-Body an
  • Versuche den neuen Task in der Tabelle anzulegen
  • Wenn es ein Fehler gab, wird dieser zurückgegeben, ansonsten das neu angelegte Objekt
import boto3
import json
import shared
import uuid
from datetime import datetime, timezone
from decimal import Decimal
from botocore.exceptions import ClientError
 
def lambda_handler(event, context):
    taskTable = boto3.resource('dynamodb').Table("task")
    body = json.loads(event['body'])
     
    newTask = {
        'id': str(uuid.uuid4()),
        'name': body['name'],
        'name_search': body['name'].lower(),
        'priority': Decimal(body['priority']),
        'description': body['description'],
        'description_search': body['description'].lower(),
        'created_at': datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%S.%f%Z')
    }
     
    try:
        response = taskTable.put_item(Item = newTask)
 
    except ClientError as err:
        print(err.response['Error']['Message'])
        return shared.respond(err)
         
    else:
        return shared.respond(None, newTask)

Konfiguration des API-Gateways

Alle bisher angelegten AWS-Services sind nur intern aufrufbar. Damit unsere Anwendung darauf zugriff hat, müssen wir ein API-Gateway anlegen. Dieser ist ein konfigurierbarer REST-Service, indem wir die Pfade und Methoden beliebig konfigurieren können.

Um bei dem Beispiel der Anlage eines neuer Aufgabe zu bleiben, beschreibt die folgende Bilderreihe die Schritte zur Anlage einer POST-Methode, welche für die Anlage aufgerufen werden soll. Dazu stellen wir bei der Anlage ein, dass der entgegengenommene Request an unser Lambda weitergereicht werden soll.

Das API-Gateway bietet uns zusätzlich die Möglichkeit, die Methode direkt testen zu können. So können Konfigurationsfehler schnell erkannt und behoben werden.

Deployment der Komponenten

Mit der Anlage der API im API-Gateway ist es aber noch nicht getan. AWS ermöglicht die Bearbeitung der API ohne dass davon eine ggf. laufende Anwendung beeinträchtigt wird. Dies wird erreicht, in dem eine API vor der externen Nutzung veröffentlicht werden muss. Eine API kann auch mehrfach veröffentlicht werden, was somit auch eine Versionierung der API ermöglicht.

Nachdem die API veröffentlicht wurde, stellt AWS einen URL für den Endpunkt bereit. Ab diesem Moment ist die REST-API darüber erreichbar und kann mit externen Tools abgefragt werden. Nützlicherweise bietet AWS direkt fertige Schemadefinitionen des REST-Service zum Download an (z.B. swagger.json).