Der ROXstack - Teil 1: ROXcomposer und ROXconnector - Microservices als Fundament eines Analyse-Stacks

Hier bei droxIT lautet unsere Vision: "Effiziente und universelle Datenverarbeitung für alle!" Als Weg dorthin haben wir die Entwicklung eines Analyse-Stacks auserkoren - ein Ökosystem von Analysemodulen, welche flexibel kombiniert werden können, um mit geringem Aufwand maßgeschneiderte Analyse-Pipelines zu erstellen. 

Nach anfänglichen Versuchen einer Umsetzung haben wir uns dazu entschlossen, die Infrastruktur dieses Systems zuerst anzugehen. Wir entwickeln aktuell ein Microservice-Framework, welches bereits alle architektonischen und infrastrukturellen Anforderungen erfüllt, sodass man sich bei der Entwicklung der Analysemodule nur noch um die Logik kümmern muss. Um dieses Framework dreht sich der erste Teil dieser Serie.

Warum Microservices

Wir starten mit einem kurzen Exkurs in die Vorteile von Microservices für die Umsetzung unserer Ziele. Der Vorteil von Microservices gegenüber klassischen, monolithischen Architekturen liegt in einer besseren Skalierung der Entwicklung, weil einzelne, stark kohärente Services von je einem Team entwickelt werden können und bei Bedarf weitere Teams an weiteren Services arbeiten können. Auch eine Performance-Skalierung ist leicht zu realisieren, sofern sich die Verarbeitung parallelisieren lässt, indem man mehrere Instanzen eines Service startet und die Last unter ihnen verteilt. Diese Arbeitsweise bedarf einer starken Entkopplung von Funktionalität - Services sollten nicht von Implementierungsdetails anderer Services abhängen. Im besten Fall sollten sie noch nicht einmal von der Existenz anderer Services wissen. Dieser Ansatz führt zu gut austauschbaren Code-Einheiten, welche sich einfacher testen und warten lassen. Zusätzlich eignen sich Microservices sehr gut für ein Cloud-Deployment.

Neben diesen allgemeinen Vorteilen haben wir noch für unseren Fall spezielle identifiziert: Microservices bieten sich ausgezeichnet dafür an, flexible Analyse-Pipelines zu erstellen und verschiedene Algorithmen gegeneinander zu benchmarken. Darüber hinaus kann man fertige Services wie einen Bausatz anbieten und Kunden damit lediglich für die Funktionalität zahlen lassen, welche sie auch benötigen. Dies führt auch zu einem Ressourcen-Verbrauch, der sich dicht am Bedarf orientiert. Ein letzter, aber sehr zentraler Punkt ist die einfache Erweiterbarkeit.

Damit all diese Vorteile zum tragen kommen, bedarf es allerdings solider Protokolle für die Kommunikation zwischen Services und klar definierter Datenstandards, welche möglichst generische Anknüpfpunkte zwischen Services bieten - nur so lässt es sich gewährleisten, dass eine bestehende Plattform effizient weiterentwickelt und gewartet werden kann. Aus dieser Überlegung heraus haben wir uns für die Entwicklung unseres eigenen Frameworks entschieden.

ROXcomposer und ROXconnector - eine Vorstellung

Gehen wir nun zu den eigentlichen Themen dieses Blogs über: ROXcomposer und ROXconnector.

ROXcomposer ist ein Entwicklungs-Framework und eine Infrastrukturlösung in einem: im Zentrum steht ein Kommunikationsprotokoll, welches ein sehr flexibles Routing zwischen Services erlaubt und prinzipiell von jeder Programmiersprache unterstützt werden kann. Dazu haben wir ein Python-Package erstellt, welches eine Service-Basisklasse mit diversen Schnittstellen liefert, auf Basis derer man sehr leicht eigene Services implementieren kann. Im einfachsten Fall reicht es, eine on_message-Funktion zu implementieren, welche die Verarbeitungslogik enthält - alles weitere, wie Transport der Nachricht, Logging- und Konfigurations-Schnittstellen und ein Message-Tracing sind in der Basisklasse bereits enthalten.

Hier das traditionelle "Hello, World!" für ROXcomposer:

from roxcomposer import base_service
  
class MyService(base_service.BaseService):
    def __init__(self, args):
        super().__init__(args)
  
    def on_message(self, msg):
        # usually we would do something useful with msg and dispatch that
        new_msg = "Hello, World!"
        self.dispatch(new_msg)
  
args = json_loads(sys.argv[1])
ms = MyService(args)
ms.listen()

Dieser Code erklärt zwar, wie man einen Service schreibt, allerdings stellt sich noch die Frage, wie man Pipelines definiert und diese mit Daten versorgt. Beim ROXcomposer-Message-Protokoll sind sämtliche Informationen zu Pipeline und Daten-Payload in der Nachricht selbst enthalten - es reicht also, eine solche zu erzeugen und an den ersten Service in der Pipeline zu schicken. Damit man dies nicht selbst machen muss, haben wir ROXcomposer ein API-Gateway hinzugefügt, welches diese und andere Funktionen mittels einer REST-API bereitstellt. Womit wir bei unserem zweiten Produkt wären:

Bei ROXconnector handelt es sich um einen schlanken, sehr flexiblen API-Server, geschrieben in Node.js. Er erlaubt es REST-APIs mittels Konfigurationsdateien zu beschreiben und bereitzustellen und ist über Plugins leicht erweiterbar.

Ein solches Plugin für ROXcomposer erlaubt es uns, Services zu starten und zu stoppen, Pipelines zu definieren und Daten in diese Pipelines zu schicken. Wir erhalten darüber Informationen über den Stand unseres Systems und können Message-Traces abfragen. Zusätzlich gibt es uns die Möglichkeit unsere Infrastruktur in Form eines JSON-Dokuments zu persistieren und wiederherzustellen.

Zu guter letzt haben wir noch eine CLI-Anwendung geschrieben, welche das Arbeiten mit der API noch einmal vereinfacht.roxcomposer-microservice-framework

Designentscheidungen

An dieser Stelle wollen wir einmal einige der getroffenen Designentscheidungen näher beleuchten.

Warum Python?

Python ist in der Data-Science-Community stark vertreten und liefert starke Bibliotheken für diese Zwecke: z.B. scikit-learn, NLTK, Theano und Pandas, um nur ein paar zu nennen. Abgesehen davon ist Python eine elegante und leicht zu erlernende Programmiersprache. Bei performance-kritischen Operationen kann man mittels Cython auf C-Module ausweichen, welche die eigentliche Arbeit erledigen.

Es sei an der Stelle noch einmal darauf hingewiesen, dass Services auch in anderen Sprachen geschrieben werden können, allerdings müssen alle out-of-the-Box-Features dann wieder implementiert werden. Ob droxIT weitere Sprachen umsetzen wird, ist bisher noch nicht entschieden.

Warum nicht direkt Docker?

Wir haben es noch nicht explizit erwähnt, aber aktuell werden ROXcomposer-Services als simple Prozesse auf dem Host-System gestartet - nicht als Container. Der Grund dafür ist der, dass es zunächst mal das einfachste Szenario ist und sich ohne weitere Infrastruktur verwenden lässt. Man kann sich die ROXcomposer-Package nehmen und direkt anfangen zu entwickeln und zu deployen (nach Installation der Python- und Node-Abhängigkeiten). Fortgeschrittene Deployment-Szenarien, wie Docker-Container oder Cloud-Deployment sind als weiterer Schritt gut umsetzbar und stehen bei uns auch auf der Agenda.

Dependency-Injection

Bei Peripherie-Diensten wie Logging und Monitoring bieten wir einen Injection-Mechanismus an, welcher es erlaubt, mittels Konfiguration andere Implementierungen zu verwenden. Zwar ist es unser Ziel eine Out-of-the-Box-Lösung anzubieten, welche bereits produktiv nutzbar ist, allerdings wird es immer Aspekte geben, die wir nicht abdecken oder bestehende Infrastrukturen, welche wir nicht direkt unterstützen.

Generell ist unser Designprinzip, dass ROXcomposer auch gut an die eigenen Anforderungen angepasst werden kann.

Stand und Ausblick

ROXcomposer befindet sich noch im Aufbau. Man kann es bereits nutzen, ist dabei aber z.B. noch auf einen Host beschränkt. In der näheren Zukunft sind sehr hilfreiche Features geplant: neben dem Sprung auf verteilte Systeme, ist eine Elastic Stack Anbindung geplant, über die dann Logs und Monitoring-Metriken verarbeitet werden können. Die CLI soll überarbeitet werden und ein Webfrontend soll folgen. Parallel soll auch der Aufbau eines Service-Portfolios beginnen.

Tags: droxit-analyse-stack

Print Email

Um unsere Webseite für Sie optimal zu gestalten und fortlaufend verbessern zu können, verwenden wir Cookies. Durch die weitere Nutzung der Webseite stimmen Sie der Verwendung von Cookies zu. Weitere Informationen zu Cookies erhalten Sie in unserer Datenschutzerklärung.
Weitere Informationen