Teil 1 – Servlerless für alle

Serverless glücklich: Eine Einführung in Oracles Fn Project

Sven Ruppert

Der Begriff Serverless ist in aller Munde und meist verbunden mit einem der großen Cloud-Anbieter wie Amazon, IBM, Microsoft oder Google. Aber was kann man damit eigentlich machen? Wie funktioniert das ganze und wie kann man sich dem Thema nähern? Und noch wichtiger: Kann man Serverless auch ohne öffentliches Rechenzentrum verwenden? In unserer Artikelserie „Serverless glücklich“ geht Sven Ruppert, Developer Advocate bei Vaadin, auf diese und weitere Fragen ein und bietet einen Überblick über Oracles Fn Project.

Kann man Serverless auch ohne öffentliches Rechenzentrum verwenden? Die kurze und populäre Antwort hierauf lautet schlicht Yes, we can. Aber sehen wir uns zu Beginn der Serie erst einmal an, wo der Begriff „Serverless“ eigentlich herkommt bzw. was man landläufig oftmals darunter versteht.

Serverless – das kommende Buzzword

Der Begriff selbst deutet schon an, worum es geht. Unterm Strich nämlich darum, ohne zentralen Server auszukommen. Nun sind wir nicht auf dem Weg, P2P-basierte Strukturen aufzubauen, sondern es geht darum, das die logische Einheit Server aufgebrochen wird.

Der Grundgedanke ist recht einfach. Angenommen, eine Applikation ist die Summe ihrer einzelnen logischen Funktionen, so kann man sich die logische Einheit Applikation auch genau so vorstellen, also als ein Verbund einzelner logischer Funktionen, gebündelt zu einer Einheit Applikation. Demnach ist die Applikation nur noch eine abstrakte Einheit, die keineswegs der impliziten Annahme folgen muss, statischer Natur zu sein.

Wenn nun eine Applikation eine polymorphe Menge an einzelnen atomaren Funktionen darstellt, so ist die Verpackungseinheit Server nicht mehr dem Problem adäquat. Vielmehr gehen wir nun davon aus, dass es sich hier um ein verteiltes System handeln kann, das demnach einige der für ein solches Gebilde üblichen Merkmale aufweist. Hier sprechen wir explizit von der sogenannten „Ortstransparenz“. Die Funktionen selbst sind nicht geographisch oder technisch an einen Ort gebunden. Daraus folgt, dass die jeweilige logische Funktion über Rechnergrenzen hinweg frei verteilt werden kann.

Die logische Entität einer Funktion kann aus beliebig vielen Klonen bestehen. Die logische Funktion selbst ist also von der Anzahl der Klone nicht beeinflusst, solange diese einer Menge größer null entspricht. Und schon ist das Thema der Skalierbarkeit ein rein technisches und kein allgemein algorithmisches Problem mehr.

DevOpsCon Whitepaper 2018

Free: 40+ pages of DevOps expert knowledge

Learn about Containers,Continuous Delivery, DevOps Culture, Cloud Platforms & Security with articles by experts like Kai Tödter (Siemens), Nicki Watt (OpenCredo), Tobias Gesellchen (Europace AG) and many more.

Funktionen selbst sollten atomarer Natur sein, können jedoch zu logischen Transaktionen zusammengeschaltet werden. Eine Funktion wiederum kann aus anderen Funktionen erstellt werden. Hier ist ebenfalls keine Begrenzung der Lokalität anzunehmen. Wenn dieser Schritt konsequent weiter verfolgt wird, so ergeben sich Listen oder Bäume, bzw. generischer formuliert, gerichtete Graphen.

Alle diese Dinge in Kontext gesetzt und gemeinsam betrachtet, ergibt sich dennoch in den meisten Fällen ein sehr einfaches Gebilde. Die Eleganz liegt hier in der sehr klar definierten Struktur und der frei wählbaren Kombination der einzelnen Elemente untereinander.

Oracle Fn Project – der technische Ansatz

Der technische Ansatz selbst ist nicht so kompliziert. Wenn eine Funktion atomar und zustandslos sein soll, so gibt es keinen Zustand, der gehalten werden kann bzw. darf. Demnach ist die Bezeichnung Function schon sehr gut. In Java gibt es das Interface Function<A,B>, aber das ist hier nicht zwangsweise vorgesehen. Die Anforderung kann weniger hart formuliert werden, es ist lediglich ein Methodenaufruf. Diese Methode hat n Eingangsparameter und einen Ausgangswert. Klassenattribute sollten nicht zum Einsatz kommen, da nicht sichergestellt ist, ob und wenn ja wie oft diese Instanz schon verwendet worden ist.

Als Ablaufumgebung kann man sich nun eine JVM vorstellen, die genau für einen Aufruf erzeugt, verwendet und nachfolgend evtl. vernichtet wird. Um diese Java Virtual Machine zu isolieren, wird sie allerdings in einen Docker-Container verpackt. So kann man sich nun Oracles Fn Project vorstellen, nur dass dieses Projekt noch eine Menge an technischen Dingen drumherum abbildet. Gemeint ist hier zum Beispiel das Management der Docker-Container selbst.

Vorbereitungen

Um nun mit der Verwendung zu beginnen, sind einige technische Voraussetzungen notwendig. Als Ablaufumgebung kommt Docker zum Einsatz. Die Installation von Docker ist nicht Bestandteil dieses Artikels, dafür verweise ich auf die verfügbare Dokumentation, die es in recht vielen Sprachen und für die gängigen Betriebssysteme im Internet zu finden gibt.

Das Fn Project selbst bietet Unterstützung auf Kommandozeile an und die Installation erfolgt auf Linux/Unix-basierten Betriebssystemen sehr einfach mittels curl

 
curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh

Nachdem die Dateien geladen und installiert worden sind, kann mit der Verwendung begonnen werden.

Hello World in Serverless

Da nach der Eingabe des Befehls die Voraussetzungen erfüllt sind, kann der Fn Server gestartet werden. Hierzu kann man nun via Kommandozeile mit dem Befehl fn start den Startprozess in Gang setzen. Bei der ersten Verwendung wird das dazu benötigte Docker Image aus dem Internet bezogen, was demnach zu einer kleinen Verzögerung führen könnte. Es heißt also, Geduld zu haben. Sobald alles lokal vorhanden ist und der Fn Server Container erzeugt sowie gestartet worden ist, wird auf der Kommandozeile der erfolgreiche Start quittiert:

Svens-Mobile-CI:~ mobileci$ fn start
time="2018-03-28T15:20:04Z" level=info msg="Setting log level to" level=info
# SNIPP .......
# SNIPP .......
# SNIPP .......
time="2018-03-28T15:20:04Z" level=info msg="available cpu" availCPU=4000 totalCPU=4000
time="2018-03-28T15:20:04Z" level=info msg="sync and async cpu reservations" cpuAsync=3200 cpuAsyncHWMark=2560 cpuSync=800

        ______
       / ____/___
      / /_  / __ \
     / __/ / / / /
    /_/   /_/ /_/
        v0.3.397

time="2018-03-28T15:20:04Z" level=info msg="Fn serving on `:8080`" type=full

Die Ausgabe gibt einem nochmals den Hinweis, welche Version aktiv ist und welcher Port verwendet wird. In diesem Fall ist es der Port 8080, der leider oft auch schon von anderen Projekten/Produkten belegt wird. Als nächstes wird die Hello World-Funktion erzeugt.

Die erste Funktion mit Fn Project

Oracles Fn Project liefert dem Nutzer einen Generator mit, der es ihm ermöglicht, einfach und bequem das notwendige Grundgerüst für eine Funktion zu erzeugen. Hierzu wird in das Verzeichnis gewechselt, in dem das Projekt seine neue Heimat finden soll. In diesem Fall wird ein Verzeichnis mit dem Namen vaadin-fnproject-001 erzeugt. Nachdem dort hineingewechselt worden ist, kann mit dem vorhin installierten Kommandozeilenwerkzeug die Grundstruktur für das Projekt erzeugt werden.

fn init --runtime=java --name [your_dockerhub_account]/hello

In diesem Befehl ist der Platzhalter [your_dockerhub_account] durch den gewünschten Login-Namen, der verwendet werden soll, zu ersetzen. Nachdem der Befehl durchlaufen ist, befinden sich in dem Verzeichnis alle benötigten Dateien und Verzeichnisse, die für ein mittels Maven organisiertes Projekt erforderlich sind. In dem Quelltextverzeichnis wurde unter anderem eine Klasse abgelegt, die aus lediglich einer einzelnen Methode besteht.

package org.rapidpm.fnproject.helloworld;

public class HelloFunction {

    public String handleRequest(String input) {
        String name = (input == null || input.isEmpty()) ? "world"  : input;
        return "Hello, " + name + "!";
    }
}

Es wird weder von irgendeiner Klasse abgeleitet, noch ein Interface implementiert oder irgendeine spezielle Klasse verwendet. Es handelt sich demnach um eine klassische, einfache Java-Klasse mit einer einfach Methode. Zusätzlich befindet sich in dem Verzeichnis für Test-Quelltexte ein auf JUnit 4 basierender Test. Diese Implementierung werden wir uns zu einem späteren Zeitpunkt noch genauer ansehen.

„func.yml“

Im Hauptverzeichnis dieses Projektes befindet sich noch eine Datei mit dem Namen func.yml. In dieser werden all die benötigten Metainformationen hinterlegt, die von dem System später für die Ausführung benötigt werden. Die meisten Schlüssel-/Wertpaare sind recht selbsterklärend. Jedoch möchte ich auf das Schlüsselwort cmd hinweisen. Bei dessen zugehörigem Wert handelt es sich um die Adresse der Methode, die bei einem Aufruf vom System verwendet werden soll. Zu beachten ist noch, dass die standardmäßig verwendeten Build und Runtime Images auf Java 9 basieren.

name: vaadin-fnproject-001
version: 0.0.2
runtime: java
cmd: org.rapidpm.fnproject.helloworld.HelloFunction::handleRequest
build_image: fnproject/fn-java-fdk-build:jdk9-1.0.59
run_image: fnproject/fn-java-fdk:jdk9-1.0.59
format: http

Das Schlüssel-/Wertpaar version wird bei jedem Deployment automatisch inkrementiert. Das geschieht auch, wenn das Deployment fehlgeschlagen ist, womit dieses nicht in der Verantwortung des Entwicklers liegt. Wie es aussieht, wenn mehr als ein Entwickler an der gleichen Funktion arbeitet, werden wir ausprobieren.

Der erste Lauf

Da nun alle notwendigen Teile für unser Hello World vorhanden sind, kann der erste Start durchgeführt werden. Als Ablaufumgebung dient hier die lokal verfügbare Docker-Umgebung. Die generierte Java-Klasse muss nun übersetzt und in einen Docker Container verpackt werden, damit der Aufruf erfolgen kann. Auch hierfür gibt es Unterstützung durch das Kommandozeilenwerkzeug fn. Mittels des Befehls fn build wird der Übersetzungs- und Erzeugungsprozess gestartet. Der erste Durchlauf wird ein wenig länger dauern, da zuerst die benötigten Images aus dem Internet geladen werden müssen.

Nachdem das Image erstellt worden ist, können wir es einmal ausprobieren. Die Implementierung der Methode ist so erstellt worden, dass ein fehlender Parameter nicht zu einer Exception führt. Somit reduziert sich die erste Ausführung auf den Aufruf selbst. Ein auf der Kommandozeile ausgeführter Befehl fn run startet das Image in der lokalen Docker-Umgebung. Auf dem Bildschirm erscheint unser Hello World.

Allerdings wurde in der Implementierung in diesem Fall kein Line Break an den String angefügt. Wenn man das nun nachholen möchte, wird wie folgt vorgegangen: Der Quelltext wird editiert, in diesem Fall wird einfach System.getProperty("line.separator"); an den finalen String angehängt. Nun muss der Quelltext neu übersetzt, das Image wieder gebaut und die Ausführung ein weiteres mal gestartet werden.

  • Quelltext ändern
  • fn build
  • fn run

Um nun der Methode einen Übergabeparameter zu übergeben, kann man bei dem lokalen Aufruf einfach per Std.In einen String übergeben.

echo -n "Ohhh no" | fn run

Hiermit ist der generelle Arbeitsablauf fertig und mit der Entwicklung kann begonnen werden. Was an dieser Stelle noch ausgelassen worden ist, ist das Thema Test-driven Development (TDD), das zu einem späteren Zeitpunkt in dieser Serie angegangen werden wird.

Das erste Deployment

Bisher haben wir uns lediglich angesehen, wie man die erstellte Funktion einmalig aufrufen kann. Das ist sicherich ausreichend in der Entwicklung, allerdings nicht in der Praxis verwendbar. Daher kommen wir nun zum Deployment der Funktion, sodass diese in einer Serverlandschaft zur Verfügung gestellt werden kann. Auf der Kommandozeile kann das Deployment in die lokale Docker-Umgebung mit dem folgenden Befehl initiiert werden:

fn deploy --local --app vaadin-fnproject-001

Der Befehl besteht aus drei Elementen.

  • deploy
  • --local, damit der lokale Docker Host verwendet wird
  • --app, der logische Applikationsname vaadin-fnproject-001

Nachdem der Deployment-Prozess erfolgreich beendet worden ist, steht die Funktion zur Verfügung. Um diese nun zu testen, kann man zum Beispiel mit dem Befehl curl die Zieladresse aufrufen. Die URL besteht aus

  • einem statischen Teil http://localhost:8080/r/,
  • einem Appklikationsnamen app-name und
  • dem Funktionsnamen function-name.

Daraus ergibt sich in diesem Fall die folgende URL im Gesamtaufbau:

curl http://localhost:8080/r/vaadin-fnproject-001/vaadin-fnproject-001

Hieraus lässt sich schon erkennen, wie wichtig eine gewisse Ordnung bei der Vergabe der Namen ist.

Fazit

Wir haben nun das grundlegende Rüstzeug, um mit der Entwicklung von Funktionen zu beginnen. In den nächsten Teilen werden wir uns ansehen, was es noch alles für spannende Ansätze, Tools und Funktionen gibt und wie wir sie einsetzen können.

Geschrieben von
Sven Ruppert
Sven Ruppert
Sven Ruppert arbeitet seit 1996 mit Java und ist Developer Advocate bei Vaadin. In seiner Freizeit spricht er auf internationalen und nationalen Konferenzen, schreibt für IT-Magazine und für Tech-Portale. Twitter: @SvenRuppert
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: