Dapr ist ein in Golang geschriebenes Framework, welches verspricht, dass es das Development von Cloud-Native Applikationen stark vereinfacht, so dass der Entwickler sich auf die Kernlogik seiner Applikation konzentrieren kann. Dapr wurde im Oktober 2019, nach anfänglichem Namen-Wirrwarr in einer ersten Alpha-Version veröffentlicht. Der Name war ursprünglich Dapper. Da es diesen Namen aber bereits gab, entschied man sich dazu den gleichen Wortlaut zu behalten und einfach das Wort selbst zu ändern.
Mittlerweile findet man Dapr in der Version 1.4 und laut Github gibt es momentan 139 Contributors.
Aber was ist nun Dapr? Hier die originale Beschreibung von der offiziellen Darp-Dokumentation:
Dapr is a portable, event-driven runtime that makes it easy for any developer to build resilient, stateless and stateful applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks. Leveraging the benefits of a sidecar architecture, Dapr helps you tackle the challenges that come with building microservices and keeps your code platform agnostic.
Dapr kodifiziert die Best Practices zum Erstellen von Microservice-Anwendungen in offene, unabhängige Bausteine, die es Ihnen ermöglichen, portable Anwendungen mit der Sprache und dem Framework Ihrer Wahl zu erstellen. Jeder Baustein ist völlig unabhängig und Sie können einen, einige oder alle davon in Ihrer Anwendung verwenden.
Darüber hinaus ist Dapr plattformunabhängig, d. h. Sie können Ihre Anwendungen lokal, auf jedem Kubernetes-Cluster und in anderen Hosting-Umgebungen ausführen, in die Dapr integriert ist. Auf diese Weise können Sie Microservice-Anwendungen erstellen, die in der Cloud und auf Edge-Devices ausgeführt werden können.
Aufbau von Dapr
Wollen wir uns den Aufbau von Dapr etwas genauer anschauen. Wie wir bereits in der offiziellen Beschreibung lesen können, wird Dapr in einem Sidecar-Container an die eigene Applikation angebunden. Somit braucht man die eigentliche Applikation theoretisch nicht zu verändern.
In der Praxis sieht es jedoch etwas anders aus, da Dapr für verschiedene Sprachen SDKs zur Verfügung stellt. Nutzt man die SDKs so hat man also eine Abhängigkeit zu Dapr in seinem Code. Man muss hier allerdings nicht auf ein SDK zurückgreifen, sondern kann auch direkt per REST oder gRPC auf die verschiedenen Building Blocks zugreifen. Hier sehen wir den grundsätzlichen Aufbau der Architektur.
Building Blocks
Building Blocks sind ein über HTTP order gRPC bereitgestellte Schnittstelle, die bekannte Probleme einer Microservice-Architektur simpel lösen sollen. Dapr liefert standardmässig die folgenden 7 Building Blocks aus.
Service-to-service Invocation
Der Serviceaufruf ermöglicht es Anwendungen, über bekannte Endpunkte (HTTP oder gRPC) Nachrichten miteinander auszutauschen. Dapr bietet einen Endpunkt, der als Kombination aus einem Reverse-Proxy mit integrierter Serviceerkennung fungiert und gleichzeitig das integrierte Tracing- und Fehlerbehandlung nutzt. Dazu nutzt Dapr Service Discovery -Komponenten. Die Kubernetes-Service Discovery-Komponente ist beispielsweise in den Kubernetes DNS Service integriert.
Dapr erlaubt es ausserdem eine Custom-Middleware in den Request-Prozess einzuhängen. Damit ist es dann möglich zusätzliche Aktionen wie beispielsweise eine Authentifizierung oder die Transformation einer Nachricht zu machen, bevor der Request dann den Applikationscode erreicht.
State Management
Um Daten einer Applikation zu speichern, benötigt man eine Persistierung. Dapr bietet hierfür eine KeyValue-Store-API um Daten dann in austauschbaren Stores zu persistieren. Dapr bietet hier verschiedene Arten von Stores an, wie Dateisystem, Datenbank, Speicher. Gängige State-Store-Implementationen sind beispielsweise Redis oder AWS DynamoDB. Eine vollständige Liste findet man hier.
Publish und Subscribe
Dapr unterstützt das Subscribe Pattern , bei dem ein Sender eine Nachricht in eine Queue schreiben kann und ein Empfänger diese Nachricht abrufen und verarbeiten kann. Dapr unterstützt hier die gängigen Anbieter wie NATS Streaming oder Kafka. Eine vollständige Liste findet man hier.
Resource Bindings and Triggers
Hiermit ist es möglich eine Applikation mit einem externen Cloud- oder On-Premise System zu verbinden. Dapr ermöglicht es externe Systeme mit der Binding API aufzurufen, oder dass die eigene Applikation durch Events von verbunden Systemen getriggert werden kann. Unterstützte Komponenten sind hier beispielsweise Cron, HTTP oder auch Apple Push -Notifications. Weitere findet man hier.
Actors
Ein Actor ist eine isolierte und unabhängige Einheit, welche es ermöglicht Code und Daten voneinander zu trennen.
Observability
Dapr-Systemkomponenten und -laufzeit geben Metriken, Protokolle und Traces aus, um Dapr-Systemdienste, -komponenten und Benutzeranwendungen zu debuggen, zu betreiben und zu überwachen.
Secrets
Dapr bietet eine Secrets Building Block API und lässt sich in alle gängigen Secret-Stores wie beispielsweise AWS Secrets Manager oder Kubernetes integrieren. Über die Secrets-API kann man im Code dann Secrets speichern und auch wieder abrufen. Eine vollständige Liste aller unterstützten Secret Stores findet man hier.
Braucht man noch weitere Building Blocks, so kann man sich über das Extension Framework neue Blocks selbst programmieren.
Ist Dapr ein Service Mesh?
Da Dapr auch ähnliche Funktionalitäten wie ein Service Mesh bietet (beispielsweise Service-zu-Service Kommunikation mit mTLS), und auch als Sidecar Container deployed wird, liegt die Frage nahe, ob Dapr schlussendlich nicht einfach ein weiterer Service Mesh ist.
Dapr selbst gibt darauf die folgende Antwort:
Anders als ein Service Mesh, welcher sich auf Netzwerk Angelegenheiten fokussiert hat Dapr den Fokus darauf sogenannte Building Blocks (siehe oben) anzubieten, welche es dem Entwickler einfach machen, Applikationen als Microservice zu bauen. Dapr ist Entwickler-zentriert, während ein Service-Mesh Infrastruktur-zentriert ist.
Somit ist Dapr kein Service Mesh. Logischerweise folgt daraus, dass man Dapr gemeinsam mit einem Service Mesh nutzen kann. Es wird einzig empfohlen die Service-zu-Service-Kommunikation mit mTLS nur mit einem der beiden Frameworks zu nutzen, da diese Funktionalität sowohl von Dapr als auch von gängigen Service-Mesh Implementationen wie Linkerd oder Istio genutzt wird.
Dapr in der Praxis
Wollen wir uns Dapr nun in der Praxis anschauen. Zunächst einmal müssen wir uns die Dapr-CLI installieren. Auf dem Mac geht das sehr einfach über das Terminal oder über Brew. Ich wähle hier den Weg übers Terminal.
|
|
Danach können wir mit dem Befehl dapr
verifizieren, ob das CLI richtig installiert wurde.
|
|
Nun können wir Dapr lokal installieren. Normalerweise läuft Dapr ja als Sidecar Container. Das bedeutet lokal, läuft es als separater Prozess. Mit dem folgenden Befehl werden also die Sidecar Binaries gezogen und auf dem lokalen Client installiert.
|
|
Wenn alles installiert ist, können wir mit dapr --version
überprüfen, ob alles korrekt installiert ist.
|
|
Soweit so gut. Starten wir nun unseren ersten Dapr Sidecar Container. Hier hilft uns der Befehl dapr run
. Wir starten
nun einen Sidecar Container mit einer leeren Applikation myapp.
Hierbei werden die Standard-Komponenten genutzt welche wir im Dapr Konfigurationsordner (~/.dapr/components/) finden. Beispielsweise wird der lokale Redis Docker Container als State Speicher und Message Broker verwendet.
|
|
Wollen wir nun was in unseren State Speicher schreiben. Dazu nutzen wir den folgenden Befehl:
|
|
Wie wir sehen können, nutzen wir die Dapr API um direkt in den Redis-State Store zu schreiben.
Wollen wir uns die Nachricht wieder anzeigen lassen, so können wir dies mit dem folgenden Befehl tun:
|
|
Nun können wir noch verifizieren, ob Dapr wirklich den Redis-Docker-Container als State-Speicher nutzt. Dazu verbinden wir uns folgendermassen direkt mit unserem Docker Container.
|
|
Da wir nun direkt in der redis-cli sind, können wir dann per keys *
alle Keys abrufen.
|
|
Nun wollen wir uns auch noch den Wert anzeigen lassen. Dazu geben wir Folgendes ein:
|
|
Eine eigene Komponente verwenden
Nun haben wir gesehen, wie man mit Dapr Standardkomponenten nutzen kann. Wollen wir uns als Nächstes anschauen, wie man seine eigene Komponente als KeyValue-Store nutzen kann.
Wir erstellen uns erstmal eine Datei im tmp Ordner /tmp/secrets.json
mit dem folgenden Inhalt:
|
|
Danach erstellen wir uns einen Ordner my-components
. In diesem Ordner erstellen wir eine Datei
my-local-secret-store.yaml
mit dem folgenden Inhalt
|
|
Das ist schon alles. Wir müssen nun lediglich beim Befehl zum Erstellen des Sidecar-Containers den Pfad zu unseren Komponenten angeben. Wir sollten dann in der Ausgabe den angegebenen String sehen.
|
|
Wollen wir schauen, ob unser Secret nun wirklich aus dem neuen eigenen Store gelesen wird.
|
|
🚀
Dapr Go SDK
Nun haben wir unsere erste eigene Komponente geschrieben und genutzt. Als Nächstes wollen wir uns noch eine Dapr-SDK anschauen.
Schreiben wir uns also ein ganz einfaches Go-Programm. Als Erstes erstellen wir ein neues Go-Projekt. Dazu geben wir die folgenden Befehle ein (Go Modules müssen enabled sein)
|
|
Als Nächstes erstellen wir das main.go
-File und schreiben den folgenden Code:
|
|
Nun kopieren wir den erstellten Ordner für unsere Custom-Komponente in das Go-Projekt. Die Ordnerstruktur sollte dann so aussehen.
|
|
Um jetzt unser Programm zu testen, müssen wir noch Dapr gemeinsam mit der Applikation starten. Wie wir sehen, haben wir erfolgreich über das SDK das Secret aus unserem Custom-Secret-Store abgerufen.
|
|
Fazit
Dapr sieht nach einem sehr interessanten Ansatz aus, um die einfache Entwicklung von Cloud Native-Applikationen zu starten. Der Anfang ist etwas gewöhnungsbedürftig, aber wenn man das Konzept erstmal verstanden hat, geht die Entwicklung wirklich leicht von der Hand.
Wir bei b-nova werden die Entwicklung von Dapr auf jeden Fall weiter verfolgen und natürlich in einem b-nova internen Projekt exzessiv nutzen. 😀