Node.js auf dem Raspberry Pi installieren & „Hallo Welt“ Webserver starten: 2 empfehlenswerte Wege

Als Video ansehen
Bereitgestellt über YouTube

Node.js auf dem Raspberry Pi installieren & „Hallo Welt“ Webserver starten: 2 empfehlenswerte Wege

Mit Node.js hat Google JavaSkript mit der Laufzeitumgebung V8 aus dem Browser geholt. Die Skriptsprache kann nun auch zur Entwicklung von Desktop- oder Serveranwendungen genutzt werden. Daraus entwickelte sich ein Ökosystem. In diesem Beitrag zeige ich dir, wie du Node.js auf einem Raspberry Pi und anderen Debian-Servern installieren kannst. Zur Demonstration werden wir mittels JavaSkript einen Webserver starten. Diesen kannst du als Grundlage nutzen oder durch eigenen Code ersetzen – je nachdem, welches Projekt du umsetzen möchtest.

Macht Node.js auf dem Raspberry Pi überhaupt Sinn?

Die gängigste Skriptsprache in der Raspberry Pi Welt ist Python. Speziell für Einsteiger, hardwarenahe Projekte und ohne besondere Anforderungen ist das eine gute Wahl: Es gibt viele Bibliotheken, gerade bei Hardware ist es ggf. sogar die einzige unterstützte Sprache. Möchte man hingegen eine 0815 Webanwendung entwickeln, die z.B. wegen des geringen Energiebedarfs aus dem Raspberry Pi betrieben werden soll, ist das unproblematisch. Sie kann auch auf einen X86 Server umgezogen oder von dort auf den Pi migriert werden.

Auch für hardwarenahe Projekte existieren teilweise (inoffizielle) Node.js Pakete – die Plattform ist dafür also nicht grundsätzlich ausgeschlossen. Wie so oft kommt es also auf den Anwendungsfall an. Meine grundsätzliche Empfehlung: Entscheide dich aus nachvollziehbaren Gründen für oder gegen eine Sprache, Bibliothek, Framework usw. Nicht, weil es gerade im Trend liegt oder man das schon immer so gemacht hat. Anders sieht es natürlich aus, wenn du eine Technologie lediglich (praktisch) kennen lernen möchtest.

Das Node.js Ökosystem ist nicht nur für Entwickler interessant

Selbst etwas mit Node.js programmieren zu wollen, ist übrigens nicht der einzige Grund für die Installation: Damit erhältst du auch den Paketmanager NPM. Neben Bibliotheken, die du in deinen Code einbauen kannst, stellt er auch Kommandozeilenwerkzeuge bereit – also fertige Software, die du darüber installieren und direkt starten kannst. In dieser Hinsicht kann NPM vereinzelt eine Alternative zu APT, Docker & co sein.

Ein Beispiel ist gtop: Das Werkzeug ermöglicht die Überwachung von Prozessor, Arbeitsspeicher und Netzwerk, inklusive grafischer Ausgabe. Auch Etherpad benötigt JavaSkript, damit man darüber mit mehreren Personen in Echtzeit an Dokumenten arbeiten kann. In diesem Falle handelt es sich um eine Webanwendung. Wer Angular oder Webpack verwenden möchte, kann die offizielle CLI nutzen, um etwa Projekte zu erstellen. Das Node.js Ökosystem ist zwar insgesamt gesehen von der Vielfalt der Pakete weit weg, klassischen Quellen wie APT ernste Konkurrenz zu machen. Aber wer bestimmte Software benutzen möchte, benötigt eben zumindest NPM oder teilweise sogar Node.js auf dem System.

2 Methoden, Node.js zu installieren

Ich zeige dir im folgenden drei verschiedene Methoden mit ihren Vor- und Nachteilen auf. Sie lassen sich oft auch auf die Installation anderer Software anwenden – hier fokussieren wir uns auf Node.js. In beiden Fällen habe ich einen kleinen Webserver vorbereitet, den wir per JS starten möchten – unabhängig davon, für welche der Varianten du dich entscheidest. Der Code wird im Beispiel in der Datei app/server.js abgelegt.

const host = '0.0.0.0'
const port = 8080

const requestListener = function (req, res) {
    console.log(`${new Date()} ${req.method} ${req.url}`)
    res.writeHead(200);
    res.end(`<h1 style="font-family: sans-serif; text-align: center">Hallo vom Node.js Webserver auf dem Raspberry Pi :)</h1>`);
}
const http = require("http")
const server = http.createServer(requestListener)

var process = require('process')
process.on('SIGTERM', () => {
  server.close()
})

server.listen(port, host, () => {
    console.log(`Server is running on http://${host}:${port}`);
})

Methode #1: Als Container mit Docker (oder Alternativen wie Podman)

Der in meinen Augen beste Weg ist die Verwendung von Containern: Deine Anwendung wird vom Betriebssystem isoliert und du hast maximale Kontrolle darüber, welche Node.js Version eingesetzt wird. Außerdem ist es kein Problem, falls du mehrere Programme mit verschiedenen Node.js Versionen betreiben möchtest: Du kannst beliebig viele Container erstellen und anhand des Images (Abbildes) individuell entscheiden, welcher Container mit welcher Version läuft. Als Grundlage musst du dafür Docker oder eine andere OCI-Kompatible Laufzeitumgebung wie z.B. Podman installieren. Für Kommandozeilenwerkzeuge ist dieser Weg ein wenig langsamer, da man einen Alias anlegen muss, der im Hintergrund den Container startet. Möglich ist es trotzdem. Wer Tools global installieren möchte, kann sich alternativ den zweiten Weg anschauen.

Wie Docker auf dem Raspberry Pi sowie jedem Debian-System installiert werden kann, habe ich in diesem Beitrag bereits gezeigt. Darüber hinaus benötigst du Docker-Compose, es erleichtert die Handhabung von Containern erheblich. Falls du beide Programme noch nicht installiert hast, musst du dies bei dieser Methode zunächst über die verlinkten Beiträge durchführen.

Sucht euch im Docker-Hub die gewünschte Version von Node.js heraus. Falls es keine besonderen Anforderungen gibt und ihr euch unsicher seid, nutzt die aktuellste LTS. Welche das ist, steht auf der Homepage. Zum Erstellzeitpunkt des Beitrages war das Version 18. Ich würde bevorzugt ein möglichst kleines Image nutzen, entweder die Slim-Version des aktuellsten Debian (derzeit Bookworm). Oder Alpine, sofern die schlanke Distribution für euch kein Problem darstellt. Mit 18-slim erhaltet ihr die aktuellste Debian-Version, sodass ihr euch nur über die Node.js Version Gedanken machen müsst. Alternativ kann man natürlich auch eine exakte Version (z.B. 18.17.0 ) nutzen – hier müsst ihr aber jede Aktualisierung erst einmal mitbekommen und von Hand einspielen!

Wir legen ein Dockerfile an und geben in der ersten Zeile unser gewünschtes Abbild an:

FROM node:18-slim
WORKDIR /app
COPY app/* .
CMD [ "node", "server.js" ]

Dazu eine übersichtliche docker-compose.yml, die ebenfalls recht überschaubar ist – sie muss nur den Container aus dem Dockerfile erstellen und den Port 80 des Webservers an den Host weiterleiten.

services:
  node-web:
    build: .
    mem_limit: 0.5G
    ports:
      - 8080:8080

Der gesamte Ordner app im aktuellen Arbeitsverzeichnis wird in das Image kopiert. Darin erstellen wir nun eine JavaSkript-Datei namens server.js, die im Dockerfile als Einstiegspunkt angegeben ist. Dort gehört unser Programmcode hinein. Beispielhaft habe ich einen kleinen Webserver erstellt, der auf jede Anfrage mit einer Standard-Antwort reagiert.

Die Struktur unseres Verzeichnisses sollte nun wie folgt aussehen:

├── app
│   └── server.js
├── docker-compose.yml
└── Dockerfile

Nun können wir Docker Compose das Image bauen lassen und uns daraus einen Container erzeugen:

docker compose up --build

Dieser startet im Vordergrund und wir sollten nach kurzer Wartezeit den Webserver starten, der uns dies mit der im Code festgelegten Logmeldung bestätigt:

daniel@pi:~/node-docker $ docker compose up --build
[+] Building 0.8s (8/8) FINISHED                                                                                                                                                              
[...]
 ✔ Container node-docker-node-demo-1  Created                                                                                                                                            0.0s 
Attaching to node-docker-node-demo-1
node-docker-node-demo-1  | Server is running on http://0.0.0.0:8080

Nun kann man im gleichen Netzwerk per Browser über den Hostname des Raspberry Pi (hier „pi“) und den Port 8080 den Webserver aufrufen (z.B. http://pi:8080). Dieser liefert uns die ebenfalls im Code festgelegte Begrüßungsseite:

Methode #2: Inoffizielle Paketquellen von Node.js

Für manche Anwendungsfälle kann oder möchte man Docker nicht benutzen und stattdessen lieber Node.js direkt auf dem Pi installieren. Dafür werden Paketquellen angeboten, die eigenständig von Node.js betreut werden – also keine offiziellen von Debian oder des Raspberry Pi OS. Da die Pakete dort sowohl für X86 als auch ARM angeboten werden, kann man sie auf einem X86 Debian Server oder PC eben so nutzen, wie auf einem Raspberry Pi. Solche Drittanbieter-Paketquellen können das System gefährden, sowohl hinsichtlich Sicherheit, als auch der Stabilität. Sie sollten daher sparsam sowie nur von vertrauenswürdigen Quellen eingesetzt werden. Im Gegensatz zu den offiziellen Raspberry Pi OS/Debian-Paketquellen sind sie aktueller.

Zur Installation werden im GitHub-Repository verschiedene Skripte für die Hauptversionen aufgelistet. Man kann sich somit systemweit für eine bestimmte Hauptversion entscheiden. Diese direkt als root mit der Bash aufzurufen, würde ich aus Sicherheitsgründen nicht empfehlen. Stattdessen besser das Skript herunterladen, abspeichern, mit einem Texteditor prüfen und dann ausführen. Trotz Sichtprüfung nutze ich bei fremden Skripten gerne den -x Schalter: Er gibt die Befehle mit eingesetzten Werten aus Variablen aus. So seht ihr, welche Änderungen das Skript tatsächlich vornimmt. Ich habe mich für die derzeitige LTS-Version 18 entschieden. Sofern nicht eine bestimmte Version oder die neuesten Versionen benötigt werden, ist LTS die aktuelle LTS-Version in der Regel eine gute Wahl.

curl https://deb.nodesource.com/setup_18.x -o nodejs18.sh
nano nodejs18.sh
sudo bash -x nodejs18.sh

In diesem Falle legt das Skript eine Paketquelle mit dazugehörigem Quellcode von Nodesource (offizieller Vertriebskanal von Node.js) an und importiert dessen Schlüssel.

daniel@pi:~ $ cat /etc/apt/sources.list.d/nodesource.list 
deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x bullseye main
deb-src [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x bullseye mai

Diese Paketquelle überschreibt das vom Raspberry Pi OS ausgelieferte veraltete Paket, sodass ihr nun unter dem gleichen Name die Hauptversion erhaltet, deren Skript ihr zuvor heruntergeladen habt:

daniel@pi:~ $ apt show nodejs
Package: nodejs
Version: 18.17.0-deb-1nodesource1

Nach der Installation wird das Paket über die zentrale APT-Paketverwaltung aktuell gehalten: Wie von Debian und dem Raspberry Pi OS gewohnt, holt man sich die Liste der neuen Pakete mit apt update. Hierfür fragt APT ebenfalls die Nodesource-Quelle an. Anschließend werden möglicherweise vorhandene Aktualisierungen mit apt upgrade installiert.

Um unseren Node.js Webserver zu starten, steht nun der node Befehl systemweit zur Verfügung:

daniel@pi:~ $ node app/server.js 
Server is running on http://0.0.0.0:8080

Analog dazu auch der Paketmanager npm, über den Pakete installiert werden können – beispielsweise der Prozessmanager gtop, der zuvor bereits als Beispiel erwähnt wurde:

npm install -g gtop

Anschließend kann man den Befehl (hier gtop) ebenfalls systemweit aufrufen. Aus Nutzersicht gibt es keinen Unterschied gegenüber Software, die per APT installiert wurde. Nicht vergessen werden sollte jedoch: Da NPM eigenständig ist, werden die Pakete nicht zentral per APT aktualisiert! Empfehlenswert ist daher, solche händischen Installationen zu dokumentieren und regelmäßige Prüfungen auf Aktualisierungen vorzunehmen.

Leave a Reply