SST - Theorie, Architektur & Deep Dive

29.11.2023Tom Trapp
Tech Serverless sst Amazon Web Services Function as a Service Pay As You Go

banner.png Quelle: https://sst.dev/

SST, Was ist das eigentlich?

Serverless Stack (SST) ist ein Open-Source-Framework, das es Entwicklern ermöglicht, Serverless Anwendungen schnell und effizient auf AWS zu erstellen und zu verwalten. SST unterstützt Live Lambda-Entwicklung, was bedeutet, dass Änderungen am Code sofort in der Cloud reflektiert werden, ohne dass ein manuelles Deployment erforderlich ist. Das Framework integriert sich nahtlos mit AWS-Diensten und bietet Funktionen wie Umgebungsvariablen, Berechtigungen und Authentifizierung, um die Entwicklung von Serverless Anwendungen zu vereinfachen.

Unter der Haube verbirgt sich AWS CDK und somit schlussendlich CloudFormation. Daher ist es auch nicht verwunderlich, dass SST nur AWS als Cloud Provider unterstützt. Man kann, in einfachen Worten sagen: SST ist eine Abstraktion der Abstraktion. Daher soll SST auch besonders einfach zu verwenden sein. Sie selbst werben viel mit ihrem Live Lambda Development, ihrem Web Dashboard und generell dem einfachen Deployment-Zyklus.

Kurzer Einschub, glücklicherweise gibt es ein Fireship Video, welches SST sehr gut erklärt.

Da SST recht umfangreich ist, verzichten wir dieses Mal noch auf Hands-On Beispiele. Dafür kommt im nächsten TechUp die volle SST Hands-On Dröhnung! 🤓

Infrastructure with Code?

SST ist ein sogenanntes Infrastructure with Code Framework, welches TypeScript nutzt, um zahlreiche Ressourcen aus der Serverless Welt anzulegen und zu verwalten. Generell kann man sagen, dass SST den imperativen Ansatz verfolgt, also Infrastructure with Code.

Kurze Klarstellung der Unterschiede zwischen IaC und IwC, danke an ChatGPT für die Erklärung:

“Infrastructure as Code” (IaC) und “Infrastructure with Code” sind zwei Ansätze zur Verwaltung und Bereitstellung von Infrastruktur, wobei sie sich in ihrer Methodik und ihrem Fokus unterscheiden. Hier ist eine kurze Erläuterung der Unterschiede zwischen den beiden:

Infrastructure as Code (IaC):

Definition:

Bei IaC wird die IT-Infrastruktur in Codeform definiert und verwaltet, wodurch die gesamte Lebensdauer der Infrastruktur automatisiert werden kann – von der Erstellung über die Aktualisierung bis hin zum Löschen.

Merkmale:

  • Declarative: IaC-Beschreibungen legen den gewünschten Endzustand der Infrastruktur fest. Werkzeuge sorgen dann dafür, dass die aktuelle Infrastruktur zu diesem Zustand konvergiert.
  • Versionskontrolle: Da die Infrastruktur als Code beschrieben wird, kann sie in Versionskontrollsystemen wie Git gespeichert werden.
  • Idempotenz: Das wiederholte Anwenden des IaC führt zum gleichen Ergebnis. Beispiele: Terraform, Ansible, CloudFormation, SAM, Serverless Framework.

Infrastructure with Code (IwC?):

Definition:

“Infrastructure with Code” bezieht sich auf Ansätze, bei denen Programmierlogik und -code verwendet werden, um Infrastruktur dynamisch zu definieren und anzupassen.

Merkmale:

  • Imperative: Hier legt der Entwickler fest, wie etwas erreicht wird, z. B. durch Schreiben von Schleifen, Bedingungen und weiterer Logik.
  • Flexibilität: Bietet Entwicklern die Möglichkeit, komplexe Logiken und Abläufe zu definieren.
  • Sprachzentriert: Es wird häufig eine gängige Programmiersprache (z. B. Python, JavaScript) verwendet, anstatt eine domänenspezifische Sprache (DSL) zu erlernen. > Beispiele: AWS CDK (Cloud Development Kit), Pulumi, SST.

Zu welcher Art von Tool man greifen sollte hängt viel von subjektiven Faktoren ab sowie auch vom UseCase. Beispielsweise kann ein imperativer Ansatz im Zusammenhang mit einem API-First Self-Service Ansatz geeigneter sein, da der Code direkt beispielsweise im REST-Endpunkt implementiert werden kann.

Back 2 SST! 🚀

Architektur

Voraussetzungen

Um SST nutzen zu können benötigt man, da es sich um ein TypeScript-Framework handelt, NodeJS und NPM. Da SST nur AWS als Cloud Provider unterstützt, benötigt man ausserdem ein AWS Account inkl. CLI Zugriff.

Komponenten

Nun wollten wir die Building Blocks von SST kennenlernen und verstehen, wie diese zusammenhängen.

Ein SST-Projekt besteht grundsätzlich aus folgenden Komponenten:

  • Stacks

    • Stacks sind die eigentlichen CloudFormation Stacks, die von SST generiert werden. Sie enthalten die Infrastruktur.
    • Ein Stack besteht grundsätzlich aus mehreren Constructs.
    • Ein Projekt muss aber nicht nur einen Stack enthalten, sondern kann auch mehrere Stacks enthalten.
  • Constructs

    • Constructs sind die Bausteine, die die Infrastruktur bilden.
    • Sie sind die eigentlichen CloudFormation-Ressourcen, die von SST generiert und abstrahiert werden.
    • Hier ist zu erwähnen, das ein Construct für eine AWS Resource oder auch für eine Kombination mehrerer AWS Ressourcen stehen kann.
    • Ein Construct kann zum Beispiel eine simple API, eine RDS-Datenbank oder auch eine komplette Serverless Anwendung sein.
    • Constructs werden über Imports direkt in den Stacks verwendet.
    • Es ist teils nicht ganz klar, welche AWS Ressourcen sich hinter einem Construct befinden. Die Abstrahierung ist hier sehr stark.
    • Alle Constructs sind hier zu finden.
  • Packages/Functions

    • Packages enthalten die Funktionen, die in der Infrastruktur deployed werden sollen, also die Lambda-Funktionen.
    • Schlussendlich handelt es sich auch hier um ein Construct, welches eine Lambda-Funktion beinhaltet.
    • SST unterstützt JavaScript, TypeScript, Python, Go, C#, Rust und Container Runtimes.
    • SST definiert bereits bestimmte Defaultwerte, wie z.B. 1024 MB Memory.
    • Ein Beispiel für vier unterschiedlichen Functions wären die vier CRUD Befehle, die wir in einer API benötigen.
  • Packages/Core

    • Die Core Packages enthalten die Businesslogik, die in den Funktionen verwendet wird.
    • Das Core Package kann als art Library gesehen werden. Dies ermöglicht eine Modularisierung zwischen den Lambda-Funktionen.
    • Im vorherigen Beispiel mit den vier CRUD Functions könnte hier, im Core, beispielsweise die Datenbankanbindung implementiert werden.
  • Clients

    • Mittels Clients können wir Ressourcen aus unseren Stacks referenzieren.
    • Hierbei handelt es sich laut SST um typesafe Node.js clients for your AWS infrastructure.
    • Clients können Properties von Ressourcen auslesen, wie beispielsweise einen Bucketname oder eine API URL.
    • Mit Clients können wir aber auch Handlers nutzen, welche wir um unsere Lambda-Funktionen packen können. Dies ermöglicht uns Typensicherheit und initialisiert einen SST-Kontext, der es uns erlaubt, Hooks zu nutzen.
    • Mit Hooks können wir bestimmte Methoden zum aktuellen Request eines unserer Clients aufrufen. So können wir beispielsweise den aktuell eingeloggten Benutzer, die komplette Session, den Request JSON Body, die Cookies oder Query Parameter in unserem Core Package nutzen.
    • Alle Clients sind hier zu finden.

Nun haben wir die wichtigsten Building Blocks kennengelernt, und wollen uns die Architektur von SST genauer anschauen.

Architektur im Detail

In der nachfolgenden Grafik ist beispielhaft das Zusammenspiel zwischen den verschiedenen Architekturkomponenten aufgezeichnet:

sst_architecture.png

Development

SST erfreut sich über Support in den beliebtesten IDEs wie VS Code, IntelliJ und WebStorm. Zum Starten eines SST Projekts kann man entweder aus unterschiedlichen Templates wählen oder im Standalone-Modus starten.

Direkt fällt auf, dass sich alles in einem Repository befindet, SST fährt einen sogenannten Mono-Repo Ansatz. SST bietet einige sehr nützliche Tools, die das Entwickeln von Serverless Anwendungen vereinfachen sollen. Hier hebt sich SST auch von anderen Frameworks ab, da es einige Features bietet, die es so in anderen Frameworks nicht gibt.

Live Lambda Development

Mit Live Lambda Development können wir unsere Lambda-Funktionen lokal entwickeln und über unsere normalen AWS-Ressourcen ansteuern. Dies erlaubt es uns, lokal etwas zu testen, während wir beispielsweise den ’echten’ AWS API Gateway einsetzen.

Moment, wie soll das gehen? 🤔

Technisch wird ein Proxying der Requests hin zur lokalen Lambda-Funktion durchgeführt. Dies erlaubt es uns, via Live Reload unsere Funktionen in unter 10 Millisekunden zu testen und zu debuggen. Konkret nutzt SST den AWS Service AWS IOT over WebSocket, um die Requests zu unseren Lambda-Funktionen zu leiten. Dies bietet uns einige Vorteile, unter anderem:

  • Lokale Entwicklung und Debugging
  • Live Reload
  • Serverless Approach, PAYG
  • Kein lokales Mocking o.ä. nötig

Live Lambda wird von allen gängigen Sprachen unterstützt, leider gibt es aktuell nur für JavaScript, TypesScripts und Python eine Community-Unterstützung, um Breakpoints zu setzen.

Ein weiterer Stolperstein könnte sein, dass die lokale Live Lambda Instanz sich nicht mit einem VPC verbinden kann. Hierfür müsste man ein eigenes VPN aufsetzen. Alternativ könnte man im Code direkt eine beispielweise lokale RDS-Datenbank ansteuern, hier hilft sicher die IS_LOCAL Umgebungsvariable.

Side Note: vom Vorgehen her erinnert dies stark an die Ansätze von Telepresence, mehr dazu in meinen TechUp über Telepresence.

Weitere Infos zum Live-Lambda findest du hier.

Wenn du nicht auf unser nächstes TechUp warten kannst, hier findest du eine kurze Demo.

Testing

Ein weiteres Feature von SST ist das Testing. Grundsätzlich unterscheidet man zwischen folgenden drei Arten von Tests.

Domain Tests

Mit Domain Tests lässt sich die Businesslogik testen. So können wir CRUD-Funktionalitäten unserer AppliKation testen. Wir prüfen, ob ein Create korrekt funktioniert und lesen anschliessend alle Einträge aus, um zu prüfen, ob der Eintrag korrekt erstellt wurde. Im Hintergrund wird hier das Testingframework Vitest für die Unittests verwendet.

API Tests

Ähnlich wie bei einem Domaintest einer Lambda-Funktion testen wir, ob für einen bestimmten Input ein bestimmter Output zurückgegeben wird. Hierbei rufen wir aber nicht die Lambda-Funktion direkt auf, sondern nutzen den API-Gateway, um unsere Lambda-Funktionen zu testen. Dieses Verhalten ist sehr nahe am effektiven Endnutzerverhalten, was diese Art von Tests sehr wertvoll macht!

Stack Tests

Wir können aber nicht nur unsere Businesslogik testen, sondern auch unsere Infrastruktur. Mit Stack Tests können wir unsere Infrastruktur testen, beispielsweise ob ein bestimmter Bucket existiert, oder ob eine bestimmte API-Route existiert. Dies ist besonders bei Updates oder ähnlichem sinnvoll, um zu testen ob auch beispielsweise Defaultwerte weiterhin korrekt gesetzt sind. Sehr cool! 🤩

Console

SST bietet eine Web Console, die uns einen Überblick über unsere Stacks und deren Ressourcen gibt: https://docs.sst.dev/console.

Auch hier trifft SST genau den Developer-Need, indem Logs, Metriken und allgemeine Infos zum Projekt zentral an einer Stelle gesammelt und aufbereitet angezeigt werden. Sehr hilfreich ist hier, dass sst console in Kombination mit sst dev, dem Command fürs lokale Entwickeln, die lokalen Logausgaben auch in der Console anzeigt.

An dieser Stelle sei zu erwähnen, dass die sst-console aktuell wohl leider nur mit der AWS Region eu-east-1 kompatibel ist.

Deployment

Zuerst müssen wir unser komplettes Projekt mit sst build bauen.

Zum Deployment gibt es nichts spezielles zu sagen, via sst deploy wird das Projekt deployed. Hier kann man dann direkt auch ein passendes Profil angeben, wohin das komplette Projekt deployed werden soll. Konkret verbirgt sich hinter dem Profil der AWS-Account, welcher in den CLI Credentials hinterlegt ist. So lässt sich beispielsweise ein Test Account mit den entsprechenden Ressourcen versorgen.

Mittels sst diff lässt sich im Voraus der lokale Stand mit dem deployten Stand im AWS Account vergleichen. Selbstverständlich empfiehlt sich auch hier ein kompletter CI/CD Prozess inkl. ausführung des Tests etc.

SEED

Seed sieht wirklich auf den ersten Blick aus, als sei es das ArgoCD für SST. Hierbei steht das Arbeiten an einer Serverless-Anwendung im gesamten Team im Vordergrund. Seed preist selbst auch Features wie Incremental Deployments, Zero Config Pipelines sowie Real-Time Lambda Alertings an.

Wir werden an dieser Stelle nicht weiter auf Seed eingehen und Seed gegebenenfalls in einem separaten TechUp genauer unter die Lupe nehmen! 🔍

Examples

Nun haben wir die grundsätzlichen Building Blocks und Features von SST kennengelernt. Dieses Wissen wollen wir anhand einiger Beispiele vertiefen. Wie schon erwähnt, gibt es die Hands-On Beispiele im nächsten TechUp, dort werden wir dann ein eigenes SST Projekt aufsetzen und deployen.

Simple API

Beispiel: https://github.com/sst/sst/tree/master/examples/rest-api

In diesem Beispiel sehen wir direkt die Projektstruktur, die wir bereits im Kapitel Architektur gesehen haben. Im Ordner Stack wird in der Datei ExampleStack.ts der komplette SST Stack definiert, hier wird das Construct API, welches einen API Gateway und eine Lambda-Funktion beinhaltet, importiert. Anschliessend wird definiert, welche Werte der Stack ausgeben soll. Dies ist sehr nützlich, da wird so direkt unsere API URL erhalten.

Im Order Package/Function wird die eigentliche Lambda-Funktion definiert, hier wird die Core Package importiert, welche unsere Businesslogik enthält. Konkret sehen wir hier, dass drei Lambda Funktionen definiert werden, eine für jede HTTP Methode.

Unter Core befindet sich dann die Datenspeicherung. In diesem simplen Beispiel wird ein Array verwendet. Selbstverständlich würde in einem Real World Use-Case hier dann beispielsweise eine Datenbank angesprochen werden. Zu erwähnen ist, dass das einfache Beispielprojekt intern aus drei NPM-Projekten besteht, jeweils mit eigener package.json inkl. Dependency Management.

Svelte App

SST liefert uns direkt fertige Constructs, um unterschiedliche Frontend-Frameworks wie Next.js, Svelte, Remix, Astro etc. zu deployen.

Beispiel: https://github.com/sst/sst/tree/master/examples/quickstart-sveltekit

In diesem Beispiel sehen wir, wie wir eine Svelte App mit SST deployen können. Direkt fällt auf, dass es keinen Stack Folder gibt, der Stack wird direkt beim Binding in der sst.config.ts definiert. Hier sieht man, dass die Constructs Bucket, SvelteKitSite sowie Cron verwendet werden.

Unter src finden wir das komplette Svelte-Projekt, inkl. der Lambda Funktion für den CronJob.

Pub/Sub

Beispiel: https://github.com/sst/sst/tree/master/examples/pub-sub

In diesem Beispiel sehen wir, wie wir einen Pub/Sub Ansatz mit SST umsetzen können. Unter der Haube wird hier ein SNS Topic verwendet, welches die Nachrichten an ein SQS Queue weiterleitet. In unserem Stack definieren wir beim anlegen des SNS Topics direkt die Handler-Funktionen, die aufgerufen werden sollen, wenn eine Nachricht ankommt. Ebenfalls sehr nützlich ist, dass wir unsere API direkt mit dem SNS Topic verbinden können und somit automatisch unsere REST Payloads in das SNS Topic senden können.

Fazit

Go SST! 🚀

SST wirkt im Vergleich zu den anderen Serverless Development & Deployment Tools sehr umfangreich und ausgereift. Der Ansatz, die AWS Ressourcen nochmals zu abstrahieren gefällt mir persönlich sehr, auch, dass via Constructs teils mehrere AWS Ressourcen zusammengefasst sind. Auch die Nutzung von Proxying statt Mocking beim Lambda Development stösst bei mir definitiv auf Anklang.

Grundsätzlich versucht man hier, Serverless auf AWS zu machen, ohne AWS wirklich kennen zu müssen. Dies macht es für Entwickler sehr einfach, die Vorteile von Serverless zu nutzen, ohne grossen Entwicklungsaufwand zu haben. Zudem kann man sich so auch sicher sein, dass beispielsweise Best Practises und Default Values sinnvoll gesetzt sind und in den meisten Fällen auch so verwendet werden können.

Im nächsten TechUp zu SST machen wir uns dann die Hände schmutzig und erstellen ein eigenes SST-Projekt, stay tuned! 🤓

Tom Trapp

Tom Trapp – Problemlöser, Innovator, Sportler. Am liebsten feilt Tom den ganzen Tag an der moderner Software und legt viel Wert auf objektiv sauberen, leanen Code.