comSysto bei XING

06
.
06
.
2017

Selbstskalierende Microservice Infrastruktur - PART III

Teil drei mit Frontend Deployment und Hot-Reload

selbstskalierende Self Contained Microservice Infrastruktur auf AWS mit Boxfuse am Beispiel der Lebenslauf-Management-App SeeVee.

Bernhard

Software Engineer - Java, JVM

EINLEITUNG

Bereits in Teil I und Teil II der BlogPost-Serie haben wir uns mit dem SeeVee-Backend befasst, um für das Lebenslauf-Management Projekt SeeVee eine Self Contained Service Microservice Infrastruktur in der AWS Cloud aufzusetzen.

Ziel von Teil III der BlogPost-Serie ist nun das SeeVee-Frontend zu deployen und komfortable Entwicklung auf der lokalen Workstation zu ermöglichen.

 

 

ANFORDERUNGEN UND VORBEREITUNG

Wir haben es bereits im Teil I der Serie definiert, wiederholen es an dieser Stelle aber noch einmal kurz. Die Anforderungen an das SeeVee-Frontend sind wie folgt.

Frontend Anforderungen
‍Frontend Anforderungen

 

Wir haben in Teil I bereits die ‘seevee-frontend’ Applikation in der Boxfuse Web-GUI angelegt. Damit wir auch für das Frontend schöne Domainnamen nutzen können legen wir analog zum Backend die DNS CNAME und A-Einträge für alle Environments fest.

Frontend DNS Einträge
‍Frontend DNS Einträge

Boxfuse hat seit kurzem ein neues Feature welches die Custom-Domains automatisch im ‘AWS Route53’ DNS Service anlegen kann und den Boxfuse-Anwendungen zuordnet. Bisher wusste Boxfuse nichts von den Custom-Domains - jetzt weiß Boxfuse bescheid und zeigt uns auch in der Web-GUI unsere Custom-Domains an. Dazu muss aber AWS Route53 die DNS-Einträge managen. Für SeeVee sieht das momentan so aus. Wichtig ist, dass man nun keine CNAME Einträge mehr manuell anlegt, sondern Boxfuse das tun lässt.

DNS für seevee.io
‍DNS für seevee.io

In der Boxfuse Web-GUI sieht es nun wie folgt aus.

Boxfuse Custom Domains
‍Boxfuse Custom Domains

Beachten sie bitte, dass:

  • Sie ihre IAM Role für Boxfuse updaten und ‘route53:’ eingefügen, sonst sieht Boxfuse die Domains nicht (nur bei bestehenden Usern, die vorher schon boxfuse genutzt haben).
  • Bestehende CNAMEs muss man löschen damit Boxfuse diese neu anlegen kann. Boxfuse legt dabei dynamische A Aliase auf den ELB an.

Damit wir in der nächsten Iteration loslegen können sollte NodeJS 7.5+ auf ihrem System installiert sein.

sure ...
‍sure ...

 

 

ITERATION 5: FRONTEND MIT SSL ABGESICHERT LOKAL STARTEN

Da wir ja bereits ein Frontend-Projekt haben, welches unsere Anwendung mittels Webpack baut, holen wir uns nur die Teile herein, die wir brauchen. Wenn man das ganze ‘from-scratch’ aufsetzen will, kann man sich den Get-Started-Guide absehen. Wir erstellen uns aber einen custom NodeJS Express-Server innerhalb unserer existierenden Anwendung. Die Anwendungs-Architektur sieht wie folgt aus.

SeeVee-Frontend App-Structure
‍SeeVee-Frontend App-Structure

Die Build-Chain kann man sich bei GitHub herunterladen:

https://github.com/comsysto/express-react-boxfuse-starter

Warum Express Server mit NodeJS? Diese Frage ist berechtigt. Es hätte auch ein NGINX sein können, welcher einfach statisch die Anwendung ausliefert. Jedoch wollten wir folgende wichtige Dinge berücksichtigen.

  • Einheitliche NodeJS Build-Chain.
  • Boxfuse Livereload Feature nutzbar für Express Server.
  • Serverseitige Endpoints für Environment-Detection.
  • Möglichkeit serverseitig React-Componenten zu rendern.

Für uns geht es jetzt nicht darum riesige serverseitige Features mit Express zu implementieren. Wir wollen aber die Möglichkeit haben kleinere Dinge serverseitig zu tun, die aber rein mit dem Frontend zu tun haben, wie bspw. die Environment-Detection.

Warum eine Anwendung in der Anwendung und zwei ‘package.json’? Ich habe lange überlegt, wie man eine klare Trennung hinbekommt und es trotzdem einfach halten kann. Denn die Dependencies aus ‘./package.json’ sind rein für die Browser-Anwendung (React, Lodash usw.) aber die ‘./server/package.json’ Dependencies werden einzig für den serverseitigen Express-Server benötigt. Sie haben nichts miteinander zu tun, daher habe ich es klar nach Separation of Concerns getrennt.

Bauen der Browser-Anwendung

  • ‘npm install’ installiert die Dependencies aus ‘./package.json’ stößt anschließend durch einen postinstall-Hook ‘npm install’ für die ‘./server/package.json’ an.
  • Webpack-Build erzeugt ‘./server/public/bundle.js’ aus den JavaScript Sourcen unter ‘./src/’.
  • Webpack-Build erzeugt ‘./server/public/bundle.css’ aus den Sass-Dateien unter ‘./scss/’.
  • Webpack-Build kann mittels ‘watch’ gestartet werden, um bei Änderungen an den Dateien die Bundles neu zu bauen.

Bereitstellen des Express-Servers

  • Hochfahren des Express-Server mit Boxfuse unter ‘./server/’.
  • Ausliefern von ‘bundle.css’ und ‘bundle.js’ als statischen Content mit Hot-Reload.
  • Start eines HTTPS abgesicherten Servers mit Wildcard-Zertifikat aus ‘./server/ssl/’.
  • Ausliefern der Index-Seite ‘/’ durch Express-Route mit basis HTML und Referenz auf CSS+JS Bundle.
  • Bereitstellen von globalen JS-Variablen zur Environment-Detection auf der Index-Seite.
  • Ausliefern der Info-Seite ‘/info’ als JSON mit Deployment-Informationen.

 

Wir fahren die Anwendung also wie folgt hoch. Zuerst klonen wir das Git-Repository.

shell:git clone https://server.com/seevee-frontend-boxfuse.git && cd seevee-frontend-boxfuse

Nun installieren wir die Browser- und Server-Dependencies.

shell:npm install

Als nächstes starten wir mit boxfuse den Express-Server mit Hot-Reload feature. Da wir wegen dem GitHub oAuth einen fixe URL brauchen, müssen wir auch den HTTP-Port fest vorgeben. Daher starten wir mit folgendem Parameter, damit der Server auf TCP Port 10444 gebunden wird und Boxfuse auch erkennt, dass wir mit HTTPS arbeiten wollen.

shell:cd server

 

shell:boxfuse run -ports.https=10444 -live

Nun starten wir den Webpack-Build zum erstellen der JS+CSS Bundles, welcher auch mit ‘watch’ startet.

shell:cd ../

 

shell:npm run develop

Das ganze sieht dann wie folgt aus.

Boxfuse run Frontend mit Hot-Reload
‍Boxfuse run Frontend mit Hot-Reload

Ok sehr schön. Wir haben unser Frontend lokal hochgefahren und Änderungen am Code werden hot-reloaded. So können wir komfortabel lokal arbeiten und entwickeln.

 

 

ITERATION 6: ENVIRONMENT-DETECTION FÜR FRONTEND UND BACKEND

Wir haben ja bereits ausführlich die Bedeutung der verschiedenen Environments erklärt. Jedoch ist vielen Leuten unter Umständen nicht klar, dass auch die Anwendungen selbst erkennen müssen in welchem Environment sie laufen - die sogenannte Environment-Detection. Ich habe viele Anwendungen gesehen, die das Anhand von URL-Erkennung oder sonstiger ‘Magic’ machen. Wenn man mich fragt, sollte man das aber lieber durch Environment-Variablen lösen und so der Anwendung mitteilen, in welchem Environment sie läuft. Bei einer Java-Anwendung wie dem SeeVee-Backend haben wir das ja bereits gesehen.

Warum brauche ich Environment-Detection? Gute Frage. Ich fasse die Gründe kurz zusammen.

  • Die Anwendung muss erkennen können in welchem Environment sie selbst läuft.
  • Die Anwendung muss erkennen können welche Services sie in welchen Environments ansteuern soll. (Backend, APIs usw.)
  • Unterschiedliches Verhalten pro Environment bspw. Google Analytics ist nur im prod-Environment scharf geschaltet.

Da es in der browserseitigen JavaScript Welt so etwas wie Environment-Variablen nicht gibt, verwenden wir dazu Globale Variablen in folgender Form.

github:a707ee863374618514ee60b6bf30a349

Die Anwendung kann nun Anhand dieser Variablen Entscheidungen treffen, welches Backend sie ansteuert und wie sie sich bzgl. GitHub oAuth verhält. Wir reichen Boxfuse Environment Variablen durch den Express-Server zum Browser durch. Das ist im Detail in folgendem Screenshot dargestellt.

Environment-Detection
‍Environment-Detection

Dabei ist die ‘SEEVEE_FRONTEND’ variable durch die von Boxfuse bereitgestellte ‘BOXFUSE_ENV’ variable definiert. Die ‘SEEVEE_BACKEND’ Variable ergibt sich per Convention-Over-Configuration aus der ‘SEEVEE_FRONTEND’ Variable, außer man überschreibt diese manuell.

Warum brauche ich zwei Variablen? Es handelt sich ja um zwei Services die miteinander arbeiten sollen. Das Frontend und das Backend. Normalerweise wird das ‘test’-Frontend auch das ‘test’-Backend ansteuern. Das ‘prod’-Frontend wird das ‘prod’-Backend ansteuern. Doch in der Regel entwickeln Frontend-Entwickler am SeeVee-Frontend und diese wollen meist kein lokales Backend starten. Deshalb ist der Default Fall, dass das ‘dev’-Frontend auf das ‘test’-Backend verbindet. Nur wenn man wirklich beides lokal starten will, kann man das Frontend mit folgendem Befehl starten und die ‘SEEVEE_BACKEND’ Variable auf ‘dev’ setzen.

shell:boxfuse run -ports.https=10444 -live -envvars.SEEVEE_BACKEND=dev

 

 

ITERATION 7: DEPLOYMENT AUF STAGINGUMGEBUNG

Wir haben in den vorherigen Iteration gesehen wie wir ein ‘exploded’-Projekt starten, doch nun wollen wir einen fixen Versionsstand auf der Stagingumgebung durch Jenkins deployen lassen.

Im Teil I haben wir das Backend zuerst ohne HTTPS auf Port 8080 deployed. Danach mussten wir einiges in der AWS Console anpassen und darauf wollen wir diesmal verzichten. Wir wollen Boxfuse direkt mitteilen, dass wir eine mit HTTPS abgesicherte Anwendung auf TCP Port 443 deployen wollen. So kann Boxfuse die Anwendung initial richtig einrichten und wir müssen nichts manuell anpassen.

Zuerst müssen wir aber die Anwendung bundlen und die dependencies einfrieren. Dazu nutzen wir npm-bundle und npm shrinkwrap.

Unsere fertige ‘jenkins.sh’ sieht dann wie folgt aus.

github:983770998995043888edb85810c5859c

Das sieht erstmal viel aus aber eigentlich geschieht nur folgendes.

  • NodeJS bereitstellen
  • Boxfuse Client bereitstellen
  • Browser-Anwendung bauen (bundle.js + bundle.css)
  • Version mit Jenkins Build-Number anreichern
  • Build-Informationen in ‘./server/routes/index.js’ einfügen
  • Server-Bundle erzeugen
  • Mit Boxfuse je nach Branch auf das entsprechende Environment deployen

Für die package.json Version wir müssen leider die dritte Stelle ‘misbrauchen’, denn npm beschwert sich sonst wenn wir 0.0.0.1 machen würden. Das ist nicht erlaubt nach SEM. Was andererseits unter npm geht, also bspw. 0.0.0-1 ist wiederum von Boxfuse nicht erlaubt. Daher machen wir 0.0.1 und verwenden die letzte stelle für BUILD_NUMBER.

Ein Starter-Template für unsere Applikation kann man hier herunterladen: github.com/comsysto/express-react-boxfuse-starter

Boxfuse führt ein echtes Zero-Downtime Deployment durch. Es fährt eine komplett neue Auto Scaling Group mit Instanz hoch, switcht am ELB auf die neu deployte Version um und zerstört die alte Version. Somit hat man Zero-Downtime. Das sieht man sehr gut in der Jenkins Console.

Boxfuse Zero Downtime Deployment
‍Boxfuse Zero Downtime Deployment

Mit Pug stellen wir den Index und Info-Endpoint bereit, welchen wir nach erfolgreichem Deployment mit einem Test-Curl abfragen können.

shell:curl -s https://app-test.seevee.io/info | jq '.'
SeeVee-Frontend Test Curl
‍SeeVee-Frontend Test Curl

 

Wir haben alle Frontend-Anforderungen umgesetzt. Und sind nun mit unserer Microservice-Infrastruktur sehr zufrieden.

umgesetzte Frontend Anforderungen
‍umgesetzte Frontend Anforderungen

 

 

ITERATION 8: CROPPING, THUMBNAIL UND PNG IM BROWSER-ANWENDUNG

In Teil II hatte ich angemerkt, dass wir das Image-Cropping ins Frontend ausgelagert haben. Bisher hatten wir im Backend mittels Imagemagick serverseitig Bilder gecroppt und Thumbnails erstellt.

Wir nutzen nun im Frontend den react-cropper, welcher uns bereits im Browser Base64 Data URIs jeweils des Thumbnails und des zugeschnittenen Originalbilds erstellt. Das sieht dann so aus:

Portrait Cropping im Frontend
‍Portrait Cropping im Frontend

Im Code lassen wir uns einfach direkt nach dem Croppen die Base64 Data URIs generieren und speichern diese 1:1 über die SeeVee-Backend REST API in die Datenbank.

Base64 Data URIs vom Cropper
‍Base64 Data URIs vom Cropper

Somit brauchen wir im SeeVee-Backend keine serverseitige Bildoptimierung mehr vornehmen und können auf Imagemagick verzichten. Alle Browser unterstützen das mittlerweile via ‘Canvas’.

 

 

ITERATION 9: MICROSERVICES GEGEN HEARTBLEED ABSICHERN (SSL)

Wir sind jetzt eigentlich fertig, wollen aber noch als letztes überprüfen, ob unsere Microservices auch wirklich auf dem Stand der Technik abgesichert sind. Lücken wie Heartbleed müsste mittlerweile jeder kennen. Damit unsere Microservices dagegen abgesichert sind, führen wir den SSL Test von Qualys aus und sichern unsere Services entsprechend ab.

Qualys SSL Labs - Grade A
‍Qualys SSL Labs - Grade A

Wir wollen Grade A erreichen welchen unsere NodeJS+ExpressJS Anwendungen bereits Out-of-The-Box haben. Bei den Spring Boot Anwendungen müssen wir etwas nachrüsten. Dazu muss SSLv3 deaktiviert sein und der Handshake gehärtet werden. In unserer Spring Boot Applikation fügen wir daher folgendes hinzu und holen uns in der ‘servletContainer’-Methode über die Environment Variable ‘BOXFUSE_PORTS_HTTPS’ den HTTPS TCP Port (dieser ist ja je nach Environment unterschiedlich).

github:ed030004fd92040e6e2dc7ba43a742a2

So gehärtet erreicht auch unsere Spring Boot Anwendung Grade A und wir haben nun eine sichere Ansammlung von self-contained Microservices.

 

 

FAZIT UND AUSBLICK

Wir haben gezeigt, wie man trotz des Einsatzes von Boxfuse nicht den Komfort von Hot-Reloading während der Frontend-Entwicklung verliert. Über die Environment-Detection ist sichergestellt, dass unsere Microservices auch richtig miteinander kommunzieren. Das ins Frontend ausgelagerte Image-Cropping funktioniert in allen Browsern und vereinfacht das Backend, durch den Verzicht auf serverseitige Bildoptimierung wie Imagemagick. Die Absicherung unser Webservices gegen SSL Exploits wie Heartbleed ist auch sehr einfach möglich. Alles in allem war die Migration auf Microservices sehr interessant und hat die Anwendung in sinnhafte Teilstücke unterteilt, die das Weiterentwickeln der Anwendung sehr vereinfachen, da man Aufgrund der Schlankheit und Single-Responsibility jedes Microservices sehr schnell versteht was dieser tut. Ich würde jede Anwendung dieser Art künftig gerne von Beginn an mit Microservices entwerfen. Boxfuse ist hier eine sehr große Hilfe und nimmt einem die lästigen Infrastruktur Aufgaben ab, sodass man sich zu 100% auf die Entwicklung konzentrieren kann.

Wenn auch Sie Ihre Dienste mit Boxfuse betreiben wollen und alle Vorteile von unveränderbarer Infrastruktur nutzen wollen, untersützen wir Sie gerne. Wir können in Workshops oder projektbegleitendem Coaching Ihre Mitarbeiter im Umgang mit Boxfuse schulen oder auch gemeinsam mit Ihnen Ihre Infrastruktur migrieren. Mehr Details finden Sie unter Architektur & Entwicklung. Wir freuen uns über eine Kontaktaufnahme, gerne auch ganz formlos über das unten angezeigte Kontaktformular.

Themen:

Kommentare?

comSysto Logo