Abschnitt 18 · Lektion 4
Vom Rechner ins Netz
„Läuft bei mir” ist kein Witz, sondern eine präzise Aussage. Dein Programm läuft in einer Umgebung, die du über Monate eingerichtet hast, ohne dass du gemerkt hättest, dass du das tust. Diese Lektion zählt auf, was daran nicht mitkommt.
Was hier bewusst nicht steht: welchen Anbieter du nehmen sollst. Das ändert sich alle zwei Jahre, und die Liste darunter ändert sich nicht.
Was auf deinem Rechner alles gesetzt ist
import { createServer } from "node:http";
import { createHmac } from "node:crypto";
import { DatabaseSync } from "node:sqlite";
// Vier Annahmen, die nur auf deinem Rechner stimmen.
const PORT = 3000;
const GEHEIMNIS = "bitte-aendern";
const db = new DatabaseSync("./bibliothek.db");
db.exec("CREATE TABLE IF NOT EXISTS buecher (id INTEGER PRIMARY KEY)");
const server = createServer((req, res) => {
console.log(`${req.method} ${req.url}`);
const ausweis = createHmac("sha256", GEHEIMNIS).update("nutzer=7").digest("hex").slice(0, 16);
res.setHeader("Set-Cookie", `sid=${ausweis}; Path=/`);
res.setHeader("Content-Type", "text/html; charset=utf-8");
res.end([
"<!doctype html>",
'<meta charset="utf-8">',
"<title>Läuft</title>",
"<h1>Läuft</h1>",
`<p>Dein Ausweis für diese Anfrage: <code>${ausweis}</code></p>`,
"<p>Er ist bei jedem Aufruf derselbe, denn das Geheimnis steht im Quelltext.</p>",
].join("\n"));
});
server.listen(PORT, () => console.log(`laeuft auf http://localhost:${PORT}`)); Vier Zeilen, vier Annahmen. Der Port ist fest, das Geheimnis steht im Code, die Datenbankdatei liegt
neben dem Programm, und das Protokoll geht mit console.log irgendwohin. Auf deinem Rechner ist
jede davon richtig. Im Betrieb ist jede davon falsch, und zwar aus einem anderen Grund.
Dieses Beispiel läuft, und im Reiter „Browser” steht die Seite dazu. Klick ein paarmal neu: Der Ausweis bleibt derselbe, denn das Geheimnis, aus dem er gerechnet wird, steht im Quelltext. Wer den Quelltext hat, hat damit jeden Ausweis. Das ist der zweite Punkt der Liste, und er ist der teuerste.
Dieselbe Datei, betriebsfähig
import { createServer } from "node:http";
import { DatabaseSync } from "node:sqlite";
// Der Port kommt von aussen. Wer ihn festverdrahtet, laesst den
// Anbieter ins Leere greifen.
const PORT = Number(process.env.PORT ?? 3000);
// Die Datenbankdatei liegt nicht im Anwendungsverzeichnis, sondern
// dort, wo die Umgebung sie hinlegt.
const DB_PFAD = process.env.DB_PFAD ?? "./bibliothek.db";
// Kein Standardwert fuer ein Secret. Fehlt es, startet nichts.
if (!process.env.SITZUNGS_GEHEIMNIS) {
throw new Error("Start abgebrochen: SITZUNGS_GEHEIMNIS fehlt.");
}
const db = new DatabaseSync(DB_PFAD);
db.exec("CREATE TABLE IF NOT EXISTS buecher (id INTEGER PRIMARY KEY)");
const server = createServer((req, res) => {
// Protokoll nach stdout, eine Zeile JSON. Wer es in eine Datei
// schreibt, sucht sie spaeter in einem Container, den es nicht mehr gibt.
process.stdout.write(JSON.stringify({ stufe: "info", methode: req.method, pfad: req.url }) + "\n");
res.end("ok");
});
server.listen(PORT, () => {
process.stdout.write(JSON.stringify({ stufe: "info", ereignis: "start", port: PORT }) + "\n");
});
// Der Anbieter schickt SIGTERM und wartet ein paar Sekunden.
process.on("SIGTERM", () => {
process.stdout.write(JSON.stringify({ stufe: "info", ereignis: "fahre herunter" }) + "\n");
server.close(() => process.exit(0));
}); Dieses Beispiel lässt sich hier nicht starten, und das ist die Aussage: Ohne
SITZUNGS_GEHEIMNIS in der Umgebung startet es nicht. Genau so soll es sein. Ein Programm, das
sich beim Start beschwert, ist besser als eines, das mit einem Standardgeheimnis fröhlich
weiterläuft.
Die Liste zum Abhaken, in der Reihenfolge, in der sie dir sonst um die Ohren fliegt:
| Punkt | Warum |
|---|---|
| Port aus der Umgebung | Der Anbieter sagt dir, auf welchem Port du hören sollst. Ein fester Port heißt, dass nie jemand ankommt. |
| Secrets aus der Umgebung | Und ohne Standardwert, sonst läuft der Betrieb mit einem Schlüssel, den jeder kennt (18.2). |
npm ci --omit=dev | Nimmt das Lockfile wörtlich und lässt die Entwicklungspakete weg. Was in der falschen Liste steht, fehlt dann, und das ist der Sinn (5.4). |
| Ein Prozessmanager | Stirbt dein Prozess, muss ihn jemand neu starten. Das ist der ehrliche Anschluss an 16.4: Abstürzen ist richtig, liegenbleiben nicht. |
| HTTPS macht jemand davor | Ein Reverse Proxy oder der Anbieter selbst. Dein Node-Prozess spricht weiterhin HTTP, und das ist keine Nachlässigkeit, sondern die übliche Aufteilung. |
| Protokoll nach stdout | Wer in eine Datei schreibt, sucht sie später in einem Container, den es nicht mehr gibt. Das Einsammeln ist Aufgabe der Umgebung (16.5). |
| Sauber herunterfahren | SIGTERM kommt bei jedem Deployment. Wer ihn ignoriert, bricht laufende Anfragen ab (16.6). |
Wo die Daten liegen, ist die erste Frage
Der Punkt, der am häufigsten wehtut und am seltensten in Anleitungen steht: die Datenbankdatei gehört nicht ins Anwendungsverzeichnis. Bei den meisten Anbietern wird dieses Verzeichnis bei jedem Deployment neu aus deinem Repository erzeugt, und was du hineingeschrieben hast, ist danach weg.
Deshalb ist die erste Frage bei einem Deployment nicht „welcher Anbieter”, sondern:
- Wo liegen die Daten, und überleben sie ein Deployment?
- Wer sichert sie, wie oft, und hast du eine Sicherung schon einmal zurückgespielt?
- Was passiert, wenn du den Anbieter wechseln willst?
Wer diese drei beantworten kann, hat den schwierigen Teil hinter sich. Der Rest ist Konfiguration.
Was ein Container ändert und was nicht
Ein Container packt dein Programm samt Node-Version, Systembibliotheken und Startbefehl in ein Abbild, das überall gleich läuft. Damit ist genau ein Problem gelöst: die Umgebung ist überall dieselbe.
Nicht gelöst ist alles andere. Der Container hat immer noch keine Daten, keine Secrets, kein HTTPS und keinen, der ihn neu startet. Und dein Code ist derselbe geblieben. Ein Container ist deshalb ein gutes Werkzeug und keine Antwort auf die Frage, ob deine Anwendung betriebsfähig ist.
Der ehrliche Zwischenschritt für ein kleines Projekt: ein Anbieter, der aus deinem Repository baut, dir Umgebungsvariablen und einen verwalteten Speicher gibt und den Neustart übernimmt. Damit hakst du sechs der sieben Punkte oben ab, ohne selbst einen Server zu betreiben. Der siebte, das saubere Herunterfahren, bleibt deine Zeile Code, und die hast du in Abschnitt 16 schon geschrieben.
Zum Mitnehmen
Diese Lektion hat keine Challenge, und das ist keine Lücke. Ein echtes Deployment braucht einen Anbieter, eine Domain und eine Rechnung, und alle drei hat die Sandbox nicht. Beschreiben lässt es sich trotzdem, behaupten geprüft zu haben nicht.
Jetzt du
Basis Konto, kostenlosIm Editor änderst du die Beispiele dieser Lektion und lässt sie gleich laufen. So merkst du am schnellsten, ob es sitzt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.
Was in dieser Lektion steckt
-
Artikel mit 2 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.