NGINX Unit mit NodeJS

Was ist NGINX Unit?

NGINX Unit ist ein Applikations- Server, der verschiedene Sprachen, wie beispielsweise PHP, Perl oder Go, unterstützt. Dabei kann eine NGINX Unit Applikation im Umfeld eines NGINX Plus oder NGINX Open Source Systems oder auch als Standalone-Applikation betrieben werden. NGINX Unit bietet eine RESTful JSON API und kann darüber zur Laufzeit, ohne Betriebsunterbrechungen konfiguriert werden. Welche Features NGINX Unit noch bietet kann hier nachgelesen werden.

Vorteile von NGINX Unit

  • NGINX hat berets langjährige Erfahrung im Bereich WebServer / HTTP / TLS / Zertifikatsmanagement / ReverseProxy / LoadBalancing etc.
  • Durch die RESTful JSON API kann die NGINX Unit Anwendung zur Laufzeit und von außen ohne Downtime (um-)konfiguriert werden
  • NGINX Unit Anwendungen können mit dem NGINX Controller einfach integriert und verwaltet werden
  • Der Fokus kann voll und ganz auf die Business Logik gelegt werden
  • NGINX Unit unterstützt unterschiedliche Programmiersprachen (z.B. Go, Python, NodeJs, PHP, …) und bietet die Möglichkeit auch verschiedene Programmiersprachen in einer NGINX Unit zu kombinieren


NGINX Unit mit einer NodeJs Applikation

Die Basis

Bevor mit der Entwicklung einer Applikation begonnen wird, sollten sich Gedanken darüber gemacht werden, in welcher Umgebung die Applikation später laufen wird. Soll NGINX Unit nativ auf einem Betriebssystem installiert werden, oder wird es doch eher in einem Docker-Container laufen? Wir haben uns für die Docker-Variante entschieden, da wir dadurch sehr flexibel und unabhängig von dem darunter liegenden System sind. NGINX stellt hierfür bereits vorkonfigurierte Docker-Images zur Verfügung. Dafür gibt es verschiedene Varianten – eine Full-Variante, die alle Programmiersprachen unterstützt und jeweils eine sprachspezifische Variante (SIEHE DOCKER HUB). Leider ist das mit den “alle Programmiersprachen” so nicht vollständig richtig. Bereitgestellt werden Images für PHP, Python, Perl, Ruby und Go. NodeJs ist in dieser Aufzählung jedoch nicht vorhanden. Daher mussten wir uns für diesen Fall etwas anderes einfallen lassen.

NGINX Unit – NodeJs Docker Image

Um NodeJs mit NGINX Unit zu nutzen, musste also ein passendes Image gebaut werden. Hierfür wurde das nginx/unit:1.18.0-minimal als Basis verwendet, danach folgte die manuelle Installation aller Komponenten und das anschließende hinzufügen unserer Applikation, gefolgt von der Konfiguration mit NGINX Unit. Außerdem wird das npm package unit-http global installiert, das wir später in unserer App mit einem sym-link durch npm link unit-http verknüpfen. 

Tipp: Damit während der Entwicklung nicht immer ein neues Image erstellt werden muss, mounten wir unsere Applikation über ein Laufwerk in den Docker-Container, somit muss nur ein Neustart von NGINX erfolgen. Damit kann im Zeitraum der Entwicklung sehr viel Zeit eingespart werden.

Die Anwendung

Der Fokus dieses Artikels liegt aber nicht auf der Anwendung, sondern bei der richtigen Integration in einen NGINX Unit Container. Gerade deshalb wird nicht weiter auf die Funktionalität oder eine Beispiel-Anwendung eingegangen, sondern nur, worin es sich zu einer NGINX Unit Anwendung unterscheidet. Grundsätzlich lässt sich sagen, dass jede beliebige NodeJs Anwendung in einem NGINX Unit Container laufen kann.

Was gibt es zu beachten?
  • Der Einsprungspunkt der Anwendung (z.b. index.js/ts) muss mit dem shebang #!/usr/bin/env node gekennzeichnet werden (Zeile 1). Damit weiß NGINX Unit, dass es sich um eine NodeJs Anwendung handelt und kann den entsprechenden Interpreter nutzen. 
  • Um einen Express-Server zu erstellen, wird nicht das normale express Package, sondern das unit-http Package verwendet (Zeile 4). Zusätzlich muss ServerResponse und IncomingMessage des http Packages durch die aus dem unit-http Package überschrieben werden (Zeilen 7 & 8). Danach kann das express Package importiert werden (Zeile 13). Gestartet wird der Server durch createServer(app) (Zeile 16), das nicht wie gewohnt aus dem express, sondern aus dem unit-http Package, kommt. 
    HinweisWichtig ist, dass express erst nach dem Überschreiben der ServerResponse und IncomingMessage importiert wird, da express selbst nichts von NGINX Unit weiß und ServerResponse und IncomingMessage direkt aus dem http Package importiert. Dies lässt sich folgendermaßen darstellen:

  • Die Kommunikation zwischen NGINX Unit und unserer Anwendung wird vollständig von NGINX Unit übernommen. Später können, auf diese Weise, durch die Konfiguration, bereits zur Laufzeit Ports konfiguriert werden. Das ist auch der Grund, warum beim Erstellen des Servers (Zeile 16) kein bestimmter Port angeben werden muss.
  • Die Anwendung selbst muss execute-Rechte besitzen, damit NGINX Unit es auch ausführen kann. 

NGINX-Unit Konfiguration

Das fertige Docker-Image, in dem unsere Anwendung liegt ist nun vorhanden und der Container kann gestartet werden. Ein maßgeblich entscheidender Teil – die NGINX Unit Konfiguration – fehlt allerdings noch.

Zum Einspielen gibt es zwei verschiedene Möglichkeiten:
  • NGINX Unit stellt eine RESTful API zur Verfügung, durch die die Konfiguration, beispielsweise mit einem einfachen CURL Befehl, eingespielt werden kann. Mehr Infos dazu gibt es hier
  • Eine Konfigurations-Datei in den Docker-Container legen, die beim Starten des Containers geladen wird.

Wir haben uns für die zweite Möglichkeit entschieden. Dafür kann ein einfaches JSON File angelegt werden, das daraufhin in das Verzeichnis /docker-entrypoint.d/ kopiert (oder gemountet) wirdDiese Möglichkeit ist ideal, um eine initiale Konfiguration einzuspielen. Die Konfiguration sieht dann z.B. so aus:

Zu sehen ist, dass der Host und Port (in diesem Fall “*:9000”) definiert wird. Wenn die Applikation von außerhalb erreichbar sein soll, muss dieser Port entsprechend im Docker Container auch exposed werden. Der pass Wert beschreibt, wo die Request hingeleitet werden soll (“application/express_project”). Das “express_project” selbst wird unter applications definiert und beinhaltet den Einstiegspunkt unserer Applikation “/www/build/index.js”

Eine detaillierte Übersicht der Konfigurationsmöglichkeiten gibt es hier.

Ausblick

Wenn das Docker-Image erfolgreich erstellt, die Konfiguration eingespielt und die Anwendung in einem Docker Container gestartet wurde, kann die API unserer NodeJs Anwendung über den Port 9000 erreicht werden.

In einer Microservice Architektur ist es wichtig, einen Service skalieren zu können. Um dieses Verhalten zu gewährleisten, kann beispielsweise ein NGINX bzw. NGINX Plus, als Loadbalancer und Proxy vorgeschalten werden.

Zusätzlich kann ein NGINX Controller für einfache App-Lifecycle- oder CI/CD-Workflow-Verwaltung eingesetzt werden. Außerdem bietet der NGINX Controller ein API Management Modul. Darüber lassen sich APIs veröffentlichen, absichern, überwachen und analysieren – mit minimalen Performance-Einbußen.