Im Cloud-Umfeld gibt es zahlreiche Möglichkeiten wie man eine Service-orientierte Architektur mithilfe von Microservices implementieren kann. Dabei steht oft die Ressourcen-, und somit die Kosteneffizienz im Vordergrund, wie auch die Skalierbarkeit bei unterschiedlichen Lastfenstern. Heute schauen wir uns an, wie man kosteneffizient Microservices in der Cloud laufen lassen kann.
Grundsätzlich gibt es zwei etablierte Arten wie man Microservices laufen lässt, entweder als eine Serverless Cloud Function oder als einen Container in einem Kubernetes-Cluster. Serverless Functions wie die beliebte AWS Lambda sind mit niedrigen Tarifen einsetzbar und leicht aufzubauen. Serverless Functions werden aber ab einer gewissen Lastgrenze markant teurer. Die Variante mit Containern auf Kubernetes ist hingegen doch komplexer, lässt sich aber langfristig kosteneffizienter skalieren. Somit ist die Kubernetes-Lösung darauf angewiesen Container auszurollen, die mit einem geringstmöglichen Footprint und verfügbarer Ansprechleistung gebaut sind.
Wir haben uns in der Vergangheit bereits Quarkus als Framework der Wahl angeschaut gehabt, mit welchem man problemlos schnell einen Microservice in Java geschrieben hat. Aber geht da noch mehr? Aus diesem Grund fokussieren wir uns heute auf Microservcies, die in Go geschrieben sind und mit einem anderen Protokoll Endpunkte bereitstellen, die noch mehr Durchsatz erzielen.
Dieser Beitrag ist inspiriert durch https://itnext.io/grpc-go-microservices-on-kubernetes-bcb6267e9f53.
gRPC – Der universelle Remote Procedure Call Framework
Bei dem Begriff Remote Prodecure Call mag man sich noch wage an Java’s Remote Method Invocation (kurz RMI) oder EJBs (Enterprise JavaBeans) erinnern, womit eine Java-Applikation remote über eine Netzwerk-Verbindung direkt eine bestimmte Methode einer zweiten Java-Applikation aufrufen konnte.
gRPC ist von Google entwickeltes Protokoll zum Aufruf von Funktionen in verteilten Systemen. gRPC steht für Google Remote Procedure Call. gRPC wird von der Cloud Native Computing Foundation als ‘Incubating Project’ bewertet. Dabei werde folgende zwei Hauptkomponenten verwendet:
-
Protocol Buffers (kurz Protobuf) als Interface Description Language: gRPC nutzt Protobuf als Serialisierung zur Kommunikation. Dabei wird ein binäres Format genutzt, was die Schnittstelle schneller auf Anfragen reagieren lässt. Die Schnittstelle definiert man semantisch (und nicht etwa binär) in einem
.proto
-File. Diese Definition generiert das Binär-Format, welches dann als Interface Description Language (wie beispielsweise JSON) fungiert. -
HTTP/2 als Transport: Seit 2015 gibt es den neuen HTTP-Standard. Diese zweite Iteration erlaubt bidirektionale Kommunikation, Server Pushs, Header Compression, Multiplexing und weitere Dinge. gRPC nutzt diese neuen Features um die Datenübertragung noch schneller zu gestalten.
Für Microservices nutzt man typischerweise eine REST-Schnittstelle um für die Kommunikation unter Applikationen zu schaffen. Mit gRPC hat Google eine Lösung implementiert, womit man noch schneller kommunizieren miteinander kann.
Features und Vorteile
-
Einfache Service-Definition
-
Effizient da binär, skalierbar
-
Sprachunabhängig, polyglot
-
Bidirektionaler Datenaustausch/-streaming
-
Authentifizierung
-
Perfekt für Interne Kommunikation unter Services
gRPC lässt sich in unterschiedlichen Programmiersprachen nutzen. Somit kann relativ einfach auf REST-Endpunkten eine gPRC-Schnittstelle implementieren, die für bessere Performance bei geringerer Leistungsbedarf sorgt. Es sei angemerkt, dass HTTP/2 noch nicht flächendeckend genutzt wird, da noch veraltete Browser noch tägliche Verwendung finden. Mit gRPC Web, einer JavaScript-Implementierung für Browser-Clients, kann man auch mit älteren Browsern gRPC ansprechen. Falls man weiterhin Applikationen im Einsatz hat, die nur REST-Schnittstellen ansprechen können, kann man mit gRPC Gateway einen Parallelbetrieb über die Definitionsfile schaffen, wobei der Service REST, wie auch gRPC exponiert. Auf diese Technologie gehen wir hier jetzt nicht genauer ein, aber es ist sicherlich gut zu wissen, wie flexibel eine Schnittstelle mit gRPC sein kann.
Wichtig zu wissen ist dass man die Schnittstelle deklarativ in einem .proto
-File schreibt und mit dieser dann stub-Files (Code-Bausteine) in der gewünschten Sprache kompilieren kann. Unabhängig von der Programmiersprache, können diese Bausteine in dem jeweiligen Projekt implementiert werden und in der laufenden Applikation miteinander sprachunabhängig kommunizieren.
Unterstütze Sprachen für gRPC
C, C++, C#, Dart, Go, Java, Kotlin, Node.js, Objective-C, PHP, Python, Ruby.
Service-Definition mit Google’s Protobuf
Die gRPC-Schnittstelle nutzt Protobuf als IDL. Diese Schnittstelle schreibt man deklarativin einem .proto
-File und kompiliert mit dieser dann stub-Files (Code-Bausteine) in die gewünschten Sprache. Unabhängig von der Programmiersprache, können diese Bausteine in dem jeweiligen Projekt implementiert werden und in der laufenden Applikation miteinander sprachunabhängig kommunizieren.
Ein Beispiel einter .proto-File könnte für eine geographisch-bewusste Chat-API wie folgt aussehen:
|
|
Protobuf installieren und anwenden
Wie können wir jetzt einen Microservice mit gRPC in Go bauen? Das ist recht einfach. Zuerst installieren wir uns Protobuf wie folgt:
|
|
Dazu muss man noch das Go-Plugin protoc-gen-go
von laden:
|
|
Stellt des weiteren sicher dass die Bash-Pfade für Go korrekt gesetzt sind :
|
|
Sowie auch :
|
|
Das folgende Snippet ist eine exemplarische grpcapi.proto
. Mithilfe der Interface Description Language von Protobuf beschreiben wir unsere Schnittstelle.
|
|
Mit diesem Befehl kompiliert der Protobuf-Compiler protoc
das Schema in ein verwendbares Go-File:
|
|
Der Output von protoc
ist ein Go-File protocol.pb.go
das in das gleiche Verzeichnis gelegt wird. Das erstelle Go-File hat über 200 Zeichen, aber um sicherzustellen dass wir den gleichen Output haben, sind die ersten Zeilenblöcke hier ersichtlich:
|
|
Microservice-Architektur mit Go
Der Server
Jetzt schreiben wir ein server.go-Microservice, der eine GrpcService
-Invocation von einem Client entgegen nehmen kann. Schaut euch die Funktion func (*grpcServer) GrpcService()
dabei genauer an.
|
|
Bevor man den server.go kompilieren kann, müssen zuerst noch die externen Dependencies gezogen werden. Einfach folgende go-Befehle in der Konsole ausführen:
|
|
Danach kann man den Server kompilieren und starten.
|
|
Der Client
Jetzt schreiben wir ein client.go-Microservice, der eine GrpcService
-Invocation an einem Server vornimmt. Schaut euch die Funktion func callService()
dabei genauer an.
|
|
Jetzt können wir auch den Client kompilieren und laufen lassen:
|
|
:white_check_mark: Super, hat geklappt !
Hier sehen wir die Response vom Server. Dabei printet wir den ganzen binären Output heraus.
Grpc-Server auf Kubernetes
Jetzt können wir das ganze Konstrukt noch in unseren Kubernetes-Cluster deployen.
Requirements
-
Docker-Kenntnisse und Zugang zu einer Docker-Repository
-
Kubernetes-Kenntnisse und Zugang zu einem Kubernetes-Cluster
-
Falls Sie das nicht haben, können Sie gerne schauen was wir hier machen
Das Dockerfile für den grpc_server
:
|
|
Kubernetes-Manifest :
|
|
Sobald Sie auf dem Kubernetes-Cluster verbunden sind, kann man das Manifest wie folgt applizieren:
|
|
Jetzt kann man die externe IP-Adresse herausfinden, um so den Client danach auszurichten:
|
|
Jetzt sind Sie dran! Wie viel performanter ist jetzt eine gRPC-Schnittstelle im Vergleich mit einer CRUD-Schnittstelle?
Stay tuned !